Coverage Report

Created: 2025-07-11 06:33

/src/bag/api/bag_vrnode.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "bag_hdfhelper.h"
3
#include "bag_private.h"
4
#include "bag_vrnode.h"
5
#include "bag_vrnodedescriptor.h"
6
7
#include <array>
8
#include <cstring>  //memset
9
#include <memory>
10
#include <H5Cpp.h>
11
12
13
namespace BAG {
14
15
namespace {
16
17
//! Create an HDF5 CompType for the variable resolution node.
18
/*!
19
\return
20
    An HDF5 CompType for the variable resolution node.
21
*/
22
::H5::CompType makeDataType()
23
0
{
24
0
    const ::H5::CompType memDataType{sizeof(BagVRNodeItem)};
25
26
0
    memDataType.insertMember("hyp_strength",
27
0
        HOFFSET(BagVRNodeItem, hyp_strength), ::H5::PredType::NATIVE_FLOAT);
28
0
    memDataType.insertMember("num_hypotheses",
29
0
        HOFFSET(BagVRNodeItem, num_hypotheses), ::H5::PredType::NATIVE_UINT32);
30
0
    memDataType.insertMember("n_samples", HOFFSET(BagVRNodeItem, n_samples),
31
0
        ::H5::PredType::NATIVE_UINT32);
32
33
0
    return memDataType;
34
0
}
35
36
//! Read an attribute from an HDF5 DataSet.
37
/*!
38
\param h5file
39
    The HDF5 file.
40
\param name
41
    The name of the attribute.
42
43
\return
44
    The value in the specified attribute.
45
*/
46
template<typename T>
47
T readAttribute(
48
    const ::H5::H5File& h5file,
49
    const char* const name)
50
0
{
51
0
    const auto h5DataSet = h5file.openDataSet(VR_NODE_PATH);
52
0
    const auto attribute = h5DataSet.openAttribute(name);
53
54
0
    T value{};
55
0
    attribute.read(attribute.getDataType(), &value);
56
57
0
    return value;
58
0
}
Unexecuted instantiation: bag_vrnode.cpp:float BAG::(anonymous namespace)::readAttribute<float>(H5::H5File const&, char const*)
Unexecuted instantiation: bag_vrnode.cpp:unsigned int BAG::(anonymous namespace)::readAttribute<unsigned int>(H5::H5File const&, char const*)
59
60
}  // namespace
61
62
//! Constructor.
63
/*!
64
\param dataset
65
    The BAG Dataset that this layer belongs to.
66
\param descriptor
67
    The descriptor of this layer.
68
\param pH5dataSet
69
    The HDF5 DataSet this class wraps.
70
*/
71
VRNode::VRNode(
72
    Dataset& dataset,
73
    VRNodeDescriptor& descriptor,
74
    std::unique_ptr<::H5::DataSet, DeleteH5dataSet> pH5dataSet)
75
0
    : Layer(dataset, descriptor)
76
0
    , m_pH5dataSet(std::move(pH5dataSet))
77
0
{
78
0
}
79
80
//! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor()
81
/*!
82
\return
83
    The layer's descriptor.
84
    Will never be nullptr.
85
*/
86
std::shared_ptr<VRNodeDescriptor> VRNode::getDescriptor() & noexcept
87
0
{
88
0
    return std::dynamic_pointer_cast<VRNodeDescriptor>(Layer::getDescriptor());
89
0
}
90
91
//! Retrieve the layer's descriptor. Note: this shadows BAG::Layer.getDescriptor()
92
/*!
93
\return
94
    The layer's descriptor.
95
    Will never be nullptr.
96
*/
97
0
std::shared_ptr<const VRNodeDescriptor> VRNode::getDescriptor() const & noexcept {
98
0
    return std::dynamic_pointer_cast<const VRNodeDescriptor>(Layer::getDescriptor());
99
0
}
100
101
//! Create a variable resolution node.
102
/*!
103
\param dataset
104
    The BAG Dataset that this layer belongs to.
105
\param chunkSize
106
    The chunk size in the HDF5 DataSet.
107
\param compressionLevel
108
    The compression level in the HDF5 DataSet.
109
110
\return
111
    The new variable resolution node.
112
*/
113
std::shared_ptr<VRNode> VRNode::create(
114
    Dataset& dataset,
115
    uint64_t chunkSize,
116
    int compressionLevel)
117
0
{
118
0
    auto descriptor = VRNodeDescriptor::create(dataset, chunkSize,
119
0
        compressionLevel);
120
121
0
    auto h5dataSet = VRNode::createH5dataSet(dataset, *descriptor);
122
123
0
    return std::make_shared<VRNode>(dataset,
124
0
        *descriptor, std::move(h5dataSet));
125
0
}
126
127
//! Open an existing variable resolution node.
128
/*!
129
\param dataset
130
    The BAG Dataset that this layer belongs to.
131
\param descriptor
132
    The descriptor of this layer.
133
134
\return
135
    The specified variable resolution node.
136
*/
137
std::shared_ptr<VRNode> VRNode::open(
138
    Dataset& dataset,
139
    VRNodeDescriptor& descriptor)
140
0
{
141
0
    auto& h5file = dataset.getH5file();
142
143
    // Read the attribute values from the file and set in the descriptor.
144
    // min/max hyp strength
145
0
    const auto minHypStrength = readAttribute<float>(h5file,
146
0
        VR_NODE_MIN_HYP_STRENGTH);
147
0
    const auto maxHypStrength = readAttribute<float>(h5file,
148
0
        VR_NODE_MAX_HYP_STRENGTH);
149
150
0
    descriptor.setMinMaxHypStrength(minHypStrength, maxHypStrength);
151
152
    // min/max num hypotheses
153
0
    const auto minNumHypotheses = readAttribute<uint32_t>(h5file,
154
0
        VR_NODE_MIN_NUM_HYPOTHESES);
155
0
    const auto maxNumHypotheses = readAttribute<uint32_t>(h5file,
156
0
        VR_NODE_MAX_NUM_HYPOTHESES);
157
158
0
    descriptor.setMinMaxNumHypotheses(minNumHypotheses, maxNumHypotheses);
159
160
    // min/max n samples
161
0
    const auto minNSamples = readAttribute<uint32_t>(h5file,
162
0
        VR_NODE_MIN_N_SAMPLES);
163
0
    const auto maxNSamples = readAttribute<uint32_t>(h5file,
164
0
        VR_NODE_MAX_N_SAMPLES);
165
166
0
    descriptor.setMinMaxNSamples(minNSamples, maxNSamples);
167
168
0
    auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
169
0
        new ::H5::DataSet{h5file.openDataSet(VR_REFINEMENT_PATH)},
170
0
            DeleteH5dataSet{});
171
172
0
    return std::make_unique<VRNode>(dataset,
173
0
        descriptor, std::move(h5dataSet));
174
0
}
175
176
177
//! Create the HDF5 DataSet.
178
/*!
179
\param dataset
180
    The BAG Dataset that this layer belongs to.
181
\param descriptor
182
    The descriptor of this layer.
183
184
\return
185
    The new HDF5 DataSet.
186
*/
187
std::unique_ptr<::H5::DataSet, DeleteH5dataSet>
188
VRNode::createH5dataSet(
189
    const Dataset& dataset,
190
    const VRNodeDescriptor& descriptor)
