Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Zarr driver
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.h"
14 :
15 : #include "cpl_compressor.h"
16 :
17 : /************************************************************************/
18 : /* ZarrV3Codec() */
19 : /************************************************************************/
20 :
21 238 : ZarrV3Codec::ZarrV3Codec(const std::string &osName) : m_osName(osName)
22 : {
23 238 : }
24 :
25 : /************************************************************************/
26 : /* ~ZarrV3Codec() */
27 : /************************************************************************/
28 :
29 : ZarrV3Codec::~ZarrV3Codec() = default;
30 :
31 : /************************************************************************/
32 : /* ZarrV3CodecGZip() */
33 : /************************************************************************/
34 :
35 54 : ZarrV3CodecGZip::ZarrV3CodecGZip() : ZarrV3Codec(NAME)
36 : {
37 54 : }
38 :
39 : /************************************************************************/
40 : /* ~ZarrV3CodecGZip() */
41 : /************************************************************************/
42 :
43 : ZarrV3CodecGZip::~ZarrV3CodecGZip() = default;
44 :
45 : /************************************************************************/
46 : /* GetConfiguration() */
47 : /************************************************************************/
48 :
49 23 : /* static */ CPLJSONObject ZarrV3CodecGZip::GetConfiguration(int nLevel)
50 : {
51 23 : CPLJSONObject oConfig;
52 23 : oConfig.Add("level", nLevel);
53 23 : return oConfig;
54 : }
55 :
56 : /************************************************************************/
57 : /* ZarrV3CodecGZip::InitFromConfiguration() */
58 : /************************************************************************/
59 :
60 54 : bool ZarrV3CodecGZip::InitFromConfiguration(
61 : const CPLJSONObject &configuration,
62 : const ZarrArrayMetadata &oInputArrayMetadata,
63 : ZarrArrayMetadata &oOutputArrayMetadata)
64 : {
65 54 : m_pCompressor = CPLGetCompressor("gzip");
66 54 : m_pDecompressor = CPLGetDecompressor("gzip");
67 54 : if (!m_pCompressor || !m_pDecompressor)
68 : {
69 0 : CPLError(CE_Failure, CPLE_AppDefined, "gzip compressor not available");
70 0 : return false;
71 : }
72 :
73 54 : m_oConfiguration = configuration.Clone();
74 54 : m_oInputArrayMetadata = oInputArrayMetadata;
75 : // byte->byte codec
76 54 : oOutputArrayMetadata = oInputArrayMetadata;
77 :
78 54 : int nLevel = 6;
79 :
80 54 : if (configuration.IsValid())
81 : {
82 54 : if (configuration.GetType() != CPLJSONObject::Type::Object)
83 : {
84 0 : CPLError(CE_Failure, CPLE_AppDefined,
85 : "Codec gzip: configuration is not an object");
86 0 : return false;
87 : }
88 :
89 108 : for (const auto &oChild : configuration.GetChildren())
90 : {
91 54 : if (oChild.GetName() != "level")
92 : {
93 0 : CPLError(
94 : CE_Failure, CPLE_AppDefined,
95 : "Codec gzip: configuration contains a unhandled member: %s",
96 0 : oChild.GetName().c_str());
97 0 : return false;
98 : }
99 : }
100 :
101 108 : const auto oLevel = configuration.GetObj("level");
102 54 : if (oLevel.IsValid())
103 : {
104 54 : if (oLevel.GetType() != CPLJSONObject::Type::Integer)
105 : {
106 0 : CPLError(CE_Failure, CPLE_AppDefined,
107 : "Codec gzip: level is not an integer");
108 0 : return false;
109 : }
110 54 : nLevel = oLevel.ToInteger();
111 54 : if (nLevel < 0 || nLevel > 9)
112 : {
113 0 : CPLError(CE_Failure, CPLE_AppDefined,
114 : "Codec gzip: invalid value for level: %d", nLevel);
115 0 : return false;
116 : }
117 : }
118 : }
119 :
120 54 : m_aosCompressorOptions.SetNameValue("LEVEL", CPLSPrintf("%d", nLevel));
121 :
122 54 : return true;
123 : }
124 :
125 : /************************************************************************/
126 : /* ZarrV3CodecGZip::Clone() */
127 : /************************************************************************/
128 :
129 8 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecGZip::Clone() const
130 : {
131 16 : auto psClone = std::make_unique<ZarrV3CodecGZip>();
132 16 : ZarrArrayMetadata oOutputArrayMetadata;
133 8 : psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
134 : oOutputArrayMetadata);
135 16 : return psClone;
136 : }
137 :
138 : /************************************************************************/
139 : /* ZarrV3CodecGZip::Encode() */
140 : /************************************************************************/
141 :
142 5363 : bool ZarrV3CodecGZip::Encode(const ZarrByteVectorQuickResize &abySrc,
143 : ZarrByteVectorQuickResize &abyDst) const
144 : {
145 5363 : abyDst.resize(abyDst.capacity());
146 5363 : void *pOutputData = abyDst.data();
147 5363 : size_t nOutputSize = abyDst.size();
148 10726 : bool bRet = m_pCompressor->pfnFunc(
149 5363 : abySrc.data(), abySrc.size(), &pOutputData, &nOutputSize,
150 5363 : m_aosCompressorOptions.List(), m_pCompressor->user_data);
151 5363 : if (bRet)
152 : {
153 5363 : abyDst.resize(nOutputSize);
154 : }
155 0 : else if (nOutputSize > abyDst.size())
156 : {
157 0 : CPLError(CE_Failure, CPLE_AppDefined,
158 : "ZarrV3CodecGZip::Encode(): output buffer too small");
159 : }
160 5363 : return bRet;
161 : }
162 :
163 : /************************************************************************/
164 : /* ZarrV3CodecGZip::Decode() */
165 : /************************************************************************/
166 :
167 5295 : bool ZarrV3CodecGZip::Decode(const ZarrByteVectorQuickResize &abySrc,
168 : ZarrByteVectorQuickResize &abyDst) const
169 : {
170 5295 : abyDst.resize(abyDst.capacity());
171 5147 : void *pOutputData = abyDst.data();
172 5124 : size_t nOutputSize = abyDst.size();
173 5238 : bool bRet = m_pDecompressor->pfnFunc(abySrc.data(), abySrc.size(),
174 : &pOutputData, &nOutputSize, nullptr,
175 5071 : m_pDecompressor->user_data);
176 5169 : if (bRet)
177 : {
178 5169 : abyDst.resize(nOutputSize);
179 : }
180 0 : else if (nOutputSize > abyDst.size())
181 : {
182 0 : CPLError(CE_Failure, CPLE_AppDefined,
183 : "ZarrV3CodecGZip::Decode(): output buffer too small");
184 : }
185 5275 : return bRet;
186 : }
187 :
188 : /************************************************************************/
189 : /* ZarrV3CodecBlosc() */
190 : /************************************************************************/
191 :
192 4 : ZarrV3CodecBlosc::ZarrV3CodecBlosc() : ZarrV3Codec(NAME)
193 : {
194 4 : }
195 :
196 : /************************************************************************/
197 : /* ~ZarrV3CodecBlosc() */
198 : /************************************************************************/
199 :
200 : ZarrV3CodecBlosc::~ZarrV3CodecBlosc() = default;
201 :
202 : /************************************************************************/
203 : /* GetConfiguration() */
204 : /************************************************************************/
205 :
206 : /* static */ CPLJSONObject
207 2 : ZarrV3CodecBlosc::GetConfiguration(const char *cname, int clevel,
208 : const char *shuffle, int typesize,
209 : int blocksize)
210 : {
211 2 : CPLJSONObject oConfig;
212 2 : oConfig.Add("cname", cname);
213 2 : oConfig.Add("clevel", clevel);
214 2 : oConfig.Add("shuffle", shuffle);
215 2 : if (strcmp(shuffle, "noshuffle") != 0)
216 1 : oConfig.Add("typesize", typesize);
217 2 : oConfig.Add("blocksize", blocksize);
218 2 : return oConfig;
219 : }
220 :
221 : /************************************************************************/
222 : /* ZarrV3CodecBlosc::InitFromConfiguration() */
223 : /************************************************************************/
224 :
225 4 : bool ZarrV3CodecBlosc::InitFromConfiguration(
226 : const CPLJSONObject &configuration,
227 : const ZarrArrayMetadata &oInputArrayMetadata,
228 : ZarrArrayMetadata &oOutputArrayMetadata)
229 : {
230 4 : m_pCompressor = CPLGetCompressor("blosc");
231 4 : m_pDecompressor = CPLGetDecompressor("blosc");
232 4 : if (!m_pCompressor || !m_pDecompressor)
233 : {
234 0 : CPLError(CE_Failure, CPLE_AppDefined, "blosc compressor not available");
235 0 : return false;
236 : }
237 :
238 4 : m_oConfiguration = configuration.Clone();
239 4 : m_oInputArrayMetadata = oInputArrayMetadata;
240 : // byte->byte codec
241 4 : oOutputArrayMetadata = oInputArrayMetadata;
242 :
243 8 : if (!configuration.IsValid() ||
244 4 : configuration.GetType() != CPLJSONObject::Type::Object)
245 : {
246 0 : CPLError(CE_Failure, CPLE_AppDefined,
247 : "Codec blosc: configuration missing or not an object");
248 0 : return false;
249 : }
250 :
251 22 : for (const auto &oChild : configuration.GetChildren())
252 : {
253 18 : const auto osName = oChild.GetName();
254 32 : if (osName != "cname" && osName != "clevel" && osName != "shuffle" &&
255 32 : osName != "typesize" && osName != "blocksize")
256 : {
257 0 : CPLError(
258 : CE_Failure, CPLE_AppDefined,
259 : "Codec blosc: configuration contains a unhandled member: %s",
260 : osName.c_str());
261 0 : return false;
262 : }
263 : }
264 :
265 12 : const auto oCname = configuration.GetObj("cname");
266 4 : if (oCname.GetType() != CPLJSONObject::Type::String)
267 : {
268 0 : CPLError(CE_Failure, CPLE_AppDefined,
269 : "Codec blosc: cname is missing or not a string");
270 0 : return false;
271 : }
272 4 : m_aosCompressorOptions.SetNameValue("CNAME", oCname.ToString().c_str());
273 :
274 12 : const auto oLevel = configuration.GetObj("clevel");
275 4 : if (oLevel.IsValid())
276 : {
277 4 : if (oLevel.GetType() != CPLJSONObject::Type::Integer)
278 : {
279 0 : CPLError(CE_Failure, CPLE_AppDefined,
280 : "Codec blosc: clevel is not an integer");
281 0 : return false;
282 : }
283 4 : const int nLevel = oLevel.ToInteger();
284 4 : if (nLevel < 0 || nLevel > 9)
285 : {
286 0 : CPLError(CE_Failure, CPLE_AppDefined,
287 : "Codec blosc: clevel value for level: %d", nLevel);
288 0 : return false;
289 : }
290 4 : m_aosCompressorOptions.SetNameValue("CLEVEL", CPLSPrintf("%d", nLevel));
291 : }
292 :
293 12 : const auto oShuffle = configuration.GetObj("shuffle");
294 4 : if (oShuffle.GetType() != CPLJSONObject::Type::String)
295 : {
296 0 : CPLError(CE_Failure, CPLE_AppDefined,
297 : "Codec blosc: shuffle is missing or not a string");
298 0 : return false;
299 : }
300 4 : if (oShuffle.ToString() == "noshuffle")
301 2 : m_aosCompressorOptions.SetNameValue("SHUFFLE", "NONE");
302 2 : else if (oShuffle.ToString() == "shuffle")
303 2 : m_aosCompressorOptions.SetNameValue("SHUFFLE", "BYTE");
304 0 : else if (oShuffle.ToString() == "bitshuffle")
305 0 : m_aosCompressorOptions.SetNameValue("SHUFFLE", "BIT");
306 : else
307 : {
308 0 : CPLError(CE_Failure, CPLE_AppDefined,
309 : "Codec blosc: Invalid value for shuffle");
310 0 : return false;
311 : }
312 :
313 12 : const auto oTypesize = configuration.GetObj("typesize");
314 4 : if (oTypesize.IsValid())
315 : {
316 2 : if (oTypesize.GetType() != CPLJSONObject::Type::Integer)
317 : {
318 0 : CPLError(CE_Failure, CPLE_AppDefined,
319 : "Codec blosc: typesize is not an integer");
320 0 : return false;
321 : }
322 2 : const int nTypeSize = oTypesize.ToInteger();
323 : m_aosCompressorOptions.SetNameValue("TYPESIZE",
324 2 : CPLSPrintf("%d", nTypeSize));
325 : }
326 :
327 12 : const auto oBlocksize = configuration.GetObj("blocksize");
328 4 : if (oBlocksize.IsValid())
329 : {
330 4 : if (oBlocksize.GetType() != CPLJSONObject::Type::Integer)
331 : {
332 0 : CPLError(CE_Failure, CPLE_AppDefined,
333 : "Codec blosc: blocksize is not an integer");
334 0 : return false;
335 : }
336 4 : const int nBlocksize = oBlocksize.ToInteger();
337 : m_aosCompressorOptions.SetNameValue("BLOCKSIZE",
338 4 : CPLSPrintf("%d", nBlocksize));
339 : }
340 :
341 4 : return true;
342 : }
343 :
344 : /************************************************************************/
345 : /* ZarrV3CodecBlosc::Clone() */
346 : /************************************************************************/
347 :
348 0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecBlosc::Clone() const
349 : {
350 0 : auto psClone = std::make_unique<ZarrV3CodecBlosc>();
351 0 : ZarrArrayMetadata oOutputArrayMetadata;
352 0 : psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
353 : oOutputArrayMetadata);
354 0 : return psClone;
355 : }
356 :
357 : /************************************************************************/
358 : /* ZarrV3CodecBlosc::Encode() */
359 : /************************************************************************/
360 :
361 2 : bool ZarrV3CodecBlosc::Encode(const ZarrByteVectorQuickResize &abySrc,
362 : ZarrByteVectorQuickResize &abyDst) const
363 : {
364 2 : abyDst.resize(abyDst.capacity());
365 2 : void *pOutputData = abyDst.data();
366 2 : size_t nOutputSize = abyDst.size();
367 4 : bool bRet = m_pCompressor->pfnFunc(
368 2 : abySrc.data(), abySrc.size(), &pOutputData, &nOutputSize,
369 2 : m_aosCompressorOptions.List(), m_pCompressor->user_data);
370 2 : if (bRet)
371 : {
372 2 : abyDst.resize(nOutputSize);
373 : }
374 0 : else if (nOutputSize > abyDst.size())
375 : {
376 0 : CPLError(CE_Failure, CPLE_AppDefined,
377 : "ZarrV3CodecBlosc::Encode(): output buffer too small");
378 : }
379 2 : return bRet;
380 : }
381 :
382 : /************************************************************************/
383 : /* ZarrV3CodecBlosc::Decode() */
384 : /************************************************************************/
385 :
386 2 : bool ZarrV3CodecBlosc::Decode(const ZarrByteVectorQuickResize &abySrc,
387 : ZarrByteVectorQuickResize &abyDst) const
388 : {
389 2 : abyDst.resize(abyDst.capacity());
390 2 : void *pOutputData = abyDst.data();
391 2 : size_t nOutputSize = abyDst.size();
392 2 : bool bRet = m_pDecompressor->pfnFunc(abySrc.data(), abySrc.size(),
393 : &pOutputData, &nOutputSize, nullptr,
394 2 : m_pDecompressor->user_data);
395 2 : if (bRet)
396 : {
397 2 : abyDst.resize(nOutputSize);
398 : }
399 0 : else if (nOutputSize > abyDst.size())
400 : {
401 0 : CPLError(CE_Failure, CPLE_AppDefined,
402 : "ZarrV3CodecBlosc::Decode(): output buffer too small");
403 : }
404 2 : return bRet;
405 : }
406 :
407 : /************************************************************************/
408 : /* ZarrV3CodecEndian() */
409 : /************************************************************************/
410 :
411 100 : ZarrV3CodecEndian::ZarrV3CodecEndian() : ZarrV3Codec(NAME)
412 : {
413 100 : }
414 :
415 : /************************************************************************/
416 : /* ~ZarrV3CodecEndian() */
417 : /************************************************************************/
418 :
419 : ZarrV3CodecEndian::~ZarrV3CodecEndian() = default;
420 :
421 : /************************************************************************/
422 : /* GetConfiguration() */
423 : /************************************************************************/
424 :
425 50 : /* static */ CPLJSONObject ZarrV3CodecEndian::GetConfiguration(bool bLittle)
426 : {
427 50 : CPLJSONObject oConfig;
428 50 : oConfig.Add("endian", bLittle ? "little" : "big");
429 50 : return oConfig;
430 : }
431 :
432 : /************************************************************************/
433 : /* ZarrV3CodecEndian::InitFromConfiguration() */
434 : /************************************************************************/
435 :
436 100 : bool ZarrV3CodecEndian::InitFromConfiguration(
437 : const CPLJSONObject &configuration,
438 : const ZarrArrayMetadata &oInputArrayMetadata,
439 : ZarrArrayMetadata &oOutputArrayMetadata)
440 : {
441 100 : m_oConfiguration = configuration.Clone();
442 100 : m_bLittle = true;
443 100 : m_oInputArrayMetadata = oInputArrayMetadata;
444 100 : oOutputArrayMetadata = oInputArrayMetadata;
445 :
446 100 : if (configuration.IsValid())
447 : {
448 100 : if (configuration.GetType() != CPLJSONObject::Type::Object)
449 : {
450 0 : CPLError(CE_Failure, CPLE_AppDefined,
451 : "Codec endian: configuration is not an object");
452 0 : return false;
453 : }
454 :
455 200 : for (const auto &oChild : configuration.GetChildren())
456 : {
457 100 : if (oChild.GetName() != "endian")
458 : {
459 0 : CPLError(CE_Failure, CPLE_AppDefined,
460 : "Codec endian: configuration contains a unhandled "
461 : "member: %s",
462 0 : oChild.GetName().c_str());
463 0 : return false;
464 : }
465 : }
466 :
467 200 : const auto oEndian = configuration.GetObj("endian");
468 100 : if (oEndian.IsValid())
469 : {
470 100 : if (oEndian.GetType() != CPLJSONObject::Type::String)
471 : {
472 0 : CPLError(CE_Failure, CPLE_AppDefined,
473 : "Codec gzip: endian is not a string");
474 0 : return false;
475 : }
476 100 : if (oEndian.ToString() == "little")
477 40 : m_bLittle = true;
478 60 : else if (oEndian.ToString() == "big")
479 60 : m_bLittle = false;
480 : else
481 : {
482 0 : CPLError(CE_Failure, CPLE_AppDefined,
483 : "Codec gzip: invalid value for endian");
484 0 : return false;
485 : }
486 : }
487 : }
488 :
489 100 : return true;
490 : }
491 :
492 : /************************************************************************/
493 : /* ZarrV3CodecEndian::Clone() */
494 : /************************************************************************/
495 :
496 0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecEndian::Clone() const
497 : {
498 0 : auto psClone = std::make_unique<ZarrV3CodecEndian>();
499 0 : ZarrArrayMetadata oOutputArrayMetadata;
500 0 : psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
501 : oOutputArrayMetadata);
502 0 : return psClone;
503 : }
504 :
505 : /************************************************************************/
506 : /* ZarrV3CodecEndian::Encode() */
507 : /************************************************************************/
508 :
509 48 : bool ZarrV3CodecEndian::Encode(const ZarrByteVectorQuickResize &abySrc,
510 : ZarrByteVectorQuickResize &abyDst) const
511 : {
512 48 : CPLAssert(!IsNoOp());
513 :
514 48 : size_t nEltCount = m_oInputArrayMetadata.GetEltCount();
515 48 : size_t nNativeSize = m_oInputArrayMetadata.oElt.nativeSize;
516 48 : if (abySrc.size() < nEltCount * nNativeSize)
517 : {
518 0 : CPLError(CE_Failure, CPLE_AppDefined,
519 : "ZarrV3CodecTranspose::Encode(): input buffer too small");
520 0 : return false;
521 : }
522 48 : CPLAssert(abySrc.size() >= nEltCount * nNativeSize);
523 48 : abyDst.resize(nEltCount * nNativeSize);
524 :
525 48 : const GByte *pabySrc = abySrc.data();
526 48 : GByte *pabyDst = abyDst.data();
527 :
528 48 : if (m_oInputArrayMetadata.oElt.nativeType ==
529 : DtypeElt::NativeType::COMPLEX_IEEEFP)
530 : {
531 0 : nEltCount *= 2;
532 0 : nNativeSize /= 2;
533 : }
534 48 : if (nNativeSize == 2)
535 : {
536 36 : for (size_t i = 0; i < nEltCount; ++i)
537 : {
538 24 : const uint16_t val = CPL_SWAP16(*reinterpret_cast<const uint16_t *>(
539 : pabySrc + sizeof(uint16_t) * i));
540 24 : memcpy(pabyDst + sizeof(uint16_t) * i, &val, sizeof(val));
541 : }
542 : }
543 36 : else if (nNativeSize == 4)
544 : {
545 54 : for (size_t i = 0; i < nEltCount; ++i)
546 : {
547 36 : const uint32_t val = CPL_SWAP32(*reinterpret_cast<const uint32_t *>(
548 : pabySrc + sizeof(uint32_t) * i));
549 36 : memcpy(pabyDst + sizeof(uint32_t) * i, &val, sizeof(val));
550 : }
551 : }
552 18 : else if (nNativeSize == 8)
553 : {
554 54 : for (size_t i = 0; i < nEltCount; ++i)
555 : {
556 36 : const uint64_t val = CPL_SWAP64(*reinterpret_cast<const uint64_t *>(
557 : pabySrc + sizeof(uint64_t) * i));
558 36 : memcpy(pabyDst + sizeof(uint64_t) * i, &val, sizeof(val));
559 : }
560 : }
561 : else
562 : {
563 0 : CPLAssert(false);
564 : }
565 48 : return true;
566 : }
567 :
568 : /************************************************************************/
569 : /* ZarrV3CodecEndian::Decode() */
570 : /************************************************************************/
571 :
572 24 : bool ZarrV3CodecEndian::Decode(const ZarrByteVectorQuickResize &abySrc,
573 : ZarrByteVectorQuickResize &abyDst) const
574 : {
575 24 : return Encode(abySrc, abyDst);
576 : }
577 :
578 : /************************************************************************/
579 : /* ZarrV3CodecTranspose() */
580 : /************************************************************************/
581 :
582 80 : ZarrV3CodecTranspose::ZarrV3CodecTranspose() : ZarrV3Codec(NAME)
583 : {
584 80 : }
585 :
586 : /************************************************************************/
587 : /* ~ZarrV3CodecTranspose() */
588 : /************************************************************************/
589 :
590 : ZarrV3CodecTranspose::~ZarrV3CodecTranspose() = default;
591 :
592 : /************************************************************************/
593 : /* IsNoOp() */
594 : /************************************************************************/
595 :
596 120 : bool ZarrV3CodecTranspose::IsNoOp() const
597 : {
598 180 : for (int i = 0; i < static_cast<int>(m_anOrder.size()); ++i)
599 : {
600 120 : if (m_anOrder[i] != i)
601 60 : return false;
602 : }
603 60 : return true;
604 : }
605 :
606 : /************************************************************************/
607 : /* GetConfiguration() */
608 : /************************************************************************/
609 :
610 : /* static */ CPLJSONObject
611 0 : ZarrV3CodecTranspose::GetConfiguration(const std::vector<int> &anOrder)
612 : {
613 0 : CPLJSONObject oConfig;
614 0 : CPLJSONArray oOrder;
615 0 : for (const auto nVal : anOrder)
616 0 : oOrder.Add(nVal);
617 0 : oConfig.Add("order", oOrder);
618 0 : return oConfig;
619 : }
620 :
621 : /************************************************************************/
622 : /* GetConfiguration() */
623 : /************************************************************************/
624 :
625 : /* static */ CPLJSONObject
626 40 : ZarrV3CodecTranspose::GetConfiguration(const std::string &osOrder)
627 : {
628 40 : CPLJSONObject oConfig;
629 80 : CPLJSONArray oOrder;
630 40 : oConfig.Add("order", osOrder);
631 80 : return oConfig;
632 : }
633 :
634 : /************************************************************************/
635 : /* ZarrV3CodecTranspose::InitFromConfiguration() */
636 : /************************************************************************/
637 :
638 80 : bool ZarrV3CodecTranspose::InitFromConfiguration(
639 : const CPLJSONObject &configuration,
640 : const ZarrArrayMetadata &oInputArrayMetadata,
641 : ZarrArrayMetadata &oOutputArrayMetadata)
642 : {
643 80 : m_oConfiguration = configuration.Clone();
644 80 : m_oInputArrayMetadata = oInputArrayMetadata;
645 80 : oOutputArrayMetadata = oInputArrayMetadata;
646 :
647 80 : if (!configuration.IsValid() &&
648 0 : configuration.GetType() != CPLJSONObject::Type::Object)
649 : {
650 0 : CPLError(CE_Failure, CPLE_AppDefined,
651 : "Codec transpose: configuration missing or not an object");
652 0 : return false;
653 : }
654 :
655 160 : for (const auto &oChild : configuration.GetChildren())
656 : {
657 80 : if (oChild.GetName() != "order")
658 : {
659 0 : CPLError(CE_Failure, CPLE_AppDefined,
660 : "Codec transpose: configuration contains a unhandled "
661 : "member: %s",
662 0 : oChild.GetName().c_str());
663 0 : return false;
664 : }
665 : }
666 :
667 240 : const auto oOrder = configuration.GetObj("order");
668 80 : const int nDims = static_cast<int>(oInputArrayMetadata.anBlockSizes.size());
669 80 : if (oOrder.GetType() == CPLJSONObject::Type::String)
670 : {
671 160 : const auto osOrder = oOrder.ToString();
672 80 : if (osOrder == "C")
673 : {
674 0 : for (int i = 0; i < nDims; ++i)
675 : {
676 0 : m_anOrder.push_back(i);
677 : }
678 : }
679 80 : else if (osOrder == "F")
680 : {
681 200 : for (int i = 0; i < nDims; ++i)
682 : {
683 120 : m_anOrder.push_back(nDims - 1 - i);
684 120 : oOutputArrayMetadata.anBlockSizes[i] =
685 120 : oInputArrayMetadata.anBlockSizes[nDims - 1 - i];
686 : }
687 : }
688 : else
689 : {
690 0 : CPLError(CE_Failure, CPLE_AppDefined,
691 : "Codec transpose: invalid value for order");
692 0 : return false;
693 : }
694 : }
695 0 : else if (oOrder.GetType() == CPLJSONObject::Type::Array)
696 : {
697 0 : const auto oOrderArray = oOrder.ToArray();
698 0 : if (oOrderArray.Size() != nDims)
699 : {
700 0 : CPLError(CE_Failure, CPLE_AppDefined,
701 : "Codec transpose: order[] does not have the expected "
702 : "number of elements");
703 0 : return false;
704 : }
705 0 : std::vector<int> oSet(nDims);
706 0 : oOutputArrayMetadata.anBlockSizes.clear();
707 0 : for (const auto &oVal : oOrderArray)
708 : {
709 0 : const int nVal = oVal.ToInteger();
710 0 : if (nVal < 0 || nVal >= nDims || oSet[nVal])
711 : {
712 0 : CPLError(CE_Failure, CPLE_AppDefined,
713 : "Codec transpose: order[] does not define a valid "
714 : "transposition");
715 0 : return false;
716 : }
717 0 : oSet[nVal] = true;
718 0 : m_anOrder.push_back(nVal);
719 0 : oOutputArrayMetadata.anBlockSizes.push_back(
720 0 : oInputArrayMetadata.anBlockSizes[nVal]);
721 : }
722 : }
723 : else
724 : {
725 0 : CPLError(CE_Failure, CPLE_AppDefined,
726 : "Codec transpose: invalid value for order");
727 0 : return false;
728 : }
729 :
730 80 : int i = 0;
731 80 : m_anReverseOrder.resize(m_anOrder.size());
732 200 : for (const auto nVal : m_anOrder)
733 : {
734 120 : m_anReverseOrder[nVal] = i;
735 120 : ++i;
736 : }
737 :
738 80 : return true;
739 : }
740 :
741 : /************************************************************************/
742 : /* ZarrV3CodecTranspose::Clone() */
743 : /************************************************************************/
744 :
745 0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecTranspose::Clone() const
746 : {
747 0 : auto psClone = std::make_unique<ZarrV3CodecTranspose>();
748 0 : ZarrArrayMetadata oOutputArrayMetadata;
749 0 : psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
750 : oOutputArrayMetadata);
751 0 : return psClone;
752 : }
753 :
754 : /************************************************************************/
755 : /* ZarrV3CodecTranspose::Transpose() */
756 : /************************************************************************/
757 :
758 40 : bool ZarrV3CodecTranspose::Transpose(const ZarrByteVectorQuickResize &abySrc,
759 : ZarrByteVectorQuickResize &abyDst,
760 : bool bEncodeDirection) const
761 : {
762 40 : CPLAssert(m_anOrder.size() == m_oInputArrayMetadata.anBlockSizes.size());
763 40 : CPLAssert(m_anReverseOrder.size() ==
764 : m_oInputArrayMetadata.anBlockSizes.size());
765 40 : const size_t nDims = m_anOrder.size();
766 40 : const size_t nSourceSize = m_oInputArrayMetadata.oElt.nativeSize;
767 40 : const auto &anBlockSizes = m_oInputArrayMetadata.anBlockSizes;
768 40 : CPLAssert(nDims > 0);
769 40 : if (abySrc.size() < m_oInputArrayMetadata.GetEltCount() * nSourceSize)
770 : {
771 0 : CPLError(CE_Failure, CPLE_AppDefined,
772 : "ZarrV3CodecTranspose::Transpose(): input buffer too small");
773 0 : return false;
774 : }
775 40 : abyDst.resize(m_oInputArrayMetadata.GetEltCount() * nSourceSize);
776 :
777 : struct Stack
778 : {
779 : size_t nIters = 0;
780 : const GByte *src_ptr = nullptr;
781 : GByte *dst_ptr = nullptr;
782 : size_t src_inc_offset = 0;
783 : size_t dst_inc_offset = 0;
784 : };
785 :
786 40 : std::vector<Stack> stack(nDims);
787 : stack.emplace_back(
788 40 : Stack()); // to make gcc 9.3 -O2 -Wnull-dereference happy
789 :
790 40 : if (!bEncodeDirection)
791 : {
792 20 : stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
793 20 : size_t nStride = nSourceSize;
794 60 : for (size_t i = nDims - 1; i > 0;)
795 : {
796 40 : --i;
797 40 : nStride *=
798 40 : static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
799 40 : stack[m_anReverseOrder[i]].src_inc_offset = nStride;
800 : }
801 :
802 20 : stack[nDims - 1].dst_inc_offset = nSourceSize;
803 20 : nStride = nSourceSize;
804 60 : for (size_t i = nDims - 1; i > 0;)
805 : {
806 40 : --i;
807 40 : nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
808 40 : stack[i].dst_inc_offset = nStride;
809 : }
810 : }
811 : else
812 : {
813 20 : stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
814 20 : size_t nStride = nSourceSize;
815 60 : for (size_t i = nDims - 1; i > 0;)
816 : {
817 40 : --i;
818 40 : nStride *=
819 40 : static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
820 40 : stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
821 : }
822 :
823 20 : stack[nDims - 1].src_inc_offset = nSourceSize;
824 20 : nStride = nSourceSize;
825 60 : for (size_t i = nDims - 1; i > 0;)
826 : {
827 40 : --i;
828 40 : nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
829 40 : stack[i].src_inc_offset = nStride;
830 : }
831 : }
832 :
833 40 : stack[0].src_ptr = abySrc.data();
834 40 : stack[0].dst_ptr = &abyDst[0];
835 :
836 40 : size_t dimIdx = 0;
837 680 : lbl_next_depth:
838 680 : if (dimIdx == nDims)
839 : {
840 480 : void *dst_ptr = stack[nDims].dst_ptr;
841 480 : const void *src_ptr = stack[nDims].src_ptr;
842 480 : if (nSourceSize == 1)
843 96 : *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
844 384 : else if (nSourceSize == 2)
845 96 : *static_cast<uint16_t *>(dst_ptr) =
846 96 : *static_cast<const uint16_t *>(src_ptr);
847 288 : else if (nSourceSize == 4)
848 144 : *static_cast<uint32_t *>(dst_ptr) =
849 144 : *static_cast<const uint32_t *>(src_ptr);
850 144 : else if (nSourceSize == 8)
851 144 : *static_cast<uint64_t *>(dst_ptr) =
852 144 : *static_cast<const uint64_t *>(src_ptr);
853 : else
854 0 : memcpy(dst_ptr, src_ptr, nSourceSize);
855 : }
856 : else
857 : {
858 200 : stack[dimIdx].nIters = static_cast<size_t>(anBlockSizes[dimIdx]);
859 : while (true)
860 : {
861 640 : dimIdx++;
862 640 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
863 640 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
864 640 : goto lbl_next_depth;
865 640 : lbl_return_to_caller:
866 640 : dimIdx--;
867 640 : if ((--stack[dimIdx].nIters) == 0)
868 200 : break;
869 440 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
870 440 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
871 : }
872 : }
873 680 : if (dimIdx > 0)
874 640 : goto lbl_return_to_caller;
875 :
876 40 : return true;
877 : }
878 :
879 : /************************************************************************/
880 : /* ZarrV3CodecTranspose::Encode() */
881 : /************************************************************************/
882 :
883 20 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
884 : ZarrByteVectorQuickResize &abyDst) const
885 : {
886 20 : CPLAssert(!IsNoOp());
887 :
888 20 : return Transpose(abySrc, abyDst, true);
889 : }
890 :
891 : /************************************************************************/
892 : /* ZarrV3CodecTranspose::Decode() */
893 : /************************************************************************/
894 :
895 20 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
896 : ZarrByteVectorQuickResize &abyDst) const
897 : {
898 20 : CPLAssert(!IsNoOp());
899 :
900 20 : return Transpose(abySrc, abyDst, false);
901 : }
902 :
903 : /************************************************************************/
904 : /* ZarrV3CodecSequence::Clone() */
905 : /************************************************************************/
906 :
907 8 : std::unique_ptr<ZarrV3CodecSequence> ZarrV3CodecSequence::Clone() const
908 : {
909 8 : auto poClone = std::make_unique<ZarrV3CodecSequence>(m_oInputArrayMetadata);
910 16 : for (const auto &poCodec : m_apoCodecs)
911 8 : poClone->m_apoCodecs.emplace_back(poCodec->Clone());
912 8 : poClone->m_oCodecArray = m_oCodecArray.Clone();
913 8 : return poClone;
914 : }
915 :
916 : /************************************************************************/
917 : /* ZarrV3CodecSequence::InitFromJson() */
918 : /************************************************************************/
919 :
920 130 : bool ZarrV3CodecSequence::InitFromJson(const CPLJSONObject &oCodecs)
921 : {
922 130 : if (oCodecs.GetType() != CPLJSONObject::Type::Array)
923 : {
924 0 : CPLError(CE_Failure, CPLE_AppDefined, "codecs is not an array");
925 0 : return false;
926 : }
927 260 : auto oCodecsArray = oCodecs.ToArray();
928 :
929 260 : ZarrArrayMetadata oInputArrayMetadata = m_oInputArrayMetadata;
930 130 : ZarrV3Codec::IOType eLastType = ZarrV3Codec::IOType::ARRAY;
931 260 : std::string osLastCodec;
932 :
933 : #if !CPL_IS_LSB
934 : const auto InsertImplicitEndianCodecIfNeeded =
935 : [this, &oInputArrayMetadata, &eLastType, &osLastCodec]()
936 : {
937 : // Insert a little endian codec if we are on a big endian target
938 : if (eLastType == ZarrV3Codec::IOType::ARRAY &&
939 : oInputArrayMetadata.oElt.nativeSize > 1)
940 : {
941 : auto poEndianCodec = std::make_unique<ZarrV3CodecEndian>();
942 : ZarrArrayMetadata oOutputArrayMetadata;
943 : poEndianCodec->InitFromConfiguration(
944 : ZarrV3CodecEndian::GetConfiguration(true), oInputArrayMetadata,
945 : oOutputArrayMetadata);
946 : oInputArrayMetadata = oOutputArrayMetadata;
947 : eLastType = poEndianCodec->GetOutputType();
948 : osLastCodec = poEndianCodec->GetName();
949 : m_apoCodecs.emplace_back(std::move(poEndianCodec));
950 : }
951 : };
952 : #endif
953 :
954 360 : for (const auto &oCodec : oCodecsArray)
955 : {
956 230 : if (oCodec.GetType() != CPLJSONObject::Type::Object)
957 : {
958 0 : CPLError(CE_Failure, CPLE_AppDefined, "codecs[] is not an array");
959 0 : return false;
960 : }
961 460 : const auto osName = oCodec["name"].ToString();
962 0 : std::unique_ptr<ZarrV3Codec> poCodec;
963 230 : if (osName == "gzip")
964 46 : poCodec = std::make_unique<ZarrV3CodecGZip>();
965 184 : else if (osName == "blosc")
966 4 : poCodec = std::make_unique<ZarrV3CodecBlosc>();
967 180 : else if (osName == "endian")
968 100 : poCodec = std::make_unique<ZarrV3CodecEndian>();
969 80 : else if (osName == "transpose")
970 80 : poCodec = std::make_unique<ZarrV3CodecTranspose>();
971 : else
972 : {
973 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported codec: %s",
974 : osName.c_str());
975 0 : return false;
976 : }
977 :
978 230 : if (poCodec->GetInputType() == ZarrV3Codec::IOType::ARRAY)
979 : {
980 180 : if (eLastType == ZarrV3Codec::IOType::BYTES)
981 : {
982 0 : CPLError(CE_Failure, CPLE_AppDefined,
983 : "Cannot chain codec %s with %s",
984 0 : poCodec->GetName().c_str(), osLastCodec.c_str());
985 0 : return false;
986 : }
987 : }
988 : #if !CPL_IS_LSB
989 : else
990 : {
991 : InsertImplicitEndianCodecIfNeeded();
992 : }
993 : #endif
994 :
995 230 : ZarrArrayMetadata oOutputArrayMetadata;
996 460 : if (!poCodec->InitFromConfiguration(oCodec["configuration"],
997 : oInputArrayMetadata,
998 230 : oOutputArrayMetadata))
999 : {
1000 0 : return false;
1001 : }
1002 230 : oInputArrayMetadata = std::move(oOutputArrayMetadata);
1003 230 : eLastType = poCodec->GetOutputType();
1004 230 : osLastCodec = poCodec->GetName();
1005 :
1006 230 : if (!poCodec->IsNoOp())
1007 118 : m_apoCodecs.emplace_back(std::move(poCodec));
1008 : }
1009 :
1010 : #if !CPL_IS_LSB
1011 : InsertImplicitEndianCodecIfNeeded();
1012 : #endif
1013 :
1014 130 : m_oCodecArray = oCodecs.Clone();
1015 130 : return true;
1016 : }
1017 :
1018 : /************************************************************************/
1019 : /* ZarrV3CodecEndian::AllocateBuffer() */
1020 : /************************************************************************/
1021 :
1022 10790 : bool ZarrV3CodecSequence::AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer)
1023 : {
1024 10790 : if (!m_apoCodecs.empty())
1025 : {
1026 10544 : const size_t nRawSize = m_oInputArrayMetadata.GetEltCount() *
1027 10676 : m_oInputArrayMetadata.oElt.nativeSize;
1028 : // Grow the temporary buffer a bit beyond the uncompressed size
1029 10676 : const size_t nMaxSize = nRawSize + nRawSize / 3 + 64;
1030 : try
1031 : {
1032 10676 : m_abyTmp.resize(nMaxSize);
1033 : }
1034 0 : catch (const std::exception &e)
1035 : {
1036 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1037 0 : return false;
1038 : }
1039 10567 : m_abyTmp.resize(nRawSize);
1040 :
1041 : // Grow the input/output buffer too if we have several steps
1042 10569 : if (m_apoCodecs.size() >= 2 && abyBuffer.capacity() < nMaxSize)
1043 : {
1044 36 : const size_t nSize = abyBuffer.size();
1045 : try
1046 : {
1047 36 : abyBuffer.resize(nMaxSize);
1048 : }
1049 0 : catch (const std::exception &e)
1050 : {
1051 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1052 0 : return false;
1053 : }
1054 36 : abyBuffer.resize(nSize);
1055 : }
1056 : }
1057 10511 : return true;
1058 : }
1059 :
1060 : /************************************************************************/
1061 : /* ZarrV3CodecSequence::Encode() */
1062 : /************************************************************************/
1063 :
1064 5405 : bool ZarrV3CodecSequence::Encode(ZarrByteVectorQuickResize &abyBuffer)
1065 : {
1066 5405 : if (!AllocateBuffer(abyBuffer))
1067 0 : return false;
1068 10814 : for (const auto &poCodec : m_apoCodecs)
1069 : {
1070 5409 : if (!poCodec->Encode(abyBuffer, m_abyTmp))
1071 0 : return false;
1072 5409 : std::swap(abyBuffer, m_abyTmp);
1073 : }
1074 5405 : return true;
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* ZarrV3CodecSequence::Decode() */
1079 : /************************************************************************/
1080 :
1081 5396 : bool ZarrV3CodecSequence::Decode(ZarrByteVectorQuickResize &abyBuffer)
1082 : {
1083 5396 : if (!AllocateBuffer(abyBuffer))
1084 0 : return false;
1085 10575 : for (auto iter = m_apoCodecs.rbegin(); iter != m_apoCodecs.rend(); ++iter)
1086 : {
1087 5208 : const auto &poCodec = *iter;
1088 5147 : if (!poCodec->Decode(abyBuffer, m_abyTmp))
1089 0 : return false;
1090 5312 : std::swap(abyBuffer, m_abyTmp);
1091 : }
1092 5265 : return true;
1093 : }
|