Coverage Report

Created: 2026-02-26 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bag/api/bag_vrrefinements.cpp
Line
Count
Source
1
2
#include "bag_hdfhelper.h"
3
#include "bag_private.h"
4
#include "bag_vrrefinements.h"
5
#include "bag_vrrefinementsdescriptor.h"
6
7
#include <iostream>
8
#include <array>
9
#include <cstring>  //memset
10
#include <H5Cpp.h>
11
12
namespace BAG {
13
14
namespace {
15
16
//! Create an HDF5 CompType for the variable resolution refinements layer.
17
/*!
18
\return
19
    The HDF5 CompType for the variable resolution refinements layer.
20
*/
21
::H5::CompType makeDataType()
22
0
{
23
0
    const ::H5::CompType memDataType{sizeof(BagVRRefinementsItem)};
24
25
0
    memDataType.insertMember("depth", HOFFSET(BagVRRefinementsItem, depth),
26
0
        ::H5::PredType::NATIVE_FLOAT);
27
0
    memDataType.insertMember("depth_uncrt", HOFFSET(BagVRRefinementsItem, depth_uncrt),
28
0
        ::H5::PredType::NATIVE_FLOAT);
29
30
0
    return memDataType;
31
0
}
32
33
//! Read an attribute from an HDF5 DataSet.
34
/*!
35
\param h5file
36
    The HDF5 file.
37
\param name
38
    The attribute name.
39
*/
40
template<typename T>
41
T readAttribute(
42
    const ::H5::H5File& h5file,
43
    const char* const name)
44
0
{
45
0
    const auto h5DataSet = h5file.openDataSet(VR_REFINEMENT_PATH);
46
0
    const auto attribute = h5DataSet.openAttribute(name);
47
48
0
    T value{};
49
0
    attribute.read(attribute.getDataType(), &value);
50
51
0
    return value;
52
0
}
53
54
}  // namespace
55
56
//! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor()
57
/*!
58
\return
59
    The layer's descriptor.
60
    Will never be nullptr.
61
*/
62
std::shared_ptr<VRRefinementsDescriptor> VRRefinements::getDescriptor() & noexcept
63
0
{
64
0
    return std::dynamic_pointer_cast<VRRefinementsDescriptor>(Layer::getDescriptor());
65
0
}
66
67
//! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor()
68
/*!
69
\return
70
    The layer's descriptor.
71
    Will never be nullptr.
72
*/
73
std::shared_ptr<const VRRefinementsDescriptor> VRRefinements::getDescriptor() const & noexcept
74
0
{
75
0
    return std::dynamic_pointer_cast<const VRRefinementsDescriptor>(Layer::getDescriptor());
76
0
}
77
78
//! Constructor.
79
/*!
80
\param dataset
81
    The BAG Dataset this layer belongs to.
82
\param descriptor
83
    The descriptor of this layer.
84
\param h5dataSet
85
    The HDF5 DataSet that stores this layer.
86
*/
87
VRRefinements::VRRefinements(
88
    Dataset& dataset,
89
    VRRefinementsDescriptor& descriptor,
90
    std::unique_ptr<::H5::DataSet, DeleteH5dataSet> h5dataSet)
91
0
    : Layer(dataset, descriptor)
92
0
    , m_pH5dataSet(std::move(h5dataSet))
93
0
{
94
0
}
95
96
//! Create a new variable resolution refinements layer.
97
/*!
98
\param dataset
99
    The BAG Dataset this layer belongs to.
100
\param chunkSize
101
    The chunk size the HDF5 DataSet will use.
102
\param compressionLevel
103
    The compression level the HDF5 DataSet will use.
104
105
\return
106
    The new variable resolution refinements layer.
107
*/
108
std::unique_ptr<VRRefinements> VRRefinements::create(
109
    Dataset& dataset,
110
    uint64_t chunkSize,
111
    int compressionLevel)
112
0
{
113
0
    auto descriptor = VRRefinementsDescriptor::create(dataset, chunkSize,
114
0
        compressionLevel);
115
116
0
    auto h5dataSet = VRRefinements::createH5dataSet(dataset, *descriptor);
117
118
0
    return std::unique_ptr<VRRefinements>(new VRRefinements{dataset,
119
0
        *descriptor, std::move(h5dataSet)});
120
0
}
121
122
//! Open an existing variable resolution refinements layer.
123
/*!
124
\param dataset
125
    The BAG Dataset this layer belongs to.
126
\param descriptor
127
    The descriptor of this layer.
128
129
\return
130
    The existing variable resolution refinements layer.
131
*/
132
std::unique_ptr<VRRefinements> VRRefinements::open(
133
    Dataset& dataset,
134
    VRRefinementsDescriptor& descriptor)
135
0
{
136
0
    auto& h5file = dataset.getH5file();
137
138
    // Read the attribute values from the file and set in the descriptor.
139
0
    const auto minDepth = readAttribute<float>(h5file, VR_REFINEMENT_MIN_DEPTH);
140
0
    const auto maxDepth = readAttribute<float>(h5file, VR_REFINEMENT_MAX_DEPTH);
141
142
0
    descriptor.setMinMaxDepth(minDepth, maxDepth);
143
144
0
    const auto minUncertainty = readAttribute<float>(h5file,
145
0
        VR_REFINEMENT_MIN_UNCERTAINTY);
146
0
    const auto maxUncertainty = readAttribute<float>(h5file,
147
0
        VR_REFINEMENT_MAX_UNCERTAINTY);
148
149
0
    descriptor.setMinMaxUncertainty(minUncertainty, maxUncertainty);
150
151
0
    auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
152
0
        new ::H5::DataSet{h5file.openDataSet(VR_REFINEMENT_PATH)},
153
0
            DeleteH5dataSet{});
154
155
    // We need to know the dimensions of the array on file so that we can update the
156
    // descriptor for the layer.
157
0
    hsize_t dims[2];
158
0
    int ndims = h5dataSet->getSpace().getSimpleExtentDims(dims, nullptr);
159
0
    if (ndims != 2) {
160
        // Should be 1D according to BAG spec, but some implementations use a 2D array,
161
        // so for compatibility's sake, use 2D.
162
0
        throw InvalidVRRefinementDimensions{};
163
0
    }
164
0
    descriptor.setDims(dims[0], dims[1]);
165
0
    return std::unique_ptr<VRRefinements>(new VRRefinements{dataset,
166
0
        descriptor, std::move(h5dataSet)});
167
0
}
168
169
170
//! Create a new HDF5 DataSet for this variable resolution refinements layer.
171
/*!
172
\param dataset
173
    The BAG Dataset this layer belongs to.
174
\param descriptor
175
    The descriptor of this layer.
176
177
\return
178
    A new HDF5 DataSet.
179
*/
180
std::unique_ptr<::H5::DataSet, DeleteH5dataSet>
181
VRRefinements::createH5dataSet(
182
    const Dataset& dataset,
183
    const VRRefinementsDescriptor& descriptor)