191
0
{
192
0
    const hsize_t fileLength = 0;
193
0
    const hsize_t kMaxFileLength = H5S_UNLIMITED;
194
0
    const ::H5::DataSpace h5fileDataSpace{1, &fileLength, &kMaxFileLength};
195
196
    // Create the creation property list.
197
0
    const ::H5::DSetCreatPropList h5createPropList{};
198
199
    // Use chunk size and compression level from the descriptor.
200
0
    const hsize_t chunkSize = descriptor.getChunkSize();
201
0
    const auto compressionLevel = descriptor.getCompressionLevel();
202
0
    if (chunkSize > 0)
203
0
    {
204
0
        h5createPropList.setChunk(1, &chunkSize);
205
206
0
        if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel)
207
0
            h5createPropList.setDeflate(compressionLevel);
208
0
    }
209
0
    else if (compressionLevel > 0)
210
0
        throw CompressionNeedsChunkingSet{};
211
0
    else
212
0
        throw LayerRequiresChunkingSet{};
213
214
0
    h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC);
215
216
0
    const auto memDataType = makeDataType();
217
218
    // Create the DataSet using the above.
219
0
    const auto& h5file = dataset.getH5file();
220
221
0
    const auto h5dataSet = h5file.createDataSet(VR_NODE_PATH,
222
0
        memDataType, h5fileDataSpace, h5createPropList);
223
224
    // Create attributes.
225
0
    createAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
