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 5261 : bool ZarrV3CodecAbstractCompressor::Decode(
73 : const ZarrByteVectorQuickResize &abySrc,
74 : ZarrByteVectorQuickResize &abyDst) const
75 : {
76 5261 : abyDst.resize(abyDst.capacity());
77 5046 : void *pOutputData = abyDst.data();
78 5158 : size_t nOutputSize = abyDst.size();
79 5102 : bool bRet = m_pDecompressor->pfnFunc(abySrc.data(), abySrc.size(),
80 : &pOutputData, &nOutputSize, nullptr,
81 5120 : m_pDecompressor->user_data);
82 5276 : if (bRet)
83 : {
84 5276 : 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 5107 : 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 : #if defined(__GNUC__)
833 : #pragma GCC diagnostic push
834 : #pragma GCC diagnostic ignored "-Wnull-dereference"
835 : #endif
836 100 : std::vector<Stack> stack(nDims);
837 : stack.emplace_back(
838 100 : Stack()); // to make gcc 9.3 -O2 -Wnull-dereference happy
839 : #if defined(__GNUC__)
840 : #pragma GCC diagnostic pop
841 : #endif
842 :
843 100 : if (!bEncodeDirection)
844 : {
845 50 : stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
846 50 : size_t nStride = nSourceSize;
847 120 : for (size_t i = nDims - 1; i > 0;)
848 : {
849 70 : --i;
850 70 : nStride *=
851 70 : static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
852 70 : stack[m_anReverseOrder[i]].src_inc_offset = nStride;
853 : }
854 :
855 50 : stack[nDims - 1].dst_inc_offset = nSourceSize;
856 50 : nStride = nSourceSize;
857 120 : for (size_t i = nDims - 1; i > 0;)
858 : {
859 70 : --i;
860 70 : nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
861 70 : stack[i].dst_inc_offset = nStride;
862 : }
863 : }
864 : else
865 : {
866 50 : stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
867 50 : size_t nStride = nSourceSize;
868 120 : for (size_t i = nDims - 1; i > 0;)
869 : {
870 70 : --i;
871 70 : nStride *=
872 70 : static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
873 70 : stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
874 : }
875 :
876 50 : stack[nDims - 1].src_inc_offset = nSourceSize;
877 50 : nStride = nSourceSize;
878 120 : for (size_t i = nDims - 1; i > 0;)
879 : {
880 70 : --i;
881 70 : nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
882 70 : stack[i].src_inc_offset = nStride;
883 : }
884 : }
885 :
886 100 : stack[0].src_ptr = abySrc.data();
887 100 : stack[0].dst_ptr = &abyDst[0];
888 :
889 100 : size_t dimIdx = 0;
890 980 : lbl_next_depth:
891 980 : if (dimIdx == nDims)
892 : {
893 600 : void *dst_ptr = stack[nDims].dst_ptr;
894 600 : const void *src_ptr = stack[nDims].src_ptr;
895 600 : if (nSourceSize == 1)
896 120 : *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
897 480 : else if (nSourceSize == 2)
898 120 : *static_cast<uint16_t *>(dst_ptr) =
899 120 : *static_cast<const uint16_t *>(src_ptr);
900 360 : else if (nSourceSize == 4)
901 180 : *static_cast<uint32_t *>(dst_ptr) =
902 180 : *static_cast<const uint32_t *>(src_ptr);
903 180 : else if (nSourceSize == 8)
904 180 : *static_cast<uint64_t *>(dst_ptr) =
905 180 : *static_cast<const uint64_t *>(src_ptr);
906 : else
907 0 : memcpy(dst_ptr, src_ptr, nSourceSize);
908 : }
909 : else
910 : {
911 380 : stack[dimIdx].nIters = static_cast<size_t>(anBlockSizes[dimIdx]);
912 : while (true)
913 : {
914 880 : dimIdx++;
915 880 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
916 880 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
917 880 : goto lbl_next_depth;
918 880 : lbl_return_to_caller:
919 880 : dimIdx--;
920 880 : if ((--stack[dimIdx].nIters) == 0)
921 380 : break;
922 500 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
923 500 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
924 : }
925 : }
926 980 : if (dimIdx > 0)
927 880 : goto lbl_return_to_caller;
928 :
929 100 : return true;
930 : }
931 :
932 : /************************************************************************/
933 : /* ZarrV3CodecTranspose::Encode() */
934 : /************************************************************************/
935 :
936 50 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
937 : ZarrByteVectorQuickResize &abyDst) const
938 : {
939 50 : CPLAssert(!IsNoOp());
940 :
941 50 : return Transpose(abySrc, abyDst, true);
942 : }
943 :
944 : /************************************************************************/
945 : /* ZarrV3CodecTranspose::Decode() */
946 : /************************************************************************/
947 :
948 50 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
949 : ZarrByteVectorQuickResize &abyDst) const
950 : {
951 50 : CPLAssert(!IsNoOp());
952 :
953 50 : return Transpose(abySrc, abyDst, false);
954 : }
955 :
956 : /************************************************************************/
957 : /* ZarrV3CodecSequence::Clone() */
958 : /************************************************************************/
959 :
960 16 : std::unique_ptr<ZarrV3CodecSequence> ZarrV3CodecSequence::Clone() const
961 : {
962 16 : auto poClone = std::make_unique<ZarrV3CodecSequence>(m_oInputArrayMetadata);
963 24 : for (const auto &poCodec : m_apoCodecs)
964 8 : poClone->m_apoCodecs.emplace_back(poCodec->Clone());
965 16 : poClone->m_oCodecArray = m_oCodecArray.Clone();
966 16 : return poClone;
967 : }
968 :
969 : /************************************************************************/
970 : /* ZarrV3CodecSequence::InitFromJson() */
971 : /************************************************************************/
972 :
973 266 : bool ZarrV3CodecSequence::InitFromJson(const CPLJSONObject &oCodecs)
974 : {
975 266 : if (oCodecs.GetType() != CPLJSONObject::Type::Array)
976 : {
977 0 : CPLError(CE_Failure, CPLE_AppDefined, "codecs is not an array");
978 0 : return false;
979 : }
980 532 : auto oCodecsArray = oCodecs.ToArray();
981 :
982 532 : ZarrArrayMetadata oInputArrayMetadata = m_oInputArrayMetadata;
983 266 : ZarrV3Codec::IOType eLastType = ZarrV3Codec::IOType::ARRAY;
984 532 : std::string osLastCodec;
985 :
986 : const auto InsertImplicitEndianCodecIfNeeded =
987 644 : [this, &oInputArrayMetadata, &eLastType, &osLastCodec]()
988 : {
989 322 : CPL_IGNORE_RET_VAL(this);
990 322 : if (eLastType == ZarrV3Codec::IOType::ARRAY &&
991 0 : oInputArrayMetadata.oElt.nativeSize > 1)
992 : {
993 0 : CPLError(CE_Warning, CPLE_AppDefined,
994 : "'bytes' codec missing. Assuming little-endian storage, "
995 : "but such tolerance may be removed in future versions");
996 0 : auto poEndianCodec = std::make_unique<ZarrV3CodecBytes>();
997 0 : ZarrArrayMetadata oOutputArrayMetadata;
998 0 : poEndianCodec->InitFromConfiguration(
999 0 : ZarrV3CodecBytes::GetConfiguration(true), oInputArrayMetadata,
1000 : oOutputArrayMetadata);
1001 0 : oInputArrayMetadata = std::move(oOutputArrayMetadata);
1002 0 : eLastType = poEndianCodec->GetOutputType();
1003 0 : osLastCodec = poEndianCodec->GetName();
1004 : if constexpr (!CPL_IS_LSB)
1005 : {
1006 : // Insert a little endian codec if we are on a big endian target
1007 : m_apoCodecs.emplace_back(std::move(poEndianCodec));
1008 : }
1009 : }
1010 322 : };
1011 :
1012 669 : for (const auto &oCodec : oCodecsArray)
1013 : {
1014 403 : if (oCodec.GetType() != CPLJSONObject::Type::Object)
1015 : {
1016 0 : CPLError(CE_Failure, CPLE_AppDefined, "codecs[] is not an array");
1017 0 : return false;
1018 : }
1019 806 : const auto osName = oCodec["name"].ToString();
1020 0 : std::unique_ptr<ZarrV3Codec> poCodec;
1021 403 : if (osName == "gzip")
1022 48 : poCodec = std::make_unique<ZarrV3CodecGZip>();
1023 355 : else if (osName == "blosc")
1024 4 : poCodec = std::make_unique<ZarrV3CodecBlosc>();
1025 351 : else if (osName == "zstd")
1026 4 : poCodec = std::make_unique<ZarrV3CodecZstd>();
1027 428 : else if (osName == "bytes" ||
1028 81 : osName == "endian" /* endian is the old name */)
1029 266 : poCodec = std::make_unique<ZarrV3CodecBytes>();
1030 81 : else if (osName == "transpose")
1031 81 : poCodec = std::make_unique<ZarrV3CodecTranspose>();
1032 : else
1033 : {
1034 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported codec: %s",
1035 : osName.c_str());
1036 0 : return false;
1037 : }
1038 :
1039 403 : if (poCodec->GetInputType() == ZarrV3Codec::IOType::ARRAY)
1040 : {
1041 347 : if (eLastType == ZarrV3Codec::IOType::BYTES)
1042 : {
1043 0 : CPLError(CE_Failure, CPLE_AppDefined,
1044 : "Cannot chain codec %s with %s",
1045 0 : poCodec->GetName().c_str(), osLastCodec.c_str());
1046 0 : return false;
1047 : }
1048 : }
1049 : else
1050 : {
1051 56 : InsertImplicitEndianCodecIfNeeded();
1052 : }
1053 :
1054 403 : ZarrArrayMetadata oOutputArrayMetadata;
1055 806 : if (!poCodec->InitFromConfiguration(oCodec["configuration"],
1056 : oInputArrayMetadata,
1057 403 : oOutputArrayMetadata))
1058 : {
1059 0 : return false;
1060 : }
1061 403 : oInputArrayMetadata = std::move(oOutputArrayMetadata);
1062 403 : eLastType = poCodec->GetOutputType();
1063 403 : osLastCodec = poCodec->GetName();
1064 :
1065 403 : if (!poCodec->IsNoOp())
1066 186 : m_apoCodecs.emplace_back(std::move(poCodec));
1067 : }
1068 :
1069 266 : InsertImplicitEndianCodecIfNeeded();
1070 :
1071 266 : m_oCodecArray = oCodecs.Clone();
1072 266 : return true;
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* ZarrV3CodecBytes::AllocateBuffer() */
1077 : /************************************************************************/
1078 :
1079 21345 : bool ZarrV3CodecSequence::AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer)
1080 : {
1081 21345 : if (!m_apoCodecs.empty())
1082 : {
1083 10436 : const size_t nRawSize = m_oInputArrayMetadata.GetEltCount() *
1084 10539 : m_oInputArrayMetadata.oElt.nativeSize;
1085 : // Grow the temporary buffer a bit beyond the uncompressed size
1086 10539 : const size_t nMaxSize = nRawSize + nRawSize / 3 + 64;
1087 : try
1088 : {
1089 10539 : m_abyTmp.resize(nMaxSize);
1090 : }
1091 0 : catch (const std::exception &e)
1092 : {
1093 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1094 0 : return false;
1095 : }
1096 10602 : m_abyTmp.resize(nRawSize);
1097 :
1098 : // Grow the input/output buffer too if we have several steps
1099 10499 : if (m_apoCodecs.size() >= 2 && abyBuffer.capacity() < nMaxSize)
1100 : {
1101 56 : const size_t nSize = abyBuffer.size();
1102 : try
1103 : {
1104 56 : abyBuffer.resize(nMaxSize);
1105 : }
1106 0 : catch (const std::exception &e)
1107 : {
1108 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1109 0 : return false;
1110 : }
1111 56 : abyBuffer.resize(nSize);
1112 : }
1113 : }
1114 20828 : return true;
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* ZarrV3CodecSequence::Encode() */
1119 : /************************************************************************/
1120 :
1121 10747 : bool ZarrV3CodecSequence::Encode(ZarrByteVectorQuickResize &abyBuffer)
1122 : {
1123 10747 : if (!AllocateBuffer(abyBuffer))
1124 0 : return false;
1125 16188 : for (const auto &poCodec : m_apoCodecs)
1126 : {
1127 5441 : if (!poCodec->Encode(abyBuffer, m_abyTmp))
1128 0 : return false;
1129 5441 : std::swap(abyBuffer, m_abyTmp);
1130 : }
1131 10747 : return true;
1132 : }
1133 :
1134 : /************************************************************************/
1135 : /* ZarrV3CodecSequence::Decode() */
1136 : /************************************************************************/
1137 :
1138 10631 : bool ZarrV3CodecSequence::Decode(ZarrByteVectorQuickResize &abyBuffer)
1139 : {
1140 10631 : if (!AllocateBuffer(abyBuffer))
1141 0 : return false;
1142 15593 : for (auto iter = m_apoCodecs.rbegin(); iter != m_apoCodecs.rend(); ++iter)
1143 : {
1144 5044 : const auto &poCodec = *iter;
1145 5008 : if (!poCodec->Decode(abyBuffer, m_abyTmp))
1146 0 : return false;
1147 5335 : std::swap(abyBuffer, m_abyTmp);
1148 : }
1149 10301 : return true;
1150 : }
|