Coverage Report

Created: 2025-07-11 06:33

/src/bag/api/bag_simplelayer.cpp
Line
Count
Source (jump to first uncovered line)
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
145
    : Layer(dataset, descriptor)
28
145
    , m_pH5dataSet(std::move(pH5dataSet))
29
145
{
30
145
}
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
    uint64_t chunkSize,
50
    int compressionLevel)
51
0
{
52
0
    auto descriptor = SimpleLayerDescriptor::create(dataset, type, chunkSize,
53
0
        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
145
{
73
145
    const auto& h5file = dataset.getH5file();
74
145
    auto h5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
75
145
        new ::H5::DataSet{h5file.openDataSet(descriptor.getInternalPath())},
76
145
        DeleteH5dataSet{});
77
78
    // Read the min/max attribute values.
79
145
    const auto possibleMinMax = dataset.getMinMax(descriptor.getLayerType());
80
145
    if (std::get<0>(possibleMinMax))
81
145
        descriptor.setMinMax(std::get<1>(possibleMinMax),
82
145
            std::get<2>(possibleMinMax));
83
84
145
    return std::make_shared<SimpleLayer>(dataset, descriptor,std::move(h5dataSet));
85
145
}
86
87
88
//! Create the HDF5 DataSet.
89
/*!
90
\param dataset
91
    The BAG Dataset this layer belongs to.
92
\param descriptor
93
    The descriptor of this layer.
94
95
\return
96
    The new HDF5 DataSet.
97
*/
98
std::unique_ptr<::H5::DataSet, DeleteH5dataSet>
99
SimpleLayer::createH5dataSet(
100
    const Dataset& dataset,
101
    const SimpleLayerDescriptor& descriptor)
102
0
{
103
0
    uint32_t dim0 = 0, dim1 = 0;
104
0
    std::tie(dim0, dim1) = dataset.getDescriptor().getDims();
105
0
    const std::array<hsize_t, kRank> fileDims{dim0, dim1};
106
107
0
    ::H5::DataSpace h5dataSpace{kRank, fileDims.data(), fileDims.data()};
108
109
0
    ::H5::FloatType h5dataType;
110
0
    h5dataType.copy(::H5::PredType::NATIVE_FLOAT);
111
0
    h5dataType.setOrder(H5T_ORDER_LE);
112
113
    // Create the creation property list.
114
0
    const ::H5::DSetCreatPropList h5createPropList{};
115
0
    h5createPropList.setFillTime(H5D_FILL_TIME_ALLOC);
116
117
0
    constexpr float kFillValue = BAG_NULL_ELEVATION;
118
0
    h5createPropList.setFillValue(h5dataType, &kFillValue);
119
120
    // Use chunk size and compression level from the descriptor.
121
0
    const auto compressionLevel = descriptor.getCompressionLevel();
122
0
    const auto chunkSize = descriptor.getChunkSize();
123
0
    if (chunkSize > 0)
124
0
    {
125
0
        const std::array<hsize_t, kRank> chunkDims{chunkSize, chunkSize};
126
0
        h5createPropList.setChunk(kRank, chunkDims.data());
127
128
0
        if (compressionLevel > 0 && compressionLevel <= kMaxCompressionLevel)
129
0
            h5createPropList.setDeflate(compressionLevel);
130
0
    }
131
0
    else if (compressionLevel > 0)
132
0
        throw CompressionNeedsChunkingSet{};
133
134
    // Create the DataSet using the above.
135
0
    const auto& h5file = dataset.getH5file();
136
137
0
    auto pH5dataSet = std::unique_ptr<::H5::DataSet, DeleteH5dataSet>(
138
0
        new ::H5::DataSet{h5file.createDataSet(descriptor.getInternalPath(),
139
0
            h5dataType, h5dataSpace, h5createPropList)},
140
0
        DeleteH5dataSet{});
141
142
    // Create any attributes.
143
0
    const auto attInfo = getAttributeInfo(descriptor.getLayerType());
144
145
0
    const ::H5::DataSpace minElevDataSpace{};
146
0
    const auto minElevAtt = pH5dataSet->createAttribute(attInfo.minName,
147
0
        attInfo.h5type, minElevDataSpace);
148
149
0
    const ::H5::DataSpace maxElevDataSpace{};
150
0
    const auto maxElevAtt = pH5dataSet->createAttribute(attInfo.maxName,
151
0
        attInfo.h5type, maxElevDataSpace);
152
153
    // Set initial min/max values.
154
0
    constexpr float minElev = std::numeric_limits<float>::max();
155
0
    minElevAtt.write(attInfo.h5type, &minElev);
156
157
0
    constexpr float maxElev = std::numeric_limits<float>::lowest();
158
0
    maxElevAtt.write(attInfo.h5type, &maxElev);
159
160
0
    return pH5dataSet;
161
0
}
162
163
//! \copydoc Layer::read
164
UInt8Array SimpleLayer::readProxy(
165
    uint32_t rowStart,
166
    uint32_t columnStart,
167
    uint32_t rowEnd,
168
    uint32_t columnEnd) const
169
0
{
170
    // Query the file for the specified rows and columns.
171
0
    const auto h5fileDataSpace = m_pH5dataSet->getSpace();
172
173
0
    const auto rows = (rowEnd - rowStart) + 1;
174
0
    const auto columns = (columnEnd - columnStart) + 1;
175
0
    const std::array<hsize_t, kRank> count{rows, columns};
176
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
177
178
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
179
180
    // Initialize the output buffer.
181
0
    const auto bufferSize = this->getDescriptor()->getReadBufferSize(rows,
182
0
        columns);
183
0
    UInt8Array buffer{bufferSize};
184
185
    // Prepare the memory space.
186
0
    const ::H5::DataSpace h5memSpace{kRank, count.data(), count.data()};
187
188
0
    m_pH5dataSet->read(buffer.data(), H5Dget_type(m_pH5dataSet->getId()),
189
0
        h5memSpace, h5fileDataSpace);
190
191
0
    return buffer;
192
0
}
193
194
//! \copydoc Layer::writeAttributes
195
void SimpleLayer::writeAttributesProxy() const
196
0
{
197
0
    auto pDescriptor = this->getDescriptor();
198
0
    const auto attInfo = getAttributeInfo(pDescriptor->getLayerType());
199
200
    // Write any attributes, from the layer descriptor.
201
    // min value
202
0
    const auto minMax = pDescriptor->getMinMax();
203
204
0
    const auto minAtt = m_pH5dataSet->openAttribute(attInfo.minName);
205
0
    minAtt.write(attInfo.h5type, &std::get<0>(minMax));
206
207
    // max value
208
0
    const auto maxAtt = m_pH5dataSet->openAttribute(attInfo.maxName);
209
0
    maxAtt.write(attInfo.h5type, &std::get<1>(minMax));
210
0
}
211
212
//! \copydoc Layer::write
213
void SimpleLayer::writeProxy(
214
    uint32_t rowStart,
215
    uint32_t columnStart,
216
    uint32_t rowEnd,
217
    uint32_t columnEnd,
218
    const uint8_t* buffer)
219
0
{
220
0
    auto h5fileDataSpace = m_pH5dataSet->getSpace();
221
222
    // Make sure the area being written to does not exceed the file dimensions.
223
0
    std::array<hsize_t, kRank> fileDims{};
224
0
    h5fileDataSpace.getSimpleExtentDims(fileDims.data());
225
226
0
    if ((rowEnd >= fileDims[0]) || (columnEnd >= fileDims[1]))
227
0
        throw InvalidWriteSize{};
228
229
0
    const auto rows = (rowEnd - rowStart) + 1;
230
0
    const auto columns = (columnEnd - columnStart) + 1;
231
0
    const std::array<hsize_t, kRank> count{rows, columns};
232
0
    const std::array<hsize_t, kRank> offset{rowStart, columnStart};
233
234
0
    h5fileDataSpace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
235
236
    // Prepare the memory space.
237
0
    const ::H5::DataSpace h5memDataSpace{kRank, count.data(), count.data()};
238
239
0
    m_pH5dataSet->write(buffer, H5Dget_type(m_pH5dataSet->getId()),
240
0
        h5memDataSpace, h5fileDataSpace);
241
242
    // Update min/max attributes
243
0
    auto pDescriptor = this->getDescriptor();
244
0
    const auto attInfo = getAttributeInfo(pDescriptor->getLayerType());
245
0
    float min = 0.f, max = 0.f;
246
247
0
    if (attInfo.h5type == ::H5::PredType::NATIVE_FLOAT)
248
0
    {
249
0
        const auto* floatBuffer = reinterpret_cast<const float*>(buffer);
250
251
0
        const auto begin = floatBuffer;
252
0
        const auto end = floatBuffer + rows * columns;
253
254
0
        const auto mm = std::minmax_element(begin, end);
255
0
        min = *std::get<0>(mm);
256
0
        max = *std::get<1>(mm);
257
0
    }
258
0
    else if (attInfo.h5type == ::H5::PredType::NATIVE_UINT32)
259
0
    {
260
0
        const auto* uint32Buffer = reinterpret_cast<const uint32_t*>(buffer);
261
262
0
        const auto begin = uint32Buffer;
263
0
        const auto end = uint32Buffer + rows * columns;
264
265
0
        const auto mm = std::minmax_element(begin, end);
266
0
        min = static_cast<float>(*std::get<0>(mm));
267
0
        max = static_cast<float>(*std::get<1>(mm));
268
0
    }
269
0
    else
270
0
        throw UnsupportedAttributeType{};
271
272
0
    const float currentMin = std::get<0>(pDescriptor->getMinMax());
273
0
    const float currentMax = std::get<1>(pDescriptor->getMinMax());
274
275
0
    pDescriptor->setMinMax(std::min(currentMin, min), std::max(currentMax, max));
276
0
}
277
278
}   //namespace BAG
279