Coverage Report

Created: 2025-07-11 06:33

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