Coverage Report

Created: 2026-02-26 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bag/api/bag_simplelayer.cpp
Line
Count
Source
1
2
#include "bag_attributeinfo.h"
3
#include "bag_private.h"
4
#include "bag_simplelayer.h"
5
#include "bag_simplelayerdescriptor.h"
6
7
#include <algorithm>
8
#include <array>
9
#include <H5Cpp.h>
10
11
12
namespace BAG {
13
14
//! Constructor.
15
/*!
16
\param dataset
17
    The BAG Dataset this layer belongs to.
18
\param descriptor
19
    The descriptor of this layer.
20
\param pH5dataSet
21
    The HDF5 DataSet that stores this layer.
22
*/
23
SimpleLayer::SimpleLayer(
24
    Dataset& dataset,
25
    SimpleLayerDescriptor& descriptor,
26
    std::unique_ptr<::H5::DataSet, DeleteH5dataSet> pH5dataSet)
27
36
    : Layer(dataset, descriptor)
28
36
    , m_pH5dataSet(std::move(pH5dataSet))
29
36
{
30
36
}
31
32
//! Create a new simple layer.
33
/*!
34
\param dataset
35
    The BAG Dataset this layer belongs to.
36
\param type
37
    The type of layer.
38
\param chunkSize
39
    The chunk size the HDF5 DataSet will use.
40
\param compressionLevel
41
    The compression level the HDF5 DataSet will use.
42
43
\return
44
    The new simple layer.
45
*/
46
std::shared_ptr<SimpleLayer> SimpleLayer::create(
47
    Dataset& dataset,
48
    LayerType type,
49
    uint32_t rows, uint32_t cols,
50
    uint64_t chunkSize,
51
    int compressionLevel)
52
0
{
53
0
    auto descriptor = SimpleLayerDescriptor::create(dataset, type, rows, cols, chunkSize, compressionLevel);
54
0
    auto h5dataSet = SimpleLayer::createH5dataSet(dataset, *descriptor);
55
56
0
    return std::make_shared<SimpleLayer>(dataset, *descriptor, std::move(h5dataSet));
57
0
}
58
59
//! Open an existing simple layer.
60
/*!
61
\param dataset
62
    The BAG Dataset this layer belongs to.
63
\param descriptor
64
    The descriptor of this layer.
65
66
\return
67
    The specified simple layer.
68
*/
69
std::shared_ptr<SimpleLayer> SimpleLayer::open(
70
    Dataset& dataset,
71
    SimpleLayerDescriptor& descriptor)
72
36
{
73
36
    const auto& h5file = dataset.getH5file();
74
36
    auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
75
36
        new ::H5::DataSet{h5file.openDataSet(descriptor.getInternalPath())},
76
36
        DeleteH5dataSet{});
77
    
78
    // Configure the layer dimensions in the descriptor (we implicitally expect the layer
79
    // to be two-dimensional)
80
36
    hsize_t dims[2];
81
36
    h5dataSet->getSpace().getSimpleExtentDims(dims);
82
36
    descriptor.setDims(dims[0], dims[1]);
83
84
    // Read the min/max attribute values.
85
36
    const auto possibleMinMax = dataset.getMinMax(descriptor.getLayerType());
86
36
    if (std::get<0>(possibleMinMax))
87
36
        descriptor.setMinMax(std::get<1>(possibleMinMax),
88
36
            std::get<2>(possibleMinMax));
89
90
36
    return std::make_shared<SimpleLayer>(dataset, descriptor,std::move(h5dataSet));
91
36
}
92
93
94
//! Create the HDF5 DataSet.
95
/*!
96
\param dataset
97
    The BAG Dataset this layer belongs to.
98
\param descriptor
99
    The descriptor of this layer.
100
101
\return
102
    The new HDF5 DataSet.
103
*/
104
std::unique_ptr<::H5::DataSet, DeleteH5dataSet>
105
SimpleLayer::createH5dataSet(
106
    const Dataset& dataset,
107
    const SimpleLayerDescriptor& descriptor)