226
0
        {VR_NODE_MIN_HYP_STRENGTH, VR_NODE_MAX_HYP_STRENGTH});
227
228
0
    createAttributes(h5dataSet, ::H5::PredType::NATIVE_UINT32,
229
0
        {VR_NODE_MIN_NUM_HYPOTHESES, VR_NODE_MAX_NUM_HYPOTHESES,
230
0
        VR_NODE_MIN_N_SAMPLES, VR_NODE_MAX_N_SAMPLES});
231
232
    // Set initial min/max values.
233
0
    writeAttribute(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
234
0
        std::numeric_limits<float>::max(), VR_NODE_MIN_HYP_STRENGTH);
235
0
    writeAttribute(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
236
0
        std::numeric_limits<float>::lowest(), VR_NODE_MAX_HYP_STRENGTH);
237
238
0
    BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_UINT32,
239
0
        std::numeric_limits<uint32_t>::max(), {VR_NODE_MIN_NUM_HYPOTHESES,
240
0
        VR_NODE_MIN_N_SAMPLES});
241
0
    BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_UINT32,
242
0
        std::numeric_limits<uint32_t>::lowest(), {VR_NODE_MAX_NUM_HYPOTHESES,
243
0
        VR_NODE_MAX_N_SAMPLES});
244
245
0
    return std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
246
0
        new ::H5::DataSet{h5dataSet}, DeleteH5dataSet{});
247
0
}
248
249
//! \copydoc Layer::read
250
//! The rowStart and rowEnd are ignored since the data is 1 dimensional.
251
UInt8Array VRNode::readProxy(
252
    uint32_t /*rowStart*/,
253
    uint32_t columnStart,
254
    uint32_t /*rowEnd*/,
255
    uint32_t columnEnd) const
256
0
{
257
0
    auto pDescriptor = std::dynamic_pointer_cast<const VRNodeDescriptor>(
258
0
        this->getDescriptor());
259
0
    if (!pDescriptor)
260
0
        throw InvalidLayerDescriptor{};
261
262
    // Query the file for the specified rows and columns.
263
0
    const hsize_t columns = (columnEnd - columnStart) + 1;
264
0
    const hsize_t offset = columnStart;
265
266
0
    const auto fileDataSpace = m_pH5dataSet->getSpace();
267
0
    fileDataSpace.selectHyperslab(H5S_SELECT_SET, &columns, &offset);
268
269
0
    const auto bufferSize = pDescriptor->getReadBufferSize(1,
270
0
        static_cast<uint32_t>(columns));
271
0
    UInt8Array buffer{bufferSize};
272
273
0
    const ::H5::DataSpace memDataSpace{1, &columns, &columns};
274
275
0
    const auto memDataType = makeDataType();
276
277
0
    m_pH5dataSet->read(buffer.data(), memDataType, memDataSpace, fileDataSpace);
278
279
0
    return buffer;
280
0
}
281
282
//! \copydoc Layer::writeAttributes
283
void VRNode::writeAttributesProxy() const
284
0
{
285
0
    auto pDescriptor = std::dynamic_pointer_cast<const VRNodeDescriptor>(
286
0
        this->getDescriptor());
287
0
    if (!pDescriptor)
288
0
        throw InvalidLayerDescriptor{};
289
290
    // Write the attributes from the layer descriptor.
291
    // min/max hyp strength
292
0
    const auto minMaxHypStrength = pDescriptor->getMinMaxHypStrength();
293
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
294
0
        std::get<0>(minMaxHypStrength), VR_NODE_MIN_HYP_STRENGTH);
295
296
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
297
0
        std::get<1>(minMaxHypStrength), VR_NODE_MAX_HYP_STRENGTH);
298
299
    // min/max num hypotheses
300
0
    const auto minMaxNumHypotheses = pDescriptor->getMinMaxNumHypotheses();
301
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32,
302
0
        std::get<0>(minMaxNumHypotheses), VR_NODE_MIN_NUM_HYPOTHESES);
303
304
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32,
305
0
        std::get<1>(minMaxNumHypotheses), VR_NODE_MAX_NUM_HYPOTHESES);
306
307
    // min/max n samples
308
0
    const auto minMaxNSamples = pDescriptor->getMinMaxNSamples();
309
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32,
310
0
        std::get<0>(minMaxNSamples), VR_NODE_MIN_N_SAMPLES);