184
0
{
185
0
    std::array<hsize_t, kRank> fileDims{0, 0};
186
0
    const std::array<hsize_t, kRank> kMaxFileDims{H5S_UNLIMITED, H5S_UNLIMITED};
187
0
    const ::H5::DataSpace h5fileDataSpace{kRank, fileDims.data(), kMaxFileDims.data()};
188
189
    // Create the creation property list.
190
0
    const ::H5::DSetCreatPropList h5createPropList{};
191
192
    // Use chunk size and compression level from the descriptor.
193
0
    const hsize_t chunkSize = descriptor.getChunkSize();
194
0
    const auto compressionLevel = descriptor.getCompressionLevel();
195
0
    if (chunkSize > 0)
196
0
    {
197
0
        const std::array<hsize_t, kRank> chunkDims{chunkSize, chunkSize};
198
0
        h5createPropList.setChunk(kRank, chunkDims.data());
199
200
0
        if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel)
201
0
            h5createPropList.setDeflate(compressionLevel);
202
0
    }
203
0
    else if (compressionLevel > 0)
204
0
        throw CompressionNeedsChunkingSet{};
205
0
    else
206
0
        throw LayerRequiresChunkingSet{};
207
208
0
    h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC);
209
210
0
    const auto memDataType = makeDataType();