108
0
{
109
0
    uint32_t dim0 = 0, dim1 = 0;
110
0
    std::tie(dim0, dim1) = descriptor.getDims();
111
0
    const std::array<hsize_t, kRank> fileDims{dim0, dim1};
112
113
0
    ::H5::DataSpace h5dataSpace{kRank, fileDims.data(), fileDims.data()};
114
115
0
    ::H5::FloatType h5dataType;
116
0
    h5dataType.copy(::H5::PredType::NATIVE_FLOAT);
117
0
    h5dataType.setOrder(H5T_ORDER_LE);
118
119
    // Create the creation property list.
120
0
    const ::H5::DSetCreatPropList h5createPropList{};
121
0
    h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC);
122
123
0
    constexpr float kFillValue = BAG_NULL_ELEVATION;
124
0
    h5createPropList.setFillValue(h5dataType, &kFillValue);
125
126
    // Use chunk size and compression level from the descriptor.
127
0
    const auto compressionLevel = descriptor.getCompressionLevel();
128
0
    const auto chunkSize = descriptor.getChunkSize();
129
0
    if (chunkSize > 0)
130
0
    {
131
0
        const std::array<hsize_t, kRank> chunkDims{chunkSize, chunkSize};
132
0
        h5createPropList.setChunk(kRank, chunkDims.data());
133
134
0
        if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel)
135
0
            h5createPropList.setDeflate(compressionLevel);
136
0
    }
137
0
    else if (compressionLevel > 0)
138
0
        throw CompressionNeedsChunkingSet{};
139
140
    // Create the DataSet using the above.
141
0
    const auto& h5file = dataset.getH5file();
142
143
0
    auto pH5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
144
0
        new ::H5::DataSet{h5file.createDataSet(descriptor.getInternalPath(),
145
0
            h5dataType, h5dataSpace, h5createPropList)},
146
0
        DeleteH5dataSet{});
147
148
    // Create any attributes.
149
0
    const auto attInfo = getAttributeInfo(descriptor.getLayerType());
150
151
0
    const ::H5::DataSpace minElevDataSpace{};
152
0
    const auto minElevAtt = pH5dataSet->createAttribute(attInfo.minName,
153
0
        attInfo.h5type, minElevDataSpace);
154
155
0
    const ::H5::DataSpace maxElevDataSpace{};
156
0
    const auto maxElevAtt = pH5dataSet->createAttribute(attInfo.maxName,
157
0
        attInfo.h5type, maxElevDataSpace);
158
159
    // Set initial min/max values.
160
0
    constexpr float minElev = std::numeric_limits<float>::max();
161
0
    minElevAtt.write(attInfo.h5type, &minElev);
162
163
0
    constexpr float maxElev = std::numeric_limits<float>::lowest();
164
0
    maxElevAtt.write(attInfo.h5type, &maxElev);
165
166
0
    return pH5dataSet;
167
0
}
168
169
//! \copydoc Layer::read
170
UInt8Array SimpleLayer::readProxy(
171
    uint32_t rowStart,
172
    uint32_t columnStart,
173
    uint32_t rowEnd,
174
    uint32_t columnEnd) const