311
312
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_UINT32,
313
0
        std::get<1>(minMaxNSamples), VR_NODE_MAX_N_SAMPLES);
314
0
}
315
316
//! \copydoc Layer::write
317
//! Ignore rows since the data is 1 dimensional.
318
void VRNode::writeProxy(
319
    uint32_t /*rowStart*/,
320
    uint32_t columnStart,
321
    uint32_t /*rowEnd*/,
322
    uint32_t columnEnd,
323
    const uint8_t* buffer)
324
0
{
325
0
    auto pDescriptor = std::dynamic_pointer_cast<VRNodeDescriptor>(
326
0
        this->getDescriptor());
327
0
    if (!pDescriptor)
328
0
        throw InvalidLayerDescriptor{};
329
330
0
    const hsize_t columns = (columnEnd - columnStart) + 1;
331
0
    const hsize_t offset = columnStart;
332
0
    const ::H5::DataSpace memDataSpace{1, &columns, &columns};
333
334
    // Expand the file data space if needed.
335
0
    std::array<hsize_t, H5S_MAX_RANK> fileLength{};
336
0
    std::array<hsize_t, H5S_MAX_RANK> maxFileLength{};
337
338
0
    ::H5::DataSpace fileDataSpace = m_pH5dataSet->getSpace();
339
0
    const int numDims = fileDataSpace.getSimpleExtentDims(fileLength.data(),
340
0
        maxFileLength.data());
341
0
    if (numDims != 1)
342
0
        throw InvalidVRRefinementDimensions{};
343
344
0
    if (fileLength[0] < (columnEnd + 1))
345
0
    {
346
0
        const auto newMaxLength = std::max<hsize_t>(fileLength[0], columnEnd + 1);
347
348
0
        m_pH5dataSet->extend(&newMaxLength);
349
350
0
        fileDataSpace = m_pH5dataSet->getSpace();
351
352
        // Update the dataset's dimensions.
353
0
        if (this->getDataset().expired())
354
0
            throw DatasetNotFound{};
355
356
0
        auto pDataset = this->getDataset().lock();
357
0
        pDataset->getDescriptor().setDims(1, static_cast<uint32_t>(newMaxLength));
358
0
    }
359
360
0
    fileDataSpace.selectHyperslab(H5S_SELECT_SET, &columns, &offset);
361
362
0
    const auto memDataType = makeDataType();
363
364
0
    m_pH5dataSet->write(buffer, memDataType, memDataSpace, fileDataSpace);
365
366
    // Update min/max attributes
367
    // Get the current min/max from descriptor.
368
0
    float minHypStr = 0.f, maxHypStr = 0.f;
369
0
    std::tie(minHypStr, maxHypStr) = pDescriptor->getMinMaxHypStrength();
370
371
0
    uint32_t minNumHyp = 0, maxNumHyp = 0;
372
0
    std::tie(minNumHyp, maxNumHyp) = pDescriptor->getMinMaxNumHypotheses();
373
374
0
    uint32_t minNSamples = 0, maxNSamples = 0;
375
0
    std::tie(minNSamples, maxNSamples) = pDescriptor->getMinMaxNSamples();
376
377
    // Update the min/max from new data.
378
0
    const auto* items = reinterpret_cast<const BagVRNodeItem*>(buffer);
379
380
0
    auto* item = items;
381
0
    const auto end = items + columns;
382
383
0
    for (; item != end; ++item)
384
0
    {
385
0
        minHypStr = item->hyp_strength < minHypStr ? item->hyp_strength : minHypStr;
386
0
        maxHypStr = item->hyp_strength > maxHypStr ? item->hyp_strength : maxHypStr;
387
388
0
        minNumHyp = item->num_hypotheses < minNumHyp ? item->num_hypotheses : minNumHyp;
389
0
        maxNumHyp = item->num_hypotheses > maxNumHyp ? item->num_hypotheses : maxNumHyp;
390
391
0
        minNSamples = item->n_samples < minNSamples ? item->n_samples : minNSamples;
392
0
        maxNSamples = item->n_samples > maxNSamples ? item->n_samples : maxNSamples;
393
0
    }
394
395
0
    pDescriptor->setMinMaxHypStrength(minHypStr, maxHypStr);
396
0
    pDescriptor->setMinMaxNumHypotheses(minNumHyp, maxNumHyp);
397
0
    pDescriptor->setMinMaxNSamples(minNSamples, maxNSamples);
398
0
}
399
400
}   //namespace BAG
401