Coverage Report

Created: 2025-07-11 06:33

/src/bag/api/bag_vrrefinements.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "bag_hdfhelper.h"
3
#include "bag_private.h"
4
#include "bag_vrrefinements.h"
5
#include "bag_vrrefinementsdescriptor.h"
6
7
#include <array>
8
#include <cstring>  //memset
9
#include <H5Cpp.h>
10
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
0
    std::shared_ptr<const VRRefinementsDescriptor> VRRefinements::getDescriptor() const & noexcept {
74
0
        return std::dynamic_pointer_cast<const VRRefinementsDescriptor>(Layer::getDescriptor());
75
0
    }
76
77
//! Constructor.
78
/*!
79
\param dataset
80
    The BAG Dataset this layer belongs to.
81
\param descriptor
82
    The descriptor of this layer.
83
\param h5dataSet
84
    The HDF5 DataSet that stores this layer.
85
*/
86
VRRefinements::VRRefinements(
87
    Dataset& dataset,
88
    VRRefinementsDescriptor& descriptor,
89
    std::unique_ptr<::H5::DataSet, DeleteH5dataSet> h5dataSet)
90
0
    : Layer(dataset, descriptor)
91
0
    , m_pH5dataSet(std::move(h5dataSet))
92
0
{
93
0
}
94
95
//! Create a new variable resolution refinements layer.
96
/*!
97
\param dataset
98
    The BAG Dataset this layer belongs to.
99
\param chunkSize
100
    The chunk size the HDF5 DataSet will use.
101
\param compressionLevel
102
    The compression level the HDF5 DataSet will use.
103
104
\return
105
    The new variable resolution refinements layer.
106
*/
107
std::unique_ptr<VRRefinements> VRRefinements::create(
108
    Dataset& dataset,
109
    uint64_t chunkSize,
110
    int compressionLevel)
111
0
{
112
0
    auto descriptor = VRRefinementsDescriptor::create(dataset, chunkSize,
113
0
        compressionLevel);
114
115
0
    auto h5dataSet = VRRefinements::createH5dataSet(dataset, *descriptor);
116
117
0
    return std::unique_ptr<VRRefinements>(new VRRefinements{dataset,
118
0
        *descriptor, std::move(h5dataSet)});
119
0
}
120
121
//! Open an existing variable resolution refinements layer.
122
/*!
123
\param dataset
124
    The BAG Dataset this layer belongs to.
125
\param descriptor
126
    The descriptor of this layer.
127
128
\return
129
    The existing variable resolution refinements layer.
130
*/
131
std::unique_ptr<VRRefinements> VRRefinements::open(
132
    Dataset& dataset,
133
    VRRefinementsDescriptor& descriptor)
134
0
{
135
0
    auto& h5file = dataset.getH5file();
136
137
    // Read the attribute values from the file and set in the descriptor.
138
0
    const auto minDepth = readAttribute<float>(h5file, VR_REFINEMENT_MIN_DEPTH);
139
0
    const auto maxDepth = readAttribute<float>(h5file, VR_REFINEMENT_MAX_DEPTH);
140
141
0
    descriptor.setMinMaxDepth(minDepth, maxDepth);
142
143
0
    const auto minUncertainty = readAttribute<float>(h5file,
144
0
        VR_REFINEMENT_MIN_UNCERTAINTY);
145
0
    const auto maxUncertainty = readAttribute<float>(h5file,
146
0
        VR_REFINEMENT_MAX_UNCERTAINTY);
147
148
0
    descriptor.setMinMaxUncertainty(minUncertainty, maxUncertainty);
149
150
0
    auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
151
0
        new ::H5::DataSet{h5file.openDataSet(VR_REFINEMENT_PATH)},
152
0
            DeleteH5dataSet{});
153
154
0
    return std::unique_ptr<VRRefinements>(new VRRefinements{dataset,
155
0
        descriptor, std::move(h5dataSet)});
156
0
}
157
158
159
//! Create a new HDF5 DataSet for this variable resolution refinements layer.
160
/*!
161
\param dataset
162
    The BAG Dataset this layer belongs to.
163
\param descriptor
164
    The descriptor of this layer.
165
166
\return
167
    A new HDF5 DataSet.
168
*/
169
std::unique_ptr<::H5::DataSet, DeleteH5dataSet>
170
VRRefinements::createH5dataSet(
171
    const Dataset& dataset,
172
    const VRRefinementsDescriptor& descriptor)