175
0
{
176
    // Query the file for the specified rows and columns.
177
0
    const auto h5fileDataSpace = m_pH5dataSet->getSpace();
178
179
0
    const auto rows = (rowEnd - rowStart) + 1;
180
0
    const auto columns = (columnEnd - columnStart) + 1;
181
0
    const std::array<hsize_t, kRank> count{rows, columns};
182
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
183
184
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
185
186
    // Initialize the output buffer.
187
0
    const auto bufferSize = this->getDescriptor()->getReadBufferSize(rows,
188
0
        columns);
189
0
    UInt8Array buffer{bufferSize};
190
191
    // Prepare the memory space.
192
0
    const ::H5::DataSpace h5memSpace{kRank, count.data(), count.data()};
193
194
0
    m_pH5dataSet->read(buffer.data(), H5Dget_type(m_pH5dataSet->getId()),
195
0
        h5memSpace, h5fileDataSpace);
196
197
0
    return buffer;
198
0
}
199
200
//! \copydoc Layer::writeAttributes
201
void SimpleLayer::writeAttributesProxy() const
202
0
{
203
0
    auto pDescriptor = this->getDescriptor();
204
0
    const auto attInfo = getAttributeInfo(pDescriptor->getLayerType());
205
206
    // Write any attributes, from the layer descriptor.
207
    // min value
208
0
    const auto minMax = pDescriptor->getMinMax();
209
210
0
    const auto minAtt = m_pH5dataSet->openAttribute(attInfo.minName);
211
0
    minAtt.write(attInfo.h5type, &std::get<0>(minMax));
212
213
    // max value
214
0
    const auto maxAtt = m_pH5dataSet->openAttribute(attInfo.maxName);
215
0
    maxAtt.write(attInfo.h5type, &std::get<1>(minMax));
216
0
}
217
218
//! \copydoc Layer::write
219
void SimpleLayer::writeProxy(
220
    uint32_t rowStart,
221
    uint32_t columnStart,
222
    uint32_t rowEnd,
223
    uint32_t columnEnd,
224
    const uint8_t* buffer)
225
0
{
226
0
    auto h5fileDataSpace = m_pH5dataSet->getSpace();
227
228
    // Make sure the area being written to does not exceed the file dimensions.
229
0
    std::array<hsize_t, kRank> fileDims{};
230
0
    h5fileDataSpace.getSimpleExtentDims(fileDims.data());
231
232
0
    if ((rowEnd >= fileDims[0]) || (columnEnd >= fileDims[1]))
233
0
        throw InvalidWriteSize{};
234
235
0
    const auto rows = (rowEnd - rowStart) + 1;
236
0
    const auto columns = (columnEnd - columnStart) + 1;
237
0
    const std::array<hsize_t, kRank> count{rows, columns};
238
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
239
240
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
241
242
    // Prepare the memory space.
243
0
    const ::H5::DataSpace h5memDataSpace{kRank, count.data(), count.data()};
244
245
0
    m_pH5dataSet->write(buffer, H5Dget_type(m_pH5dataSet->getId()),
246
0
        h5memDataSpace, h5fileDataSpace);
247
248
    // Update min/max attributes
249
0
    auto pDescriptor = this->getDescriptor();
250
0
    const auto attInfo = getAttributeInfo(pDescriptor->getLayerType());
251
0
    float min = 0.f, max = 0.f;
252
253
0
    if (attInfo.h5type == ::H5::PredType::NATIVE_FLOAT)
254
0
    {
255
0
        const auto* floatBuffer = reinterpret_cast<const float*>(buffer);
256
257
0
        const auto begin = floatBuffer;
258
0
        const auto end = floatBuffer + rows * columns;
259
260
0
        const auto mm = std::minmax_element(begin, end);
261
0
        min = *std::get<0>(mm);
262
0
        max = *std::get<1>(mm);
263
0
    }
264
0
    else if (attInfo.h5type == ::H5::PredType::NATIVE_UINT32)
265
0
    {
266
0
        const auto* uint32Buffer = reinterpret_cast<const uint32_t*>(buffer);
267
268
0
        const auto begin = uint32Buffer;
269
0
        const auto end = uint32Buffer + rows * columns;
270
271
0
        const auto mm = std::minmax_element(begin, end);
272
0
        min = static_cast<float>(*std::get<0>(mm));
273
0
        max = static_cast<float>(*std::get<1>(mm));
274
0
    }
275
0
    else
276
0
        throw UnsupportedAttributeType{};
277
278
0
    const float currentMin = std::get<0>(pDescriptor->getMinMax());
279
0
    const float currentMax = std::get<1>(pDescriptor->getMinMax());
280
281
0
    pDescriptor->setMinMax(std::min(currentMin, min), std::max(currentMax, max));
282
0
}
283
284
}   //namespace BAG
285