Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Zarr driver, "transpose" codec
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "zarr_v3_codec.h"
14 :
15 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/transpose/index.html
16 :
17 : /************************************************************************/
18 : /* ZarrV3CodecTranspose() */
19 : /************************************************************************/
20 :
21 83 : ZarrV3CodecTranspose::ZarrV3CodecTranspose() : ZarrV3Codec(NAME)
22 : {
23 83 : }
24 :
25 : /************************************************************************/
26 : /* IsNoOp() */
27 : /************************************************************************/
28 :
29 184 : bool ZarrV3CodecTranspose::IsNoOp() const
30 : {
31 184 : for (int i = 0; i < static_cast<int>(m_anOrder.size()); ++i)
32 : {
33 184 : if (m_anOrder[i] != i)
34 184 : return false;
35 : }
36 0 : return true;
37 : }
38 :
39 : /************************************************************************/
40 : /* GetConfiguration() */
41 : /************************************************************************/
42 :
43 : /* static */ CPLJSONObject
44 40 : ZarrV3CodecTranspose::GetConfiguration(const std::vector<int> &anOrder)
45 : {
46 40 : CPLJSONObject oConfig;
47 80 : CPLJSONArray oOrder;
48 130 : for (const auto nVal : anOrder)
49 90 : oOrder.Add(nVal);
50 40 : oConfig.Add("order", oOrder);
51 80 : return oConfig;
52 : }
53 :
54 : /************************************************************************/
55 : /* ZarrV3CodecTranspose::InitFromConfiguration() */
56 : /************************************************************************/
57 :
58 83 : bool ZarrV3CodecTranspose::InitFromConfiguration(
59 : const CPLJSONObject &configuration,
60 : const ZarrArrayMetadata &oInputArrayMetadata,
61 : ZarrArrayMetadata &oOutputArrayMetadata, bool /* bEmitWarnings */)
62 : {
63 83 : m_oConfiguration = configuration.Clone();
64 83 : m_oInputArrayMetadata = oInputArrayMetadata;
65 83 : oOutputArrayMetadata = oInputArrayMetadata;
66 :
67 83 : if (!configuration.IsValid() &&
68 0 : configuration.GetType() != CPLJSONObject::Type::Object)
69 : {
70 0 : CPLError(CE_Failure, CPLE_AppDefined,
71 : "Codec transpose: configuration missing or not an object");
72 0 : return false;
73 : }
74 :
75 166 : for (const auto &oChild : configuration.GetChildren())
76 : {
77 83 : if (oChild.GetName() != "order")
78 : {
79 0 : CPLError(CE_Failure, CPLE_AppDefined,
80 : "Codec transpose: configuration contains a unhandled "
81 : "member: %s",
82 0 : oChild.GetName().c_str());
83 0 : return false;
84 : }
85 : }
86 :
87 249 : const auto oOrder = configuration.GetObj("order");
88 83 : const int nDims = static_cast<int>(oInputArrayMetadata.anBlockSizes.size());
89 83 : if (oOrder.GetType() == CPLJSONObject::Type::String)
90 : {
91 : // Deprecated
92 0 : const auto osOrder = oOrder.ToString();
93 0 : if (osOrder == "C")
94 : {
95 0 : for (int i = 0; i < nDims; ++i)
96 : {
97 0 : m_anOrder.push_back(i);
98 : }
99 : }
100 0 : else if (osOrder == "F")
101 : {
102 0 : for (int i = 0; i < nDims; ++i)
103 : {
104 0 : m_anOrder.push_back(nDims - 1 - i);
105 0 : oOutputArrayMetadata.anBlockSizes[i] =
106 0 : oInputArrayMetadata.anBlockSizes[nDims - 1 - i];
107 : }
108 : }
109 : else
110 : {
111 0 : CPLError(CE_Failure, CPLE_AppDefined,
112 : "Codec transpose: invalid value for order");
113 0 : return false;
114 : }
115 : }
116 83 : else if (oOrder.GetType() == CPLJSONObject::Type::Array)
117 : {
118 83 : const auto oOrderArray = oOrder.ToArray();
119 83 : if (oOrderArray.Size() != nDims)
120 : {
121 0 : CPLError(CE_Failure, CPLE_AppDefined,
122 : "Codec transpose: order[] does not have the expected "
123 : "number of elements");
124 0 : return false;
125 : }
126 83 : std::vector<int> oSet(nDims);
127 83 : oOutputArrayMetadata.anBlockSizes.clear();
128 270 : for (const auto &oVal : oOrderArray)
129 : {
130 187 : const int nVal = oVal.ToInteger();
131 187 : if (nVal < 0 || nVal >= nDims || oSet[nVal])
132 : {
133 0 : CPLError(CE_Failure, CPLE_AppDefined,
134 : "Codec transpose: order[] does not define a valid "
135 : "transposition");
136 0 : return false;
137 : }
138 187 : oSet[nVal] = true;
139 187 : m_anOrder.push_back(nVal);
140 187 : oOutputArrayMetadata.anBlockSizes.push_back(
141 187 : oInputArrayMetadata.anBlockSizes[nVal]);
142 : }
143 : }
144 : else
145 : {
146 0 : CPLError(CE_Failure, CPLE_AppDefined,
147 : "Codec transpose: invalid value for order");
148 0 : return false;
149 : }
150 :
151 83 : int i = 0;
152 83 : m_anReverseOrder.resize(m_anOrder.size());
153 270 : for (const auto nVal : m_anOrder)
154 : {
155 187 : m_anReverseOrder[nVal] = i;
156 187 : ++i;
157 : }
158 :
159 83 : return true;
160 : }
161 :
162 : /************************************************************************/
163 : /* ZarrV3CodecTranspose::GetInnerMostBlockSize() */
164 : /************************************************************************/
165 :
166 82 : std::vector<size_t> ZarrV3CodecTranspose::GetInnerMostBlockSize(
167 : const std::vector<size_t> &anInnerBlockSize) const
168 : {
169 82 : std::vector<size_t> ret;
170 266 : for (int idx : m_anReverseOrder)
171 184 : ret.push_back(anInnerBlockSize[idx]);
172 82 : return ret;
173 : }
174 :
175 : /************************************************************************/
176 : /* ZarrV3CodecTranspose::Clone() */
177 : /************************************************************************/
178 :
179 0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecTranspose::Clone() const
180 : {
181 0 : auto psClone = std::make_unique<ZarrV3CodecTranspose>();
182 0 : ZarrArrayMetadata oOutputArrayMetadata;
183 0 : psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
184 : oOutputArrayMetadata,
185 : /* bEmitWarnings = */ false);
186 0 : return psClone;
187 : }
188 :
189 : /************************************************************************/
190 : /* ZarrV3CodecTranspose::Transpose() */
191 : /************************************************************************/
192 :
193 125 : bool ZarrV3CodecTranspose::Transpose(
194 : const ZarrByteVectorQuickResize &abySrc, ZarrByteVectorQuickResize &abyDst,
195 : bool bEncodeDirection, const std::vector<size_t> &anForwardBlockSizes) const
196 : {
197 125 : CPLAssert(m_anOrder.size() == anForwardBlockSizes.size());
198 125 : CPLAssert(m_anReverseOrder.size() == anForwardBlockSizes.size());
199 125 : const size_t nDims = m_anOrder.size();
200 125 : const size_t nSourceSize = m_oInputArrayMetadata.oElt.nativeSize;
201 125 : CPLAssert(nDims > 0);
202 125 : if (abySrc.size() < MultiplyElements(anForwardBlockSizes) * nSourceSize)
203 : {
204 0 : CPLError(CE_Failure, CPLE_AppDefined,
205 : "ZarrV3CodecTranspose::Transpose(): input buffer too small");
206 0 : return false;
207 : }
208 125 : abyDst.resize(MultiplyElements(anForwardBlockSizes) * nSourceSize);
209 :
210 : struct Stack
211 : {
212 : size_t nIters = 0;
213 : const GByte *src_ptr = nullptr;
214 : GByte *dst_ptr = nullptr;
215 : size_t src_inc_offset = 0;
216 : size_t dst_inc_offset = 0;
217 : };
218 :
219 : #if defined(__GNUC__)
220 : #pragma GCC diagnostic push
221 : #pragma GCC diagnostic ignored "-Wnull-dereference"
222 : #endif
223 125 : std::vector<Stack> stack(nDims);
224 : stack.emplace_back(
225 125 : Stack()); // to make gcc 9.3 -O2 -Wnull-dereference happy
226 : #if defined(__GNUC__)
227 : #pragma GCC diagnostic pop
228 : #endif
229 :
230 125 : if (!bEncodeDirection)
231 : {
232 75 : stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
233 75 : size_t nStride = nSourceSize;
234 170 : for (size_t i = nDims - 1; i > 0;)
235 : {
236 95 : --i;
237 95 : nStride *= anForwardBlockSizes[m_anReverseOrder[i + 1]];
238 95 : stack[m_anReverseOrder[i]].src_inc_offset = nStride;
239 : }
240 :
241 75 : stack[nDims - 1].dst_inc_offset = nSourceSize;
242 75 : nStride = nSourceSize;
243 170 : for (size_t i = nDims - 1; i > 0;)
244 : {
245 95 : --i;
246 95 : nStride *= anForwardBlockSizes[i + 1];
247 95 : stack[i].dst_inc_offset = nStride;
248 : }
249 : }
250 : else
251 : {
252 50 : stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
253 50 : size_t nStride = nSourceSize;
254 120 : for (size_t i = nDims - 1; i > 0;)
255 : {
256 70 : --i;
257 70 : nStride *= anForwardBlockSizes[m_anReverseOrder[i + 1]];
258 70 : stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
259 : }
260 :
261 50 : stack[nDims - 1].src_inc_offset = nSourceSize;
262 50 : nStride = nSourceSize;
263 120 : for (size_t i = nDims - 1; i > 0;)
264 : {
265 70 : --i;
266 70 : nStride *= anForwardBlockSizes[i + 1];
267 70 : stack[i].src_inc_offset = nStride;
268 : }
269 : }
270 :
271 125 : stack[0].src_ptr = abySrc.data();
272 125 : stack[0].dst_ptr = &abyDst[0];
273 :
274 125 : size_t dimIdx = 0;
275 1880 : lbl_next_depth:
276 1880 : if (dimIdx == nDims)
277 : {
278 1350 : void *dst_ptr = stack[nDims].dst_ptr;
279 1350 : const void *src_ptr = stack[nDims].src_ptr;
280 1350 : if (nSourceSize == 1)
281 120 : *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
282 1230 : else if (nSourceSize == 2)
283 120 : *static_cast<uint16_t *>(dst_ptr) =
284 120 : *static_cast<const uint16_t *>(src_ptr);
285 1110 : else if (nSourceSize == 4)
286 930 : *static_cast<uint32_t *>(dst_ptr) =
287 930 : *static_cast<const uint32_t *>(src_ptr);
288 180 : else if (nSourceSize == 8)
289 180 : *static_cast<uint64_t *>(dst_ptr) =
290 180 : *static_cast<const uint64_t *>(src_ptr);
291 : else
292 0 : memcpy(dst_ptr, src_ptr, nSourceSize);
293 : }
294 : else
295 : {
296 530 : stack[dimIdx].nIters = anForwardBlockSizes[dimIdx];
297 : while (true)
298 : {
299 1755 : dimIdx++;
300 1755 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
301 1755 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
302 1755 : goto lbl_next_depth;
303 1755 : lbl_return_to_caller:
304 1755 : dimIdx--;
305 1755 : if ((--stack[dimIdx].nIters) == 0)
306 530 : break;
307 1225 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
308 1225 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
309 : }
310 : }
311 1880 : if (dimIdx > 0)
312 1755 : goto lbl_return_to_caller;
313 :
314 125 : return true;
315 : }
316 :
317 : /************************************************************************/
318 : /* ZarrV3CodecTranspose::Encode() */
319 : /************************************************************************/
320 :
321 50 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
322 : ZarrByteVectorQuickResize &abyDst) const
323 : {
324 50 : CPLAssert(!IsNoOp());
325 :
326 50 : return Transpose(abySrc, abyDst, true, m_oInputArrayMetadata.anBlockSizes);
327 : }
328 :
329 : /************************************************************************/
330 : /* ZarrV3CodecTranspose::Decode() */
331 : /************************************************************************/
332 :
333 50 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
334 : ZarrByteVectorQuickResize &abyDst) const
335 : {
336 50 : CPLAssert(!IsNoOp());
337 :
338 50 : return Transpose(abySrc, abyDst, false, m_oInputArrayMetadata.anBlockSizes);
339 : }
340 :
341 : /************************************************************************/
342 : /* ZarrV3CodecTranspose::DecodePartial() */
343 : /************************************************************************/
344 :
345 25 : bool ZarrV3CodecTranspose::DecodePartial(
346 : VSIVirtualHandle * /* poFile */, const ZarrByteVectorQuickResize &abySrc,
347 : ZarrByteVectorQuickResize &abyDst, std::vector<size_t> &anStartIdx,
348 : std::vector<size_t> &anCount)
349 : {
350 25 : CPLAssert(anStartIdx.size() == m_oInputArrayMetadata.anBlockSizes.size());
351 25 : CPLAssert(anStartIdx.size() == anCount.size());
352 :
353 25 : Reorder1DInverse(anStartIdx);
354 25 : Reorder1DInverse(anCount);
355 :
356 : // Note that we don't need to take anStartIdx into account for the
357 : // transpose operation, as abySrc corresponds to anStartIdx.
358 25 : return Transpose(abySrc, abyDst, false, anCount);
359 : }
|