173
0
{
174
0
    constexpr hsize_t fileLength = 0;
175
0
    constexpr hsize_t kMaxFileLength = H5S_UNLIMITED;
176
0
    const ::H5::DataSpace h5fileDataSpace{1, &fileLength, &kMaxFileLength};
177
178
    // Create the creation property list.
179
0
    const ::H5::DSetCreatPropList h5createPropList{};
180
181
    // Use chunk size and compression level from the descriptor.
182
0
    const hsize_t chunkSize = descriptor.getChunkSize();
183
0
    const auto compressionLevel = descriptor.getCompressionLevel();
184
0
    if (chunkSize > 0)
185
0
    {
186
0
        h5createPropList.setChunk(1, &chunkSize);
187
188
0
        if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel)
189
0
            h5createPropList.setDeflate(compressionLevel);
190
0
    }
191
0
    else if (compressionLevel > 0)
192
0
        throw CompressionNeedsChunkingSet{};
193
0
    else
194
0
        throw LayerRequiresChunkingSet{};
195
196
0
    h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC);
197
198
0
    const auto memDataType = makeDataType();
199
200
    // Create the DataSet using the above.
201
0
    const auto& h5file = dataset.getH5file();
202
203
0
    const auto h5dataSet = h5file.createDataSet(VR_REFINEMENT_PATH,
204
0
        memDataType, h5fileDataSpace, h5createPropList);
205
206
    // Create attributes.
207
0
    createAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
208
0
        {VR_REFINEMENT_MIN_DEPTH, VR_REFINEMENT_MAX_DEPTH,
209
0
        VR_REFINEMENT_MIN_UNCERTAINTY, VR_REFINEMENT_MAX_UNCERTAINTY});
210
211
    // Set initial min/max values.
212
0
    BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
213
0
        std::numeric_limits<float>::max(), {VR_REFINEMENT_MIN_DEPTH,
214
0
        VR_REFINEMENT_MIN_UNCERTAINTY});
215
0
    BAG::writeAttributes(h5dataSet, ::H5::PredType::NATIVE_FLOAT,
216
0
        std::numeric_limits<float>::lowest(), {VR_REFINEMENT_MAX_DEPTH,
217
0
        VR_REFINEMENT_MAX_UNCERTAINTY});
218
219
0
    return std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
220
0
        new ::H5::DataSet{h5dataSet}, DeleteH5dataSet{});
221
0
}
222
223
//! \copydoc Layer::read
224
//! Ignore rows since the data is 1 dimensional.
225
UInt8Array VRRefinements::readProxy(
226
    uint32_t /*rowStart*/,
227
    uint32_t columnStart,
228
    uint32_t /*rowEnd*/,
229
    uint32_t columnEnd) const
230
0
{
231
0
    auto pDescriptor = std::dynamic_pointer_cast<const VRRefinementsDescriptor>(
232
0
        this->getDescriptor());
233
0
    if (!pDescriptor)
234
0
        throw InvalidLayerDescriptor{};
235
236
    // Query the file for the specified rows and columns.
237
0
    const hsize_t columns = (columnEnd - columnStart) + 1;
238
0
    const hsize_t offset = columnStart;
239
240
0
    const auto fileDataSpace = m_pH5dataSet->getSpace();
241
0
    fileDataSpace.selectHyperslab(H5S_SELECT_SET, &columns, &offset);
242
243
0
    const auto bufferSize = pDescriptor->getReadBufferSize(1,
244
0
        static_cast<uint32_t>(columns));
245
0
    UInt8Array buffer{bufferSize};
246
247
0
    const ::H5::DataSpace memDataSpace{1, &columns, &columns};
248
249
0
    const auto memDataType = makeDataType();
250
251
0
    m_pH5dataSet->read(buffer.data(), memDataType, memDataSpace, fileDataSpace);
252
253
0
    return buffer;
254
0
}
255
256
//! \copydoc Layer::writeAttributes
257
void VRRefinements::writeAttributesProxy() const
258
0
{
259
0
    auto pDescriptor =
260
0
        std::dynamic_pointer_cast<const VRRefinementsDescriptor>(
261
0
            this->getDescriptor());
262
0
    if (!pDescriptor)
263
0
        throw InvalidLayerDescriptor{};
264
265
    // Write the attributes from the layer descriptor.
266
    // min/max depth
267
0
    const auto minMaxDepth = pDescriptor->getMinMaxDepth();
268
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
269
0
        std::get<0>(minMaxDepth), VR_REFINEMENT_MIN_DEPTH);
