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