211
212
    // Create the DataSet using the above.
213
0
    const auto& h5file = dataset.getH5file();
214
215
0
    const auto h5dataSet = h5file.createDataSet(VR_REFINEMENT_PATH,
216
0
        memDataType, h5fileDataSpace, h5createPropList);
217
218
    // Create attributes.
219
0
    createAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
220
0
        {VR_REFINEMENT_MIN_DEPTH, VR_REFINEMENT_MAX_DEPTH,
221
0
        VR_REFINEMENT_MIN_UNCERTAINTY, VR_REFINEMENT_MAX_UNCERTAINTY});
222
223
    // Set initial min/max values.
224
0
    BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
225
0
        std::numeric_limits<float>::max(), {VR_REFINEMENT_MIN_DEPTH,
226
0
        VR_REFINEMENT_MIN_UNCERTAINTY});
227
0
    BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
228
0
        std::numeric_limits<float>::lowest(), {VR_REFINEMENT_MAX_DEPTH,
229
0
        VR_REFINEMENT_MAX_UNCERTAINTY});
230
231
0
    return std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
232
0
        new ::H5::DataSet{h5dataSet}, DeleteH5dataSet{});
233
0
}
234
235
//! \copydoc Layer::read
236
//! Ignore rows since the data is 1 dimensional.
237
UInt8Array VRRefinements::readProxy(
238
    uint32_t /*rowStart*/,
239
    uint32_t columnStart,
240
    uint32_t /*rowEnd*/,
241
    uint32_t columnEnd) const
242
0
{
243
0
    auto pDescriptor = std::dynamic_pointer_cast<const VRRefinementsDescriptor>(
244
0
        this->getDescriptor());
245
0
    if (!pDescriptor)
246
0
        throw InvalidLayerDescriptor{};
247
248
    // Query the file for the specified rows and columns.
249
0
    const hsize_t columns = (columnEnd - columnStart) + 1;
250
0
    const hsize_t offset = columnStart;
251
252
0
    const std::array<hsize_t, kRank> sizes{1, columns};
253
0
    const std::array<hsize_t, kRank> offsets{0, offset};
254
255
0
    const auto h5fileDataSpace = m_pH5dataSet->getSpace();
256
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, sizes.data(), offsets.data());
257
258
0
    const auto bufferSize = pDescriptor->getReadBufferSize(1, columns);
259
0
    UInt8Array buffer{bufferSize};
260
    
261
0
    const ::H5::DataSpace memDataSpace{kRank, sizes.data(), sizes.data()};
262
263
0
    const auto memDataType = makeDataType();
264
265
0
    m_pH5dataSet->read(buffer.data(), memDataType, memDataSpace, h5fileDataSpace);
266
267
0
    return buffer;
268
0
}
269
270
//! \copydoc Layer::writeAttributes
271
void VRRefinements::writeAttributesProxy() const
272
0
{
273
0
    auto pDescriptor =
274
0
        std::dynamic_pointer_cast<const VRRefinementsDescriptor>(
275
0
            this->getDescriptor());
276
0
    if (!pDescriptor)
277
0
        throw InvalidLayerDescriptor{};
278
279
    // Write the attributes from the layer descriptor.
280
    // min/max depth
281
0
    const auto minMaxDepth = pDescriptor->getMinMaxDepth();
282
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
283
0
        std::get<0>(minMaxDepth), VR_REFINEMENT_MIN_DEPTH);
284
285
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
286
0
        std::get<1>(minMaxDepth), VR_REFINEMENT_MAX_DEPTH);
287
288
    // min/max uncertainty
289
0
    const auto minMaxUncertainty = pDescriptor->getMinMaxUncertainty();
290
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
291
0
        std::get<0>(minMaxUncertainty), VR_REFINEMENT_MIN_UNCERTAINTY);
292
293
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
294
0
        std::get<1>(minMaxUncertainty), VR_REFINEMENT_MAX_UNCERTAINTY);
295
0
}
296
297
//! \copydoc Layer::write
298
void VRRefinements::writeProxy(
299
    uint32_t rowStart,
300
    uint32_t columnStart,
301
    uint32_t rowEnd,
302
    uint32_t columnEnd,
303
    const uint8_t* buffer)
304
0
{
305
0
    auto pDescriptor = std::dynamic_pointer_cast<VRRefinementsDescriptor>(
306
0
        this->getDescriptor());
307
0
    if (!pDescriptor)
308
0
        throw InvalidLayerDescriptor{};
309
310
0
    const auto rows = (rowEnd - rowStart) + 1;
311
0
    const auto columns = (columnEnd - columnStart) + 1;
312
0
    const std::array<hsize_t, kRank> count{rows, columns};
313
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
314
0
    const ::H5::DataSpace memDataSpace{kRank, count.data(), count.data()};
315
316
0
    ::H5::DataSpace fileDataSpace = m_pH5dataSet->getSpace();
317
318
    // Expand the file data space if needed.
319
0
    std::array<hsize_t, kRank> fileDims{};
320
0
    std::array<hsize_t, kRank> maxFileDims{};
321
322
0
    const int numDims = fileDataSpace.getSimpleExtentDims(fileDims.data(),
323
0
                                                          maxFileDims.data());
324
0
    if (numDims != kRank) {
325
0
        throw InvalidVRRefinementDimensions{};
326
0
    }
327
328
0
    if ((fileDims[0] < (rowEnd + 1)) ||
329
0
        (fileDims[1] < (columnEnd + 1)))
330
0
    {
331
0
        const std::array<hsize_t, kRank> newDims{
332
0
                std::max<hsize_t>(fileDims[0], rowEnd + 1),
333
0
                std::max<hsize_t>(fileDims[1], columnEnd + 1)};
334
0
        m_pH5dataSet->extend(newDims.data());
335
336
0
        fileDataSpace = m_pH5dataSet->getSpace();
337
338
        // Update the dataset's dimensions.
339
0
        if (this->getDataset().expired())
340
0
            throw DatasetNotFound{};
341
342
        // So that the read() call checks correctly against the size of the array, rather
343
        // than the dimensions of the mandatory layer, we need to keep track of the size
344
        // of the layer in the layer-specific descriptor.
345
0
        pDescriptor->setDims(newDims[0], newDims[1]);
346
0
    }
347
348
0
    fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
349
350
0
    const auto memDataType = makeDataType();
351
352
0
    m_pH5dataSet->write(buffer, memDataType, memDataSpace, fileDataSpace);
353
354
    // Update min/max attributes
355
    // Get the current min/max from descriptor.
356
0
    float minDepth = 0.f, maxDepth = 0.f;
357
0
    std::tie(minDepth, maxDepth) = pDescriptor->getMinMaxDepth();
358
359
0
    float minUncert = 0.f, maxUncert = 0.f;
360
0
    std::tie(minUncert, maxUncert) = pDescriptor->getMinMaxUncertainty();
361
362
    // Update the min/max from new data.
363
0
    const auto* items = reinterpret_cast<const BagVRRefinementsItem*>(buffer);
364
365
0
    auto* item = items;
366
0
    const auto end = items + columns;
367
368
0
    for (; item != end; ++item)
369
0
    {
370
0
        minDepth = item->depth < minDepth ? item->depth : minDepth;
371
0
        maxDepth = item->depth > maxDepth ? item->depth : maxDepth;
372
373
0
        minUncert = item->depth_uncrt < minUncert ? item->depth_uncrt : minUncert;
374
0
        maxUncert = item->depth_uncrt > maxUncert ? item->depth_uncrt : maxUncert;
375
0
    }
376
377
0
    pDescriptor->setMinMaxDepth(minDepth, maxDepth);
378
0
    pDescriptor->setMinMaxUncertainty(minUncert, maxUncert);
379
0
}
380
381
}  // namespace BAG