270
271
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
272
0
        std::get<1>(minMaxDepth), VR_REFINEMENT_MAX_DEPTH);
273
274
    // min/max uncertainty
275
0
    const auto minMaxUncertainty = pDescriptor->getMinMaxUncertainty();
276
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
277
0
        std::get<0>(minMaxUncertainty), VR_REFINEMENT_MIN_UNCERTAINTY);
278
279
0
    writeAttribute(*m_pH5dataSet, ::H5::PredType::NATIVE_FLOAT,
280
0
        std::get<1>(minMaxUncertainty), VR_REFINEMENT_MAX_UNCERTAINTY);
281
0
}
282
283
//! \copydoc Layer::write
284
//! Ignore rows since the data is 1 dimensional.
285
void VRRefinements::writeProxy(
286
    uint32_t /*rowStart*/,
287
    uint32_t columnStart,
288
    uint32_t /*rowEnd*/,
289
    uint32_t columnEnd,
290
    const uint8_t* buffer)
291
0
{
292
0
    auto pDescriptor = std::dynamic_pointer_cast<VRRefinementsDescriptor>(
293
0
        this->getDescriptor());
294
0
    if (!pDescriptor)
295
0
        throw InvalidLayerDescriptor{};
296
297
0
    const hsize_t columns = (columnEnd - columnStart) + 1;
298
0
    const hsize_t offset = columnStart;
299
0
    const ::H5::DataSpace memDataSpace{1, &columns, &columns};
300
301
    // Expand the file data space if needed.
302
0
    std::array<hsize_t, H5S_MAX_RANK> fileLength{};
303
0
    std::array<hsize_t, H5S_MAX_RANK> maxFileLength{};
304
305
0
    ::H5::DataSpace fileDataSpace = m_pH5dataSet->getSpace();
306
0
    const int numDims = fileDataSpace.getSimpleExtentDims(fileLength.data(),
307
0
        maxFileLength.data());
308
0
    if (numDims != 1)
309
0
        throw InvalidVRRefinementDimensions{};
310
311
0
    if (fileLength[0] < (columnEnd + 1))
312
0
    {
313
0
        const auto newMaxLength = std::max<hsize_t>(fileLength[0], columnEnd + 1);
314
315
0
        m_pH5dataSet->extend(&newMaxLength);
316
317
0
        fileDataSpace = m_pH5dataSet->getSpace();
318
319
        // Update the dataset's dimensions.
320
0
        if (this->getDataset().expired())
321
0
            throw DatasetNotFound{};
322
323
0
        auto pDataset = this->getDataset().lock();
324
0
        pDataset->getDescriptor().setDims(1, static_cast<uint32_t>(newMaxLength));
325
0
    }
326
327
0
    fileDataSpace.selectHyperslab(H5S_SELECT_SET, &columns, &offset);
328
329
0
    const auto memDataType = makeDataType();
330
331
0
    m_pH5dataSet->write(buffer, memDataType, memDataSpace, fileDataSpace);
332
333
    // Update min/max attributes
334
    // Get the current min/max from descriptor.
335
0
    float minDepth = 0.f, maxDepth = 0.f;
336
0
    std::tie(minDepth, maxDepth) = pDescriptor->getMinMaxDepth();
337
338
0
    float minUncert = 0.f, maxUncert = 0.f;
339
0
    std::tie(minUncert, maxUncert) = pDescriptor->getMinMaxUncertainty();
340
341
    // Update the min/max from new data.
342
0
    const auto* items = reinterpret_cast<const BagVRRefinementsItem*>(buffer);
343
344
0
    auto* item = items;
345
0
    const auto end = items + columns;
346
347
0
    for (; item != end; ++item)
348
0
    {
349
0
        minDepth = item->depth < minDepth ? item->depth : minDepth;
350
0
        maxDepth = item->depth > maxDepth ? item->depth : maxDepth;
351
352
0
        minUncert = item->depth_uncrt < minUncert ? item->depth_uncrt : minUncert;
353
0
        maxUncert = item->depth_uncrt > maxUncert ? item->depth_uncrt : maxUncert;
354
0
    }
355
356
0
    pDescriptor->setMinMaxDepth(minDepth, maxDepth);
357
0
    pDescriptor->setMinMaxUncertainty(minUncert, maxUncert);
358
0
}
359
360
}  // namespace BAG