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 392 : ZarrV3Codec::ZarrV3Codec(const std::string &osName) : m_osName(osName)
22 : {
23 392 : }
24 :
25 : /************************************************************************/
26 : /* ~ZarrV3Codec() */
27 : /************************************************************************/
28 :
29 : ZarrV3Codec::~ZarrV3Codec() = default;
30 :
31 : /************************************************************************/
32 : /* ZarrV3CodecAbstractCompressor() */
33 : /************************************************************************/
34 :
35 62 : ZarrV3CodecAbstractCompressor::ZarrV3CodecAbstractCompressor(
36 62 : const std::string &osName)
37 62 : : ZarrV3Codec(osName)
38 : {
39 62 : }
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 5295 : bool ZarrV3CodecAbstractCompressor::Decode(
73 : const ZarrByteVectorQuickResize &abySrc,
74 : ZarrByteVectorQuickResize &abyDst) const
75 : {
76 5295 : abyDst.resize(abyDst.capacity());
77 5147 : void *pOutputData = abyDst.data();
78 5041 : size_t nOutputSize = abyDst.size();
79 5117 : bool bRet = m_pDecompressor->pfnFunc(abySrc.data(), abySrc.size(),
80 : &pOutputData, &nOutputSize, nullptr,
81 5031 : m_pDecompressor->user_data);
82 5324 : if (bRet)
83 : {
84 5324 : 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 5120 : return bRet;
93 : }
94 :
95 : /************************************************************************/
96 : /* ZarrV3CodecGZip() */
97 : /************************************************************************/
98 :
99 54 : ZarrV3CodecGZip::ZarrV3CodecGZip() : ZarrV3CodecAbstractCompressor(NAME)
100 : {
101 54 : }
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 54 : bool ZarrV3CodecGZip::InitFromConfiguration(
119 : const CPLJSONObject &configuration,
120 : const ZarrArrayMetadata &oInputArrayMetadata,
121 : ZarrArrayMetadata &oOutputArrayMetadata)
122 : {
123 54 : m_pCompressor = CPLGetCompressor("gzip");
124 54 : m_pDecompressor = CPLGetDecompressor("gzip");
125 54 : if (!m_pCompressor || !m_pDecompressor)
126 : {
127 0 : CPLError(CE_Failure, CPLE_AppDefined, "gzip compressor not available");
128 0 : return false;
129 : }
130 :
131 54 : m_oConfiguration = configuration.Clone();
132 54 : m_oInputArrayMetadata = oInputArrayMetadata;
133 : // byte->byte codec
134 54 : oOutputArrayMetadata = oInputArrayMetadata;
135 :
136 54 : int nLevel = 6;
137 :
138 54 : if (configuration.IsValid())
139 : {
140 54 : 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 108 : for (const auto &oChild : configuration.GetChildren())
148 : {
149 54 : 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 108 : const auto oLevel = configuration.GetObj("level");
160 54 : if (oLevel.IsValid())
161 : {
162 54 : 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 54 : nLevel = oLevel.ToInteger();
169 54 : 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 54 : m_aosCompressorOptions.SetNameValue("LEVEL", CPLSPrintf("%d", nLevel));
179 :
180 54 : 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("CKECKSUM", "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 250 : ZarrV3CodecBytes::ZarrV3CodecBytes() : ZarrV3Codec(NAME)
482 : {
483 250 : }
484 :
485 : /************************************************************************/
486 : /* GetConfiguration() */
487 : /************************************************************************/
488 :
489 122 : /* static */ CPLJSONObject ZarrV3CodecBytes::GetConfiguration(bool bLittle)
490 : {
491 122 : CPLJSONObject oConfig;
492 122 : oConfig.Add("endian", bLittle ? "little" : "big");
493 122 : return oConfig;
494 : }
495 :
496 : /************************************************************************/
497 : /* ZarrV3CodecBytes::InitFromConfiguration() */
498 : /************************************************************************/
499 :
500 250 : bool ZarrV3CodecBytes::InitFromConfiguration(
501 : const CPLJSONObject &configuration,
502 : const ZarrArrayMetadata &oInputArrayMetadata,
503 : ZarrArrayMetadata &oOutputArrayMetadata)
504 : {
505 250 : m_oConfiguration = configuration.Clone();
506 250 : m_bLittle = true;
507 250 : m_oInputArrayMetadata = oInputArrayMetadata;
508 250 : oOutputArrayMetadata = oInputArrayMetadata;
509 :
510 250 : if (configuration.IsValid())
511 : {
512 243 : 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 486 : for (const auto &oChild : configuration.GetChildren())
520 : {
521 243 : 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 486 : const auto oEndian = configuration.GetObj("endian");
532 243 : if (oEndian.IsValid())
533 : {
534 243 : 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 243 : if (oEndian.ToString() == "little")
541 183 : m_bLittle = true;
542 60 : else if (oEndian.ToString() == "big")
543 60 : 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 250 : 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 : "ZarrV3CodecTranspose::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 80 : ZarrV3CodecTranspose::ZarrV3CodecTranspose() : ZarrV3Codec(NAME)
647 : {
648 80 : }
649 :
650 : /************************************************************************/
651 : /* IsNoOp() */
652 : /************************************************************************/
653 :
654 120 : bool ZarrV3CodecTranspose::IsNoOp() const
655 : {
656 180 : for (int i = 0; i < static_cast<int>(m_anOrder.size()); ++i)
657 : {
658 120 : if (m_anOrder[i] != i)
659 60 : return false;
660 : }
661 60 : return true;
662 : }
663 :
664 : /************************************************************************/
665 : /* GetConfiguration() */
666 : /************************************************************************/
667 :
668 : /* static */ CPLJSONObject
669 0 : ZarrV3CodecTranspose::GetConfiguration(const std::vector<int> &anOrder)
670 : {
671 0 : CPLJSONObject oConfig;
672 0 : CPLJSONArray oOrder;
673 0 : for (const auto nVal : anOrder)
674 0 : oOrder.Add(nVal);
675 0 : oConfig.Add("order", oOrder);
676 0 : return oConfig;
677 : }
678 :
679 : /************************************************************************/
680 : /* GetConfiguration() */
681 : /************************************************************************/
682 :
683 : /* static */ CPLJSONObject
684 40 : ZarrV3CodecTranspose::GetConfiguration(const std::string &osOrder)
685 : {
686 40 : CPLJSONObject oConfig;
687 80 : CPLJSONArray oOrder;
688 40 : oConfig.Add("order", osOrder);
689 80 : return oConfig;
690 : }
691 :
692 : /************************************************************************/
693 : /* ZarrV3CodecTranspose::InitFromConfiguration() */
694 : /************************************************************************/
695 :
696 80 : bool ZarrV3CodecTranspose::InitFromConfiguration(
697 : const CPLJSONObject &configuration,
698 : const ZarrArrayMetadata &oInputArrayMetadata,
699 : ZarrArrayMetadata &oOutputArrayMetadata)
700 : {
701 80 : m_oConfiguration = configuration.Clone();
702 80 : m_oInputArrayMetadata = oInputArrayMetadata;
703 80 : oOutputArrayMetadata = oInputArrayMetadata;
704 :
705 80 : if (!configuration.IsValid() &&
706 0 : configuration.GetType() != CPLJSONObject::Type::Object)
707 : {
708 0 : CPLError(CE_Failure, CPLE_AppDefined,
709 : "Codec transpose: configuration missing or not an object");
710 0 : return false;
711 : }
712 :
713 160 : for (const auto &oChild : configuration.GetChildren())
714 : {
715 80 : if (oChild.GetName() != "order")
716 : {
717 0 : CPLError(CE_Failure, CPLE_AppDefined,
718 : "Codec transpose: configuration contains a unhandled "
719 : "member: %s",
720 0 : oChild.GetName().c_str());
721 0 : return false;
722 : }
723 : }
724 :
725 240 : const auto oOrder = configuration.GetObj("order");
726 80 : const int nDims = static_cast<int>(oInputArrayMetadata.anBlockSizes.size());
727 80 : if (oOrder.GetType() == CPLJSONObject::Type::String)
728 : {
729 160 : const auto osOrder = oOrder.ToString();
730 80 : if (osOrder == "C")
731 : {
732 0 : for (int i = 0; i < nDims; ++i)
733 : {
734 0 : m_anOrder.push_back(i);
735 : }
736 : }
737 80 : else if (osOrder == "F")
738 : {
739 200 : for (int i = 0; i < nDims; ++i)
740 : {
741 120 : m_anOrder.push_back(nDims - 1 - i);
742 120 : oOutputArrayMetadata.anBlockSizes[i] =
743 120 : oInputArrayMetadata.anBlockSizes[nDims - 1 - i];
744 : }
745 : }
746 : else
747 : {
748 0 : CPLError(CE_Failure, CPLE_AppDefined,
749 : "Codec transpose: invalid value for order");
750 0 : return false;
751 : }
752 : }
753 0 : else if (oOrder.GetType() == CPLJSONObject::Type::Array)
754 : {
755 0 : const auto oOrderArray = oOrder.ToArray();
756 0 : if (oOrderArray.Size() != nDims)
757 : {
758 0 : CPLError(CE_Failure, CPLE_AppDefined,
759 : "Codec transpose: order[] does not have the expected "
760 : "number of elements");
761 0 : return false;
762 : }
763 0 : std::vector<int> oSet(nDims);
764 0 : oOutputArrayMetadata.anBlockSizes.clear();
765 0 : for (const auto &oVal : oOrderArray)
766 : {
767 0 : const int nVal = oVal.ToInteger();
768 0 : if (nVal < 0 || nVal >= nDims || oSet[nVal])
769 : {
770 0 : CPLError(CE_Failure, CPLE_AppDefined,
771 : "Codec transpose: order[] does not define a valid "
772 : "transposition");
773 0 : return false;
774 : }
775 0 : oSet[nVal] = true;
776 0 : m_anOrder.push_back(nVal);
777 0 : oOutputArrayMetadata.anBlockSizes.push_back(
778 0 : oInputArrayMetadata.anBlockSizes[nVal]);
779 : }
780 : }
781 : else
782 : {
783 0 : CPLError(CE_Failure, CPLE_AppDefined,
784 : "Codec transpose: invalid value for order");
785 0 : return false;
786 : }
787 :
788 80 : int i = 0;
789 80 : m_anReverseOrder.resize(m_anOrder.size());
790 200 : for (const auto nVal : m_anOrder)
791 : {
792 120 : m_anReverseOrder[nVal] = i;
793 120 : ++i;
794 : }
795 :
796 80 : return true;
797 : }
798 :
799 : /************************************************************************/
800 : /* ZarrV3CodecTranspose::Clone() */
801 : /************************************************************************/
802 :
803 0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecTranspose::Clone() const
804 : {
805 0 : auto psClone = std::make_unique<ZarrV3CodecTranspose>();
806 0 : ZarrArrayMetadata oOutputArrayMetadata;
807 0 : psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
808 : oOutputArrayMetadata);
809 0 : return psClone;
810 : }
811 :
812 : /************************************************************************/
813 : /* ZarrV3CodecTranspose::Transpose() */
814 : /************************************************************************/
815 :
816 40 : bool ZarrV3CodecTranspose::Transpose(const ZarrByteVectorQuickResize &abySrc,
817 : ZarrByteVectorQuickResize &abyDst,
818 : bool bEncodeDirection) const
819 : {
820 40 : CPLAssert(m_anOrder.size() == m_oInputArrayMetadata.anBlockSizes.size());
821 40 : CPLAssert(m_anReverseOrder.size() ==
822 : m_oInputArrayMetadata.anBlockSizes.size());
823 40 : const size_t nDims = m_anOrder.size();
824 40 : const size_t nSourceSize = m_oInputArrayMetadata.oElt.nativeSize;
825 40 : const auto &anBlockSizes = m_oInputArrayMetadata.anBlockSizes;
826 40 : CPLAssert(nDims > 0);
827 40 : if (abySrc.size() < m_oInputArrayMetadata.GetEltCount() * nSourceSize)
828 : {
829 0 : CPLError(CE_Failure, CPLE_AppDefined,
830 : "ZarrV3CodecTranspose::Transpose(): input buffer too small");
831 0 : return false;
832 : }
833 40 : abyDst.resize(m_oInputArrayMetadata.GetEltCount() * nSourceSize);
834 :
835 : struct Stack
836 : {
837 : size_t nIters = 0;
838 : const GByte *src_ptr = nullptr;
839 : GByte *dst_ptr = nullptr;
840 : size_t src_inc_offset = 0;
841 : size_t dst_inc_offset = 0;
842 : };
843 :
844 40 : std::vector<Stack> stack(nDims);
845 : stack.emplace_back(
846 40 : Stack()); // to make gcc 9.3 -O2 -Wnull-dereference happy
847 :
848 40 : if (!bEncodeDirection)
849 : {
850 20 : stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
851 20 : size_t nStride = nSourceSize;
852 60 : for (size_t i = nDims - 1; i > 0;)
853 : {
854 40 : --i;
855 40 : nStride *=
856 40 : static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
857 40 : stack[m_anReverseOrder[i]].src_inc_offset = nStride;
858 : }
859 :
860 20 : stack[nDims - 1].dst_inc_offset = nSourceSize;
861 20 : nStride = nSourceSize;
862 60 : for (size_t i = nDims - 1; i > 0;)
863 : {
864 40 : --i;
865 40 : nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
866 40 : stack[i].dst_inc_offset = nStride;
867 : }
868 : }
869 : else
870 : {
871 20 : stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
872 20 : size_t nStride = nSourceSize;
873 60 : for (size_t i = nDims - 1; i > 0;)
874 : {
875 40 : --i;
876 40 : nStride *=
877 40 : static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
878 40 : stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
879 : }
880 :
881 20 : stack[nDims - 1].src_inc_offset = nSourceSize;
882 20 : nStride = nSourceSize;
883 60 : for (size_t i = nDims - 1; i > 0;)
884 : {
885 40 : --i;
886 40 : nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
887 40 : stack[i].src_inc_offset = nStride;
888 : }
889 : }
890 :
891 40 : stack[0].src_ptr = abySrc.data();
892 40 : stack[0].dst_ptr = &abyDst[0];
893 :
894 40 : size_t dimIdx = 0;
895 680 : lbl_next_depth:
896 680 : if (dimIdx == nDims)
897 : {
898 480 : void *dst_ptr = stack[nDims].dst_ptr;
899 480 : const void *src_ptr = stack[nDims].src_ptr;
900 480 : if (nSourceSize == 1)
901 96 : *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
902 384 : else if (nSourceSize == 2)
903 96 : *static_cast<uint16_t *>(dst_ptr) =
904 96 : *static_cast<const uint16_t *>(src_ptr);
905 288 : else if (nSourceSize == 4)
906 144 : *static_cast<uint32_t *>(dst_ptr) =
907 144 : *static_cast<const uint32_t *>(src_ptr);
908 144 : else if (nSourceSize == 8)
909 144 : *static_cast<uint64_t *>(dst_ptr) =
910 144 : *static_cast<const uint64_t *>(src_ptr);
911 : else
912 0 : memcpy(dst_ptr, src_ptr, nSourceSize);
913 : }
914 : else
915 : {
916 200 : stack[dimIdx].nIters = static_cast<size_t>(anBlockSizes[dimIdx]);
917 : while (true)
918 : {
919 640 : dimIdx++;
920 640 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
921 640 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
922 640 : goto lbl_next_depth;
923 640 : lbl_return_to_caller:
924 640 : dimIdx--;
925 640 : if ((--stack[dimIdx].nIters) == 0)
926 200 : break;
927 440 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
928 440 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
929 : }
930 : }
931 680 : if (dimIdx > 0)
932 640 : goto lbl_return_to_caller;
933 :
934 40 : return true;
935 : }
936 :
937 : /************************************************************************/
938 : /* ZarrV3CodecTranspose::Encode() */
939 : /************************************************************************/
940 :
941 20 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
942 : ZarrByteVectorQuickResize &abyDst) const
943 : {
944 20 : CPLAssert(!IsNoOp());
945 :
946 20 : return Transpose(abySrc, abyDst, true);
947 : }
948 :
949 : /************************************************************************/
950 : /* ZarrV3CodecTranspose::Decode() */
951 : /************************************************************************/
952 :
953 20 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
954 : ZarrByteVectorQuickResize &abyDst) const
955 : {
956 20 : CPLAssert(!IsNoOp());
957 :
958 20 : return Transpose(abySrc, abyDst, false);
959 : }
960 :
961 : /************************************************************************/
962 : /* ZarrV3CodecSequence::Clone() */
963 : /************************************************************************/
964 :
965 16 : std::unique_ptr<ZarrV3CodecSequence> ZarrV3CodecSequence::Clone() const
966 : {
967 16 : auto poClone = std::make_unique<ZarrV3CodecSequence>(m_oInputArrayMetadata);
968 24 : for (const auto &poCodec : m_apoCodecs)
969 8 : poClone->m_apoCodecs.emplace_back(poCodec->Clone());
970 16 : poClone->m_oCodecArray = m_oCodecArray.Clone();
971 16 : return poClone;
972 : }
973 :
974 : /************************************************************************/
975 : /* ZarrV3CodecSequence::InitFromJson() */
976 : /************************************************************************/
977 :
978 250 : bool ZarrV3CodecSequence::InitFromJson(const CPLJSONObject &oCodecs)
979 : {
980 250 : if (oCodecs.GetType() != CPLJSONObject::Type::Array)
981 : {
982 0 : CPLError(CE_Failure, CPLE_AppDefined, "codecs is not an array");
983 0 : return false;
984 : }
985 500 : auto oCodecsArray = oCodecs.ToArray();
986 :
987 500 : ZarrArrayMetadata oInputArrayMetadata = m_oInputArrayMetadata;
988 250 : ZarrV3Codec::IOType eLastType = ZarrV3Codec::IOType::ARRAY;
989 500 : std::string osLastCodec;
990 :
991 : const auto InsertImplicitEndianCodecIfNeeded =
992 304 : [
993 : #if !CPL_IS_LSB
994 : this,
995 : #endif
996 304 : &oInputArrayMetadata, &eLastType, &osLastCodec]()
997 : {
998 304 : if (eLastType == ZarrV3Codec::IOType::ARRAY &&
999 0 : oInputArrayMetadata.oElt.nativeSize > 1)
1000 : {
1001 0 : CPLError(CE_Warning, CPLE_AppDefined,
1002 : "'bytes' codec missing. Assuming little-endian storage, "
1003 : "but such tolerance may be removed in future versions");
1004 0 : auto poEndianCodec = std::make_unique<ZarrV3CodecBytes>();
1005 0 : ZarrArrayMetadata oOutputArrayMetadata;
1006 0 : poEndianCodec->InitFromConfiguration(
1007 0 : ZarrV3CodecBytes::GetConfiguration(true), oInputArrayMetadata,
1008 : oOutputArrayMetadata);
1009 0 : oInputArrayMetadata = std::move(oOutputArrayMetadata);
1010 0 : eLastType = poEndianCodec->GetOutputType();
1011 0 : osLastCodec = poEndianCodec->GetName();
1012 : #if !CPL_IS_LSB
1013 : // Insert a little endian codec if we are on a big endian target
1014 : m_apoCodecs.emplace_back(std::move(poEndianCodec));
1015 : #endif
1016 : }
1017 304 : };
1018 :
1019 634 : for (const auto &oCodec : oCodecsArray)
1020 : {
1021 384 : if (oCodec.GetType() != CPLJSONObject::Type::Object)
1022 : {
1023 0 : CPLError(CE_Failure, CPLE_AppDefined, "codecs[] is not an array");
1024 0 : return false;
1025 : }
1026 768 : const auto osName = oCodec["name"].ToString();
1027 0 : std::unique_ptr<ZarrV3Codec> poCodec;
1028 384 : if (osName == "gzip")
1029 46 : poCodec = std::make_unique<ZarrV3CodecGZip>();
1030 338 : else if (osName == "blosc")
1031 4 : poCodec = std::make_unique<ZarrV3CodecBlosc>();
1032 334 : else if (osName == "zstd")
1033 4 : poCodec = std::make_unique<ZarrV3CodecZstd>();
1034 410 : else if (osName == "bytes" ||
1035 80 : osName == "endian" /* endian is the old name */)
1036 250 : poCodec = std::make_unique<ZarrV3CodecBytes>();
1037 80 : else if (osName == "transpose")
1038 80 : poCodec = std::make_unique<ZarrV3CodecTranspose>();
1039 : else
1040 : {
1041 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported codec: %s",
1042 : osName.c_str());
1043 0 : return false;
1044 : }
1045 :
1046 384 : if (poCodec->GetInputType() == ZarrV3Codec::IOType::ARRAY)
1047 : {
1048 330 : if (eLastType == ZarrV3Codec::IOType::BYTES)
1049 : {
1050 0 : CPLError(CE_Failure, CPLE_AppDefined,
1051 : "Cannot chain codec %s with %s",
1052 0 : poCodec->GetName().c_str(), osLastCodec.c_str());
1053 0 : return false;
1054 : }
1055 : }
1056 : else
1057 : {
1058 54 : InsertImplicitEndianCodecIfNeeded();
1059 : }
1060 :
1061 384 : ZarrArrayMetadata oOutputArrayMetadata;
1062 768 : if (!poCodec->InitFromConfiguration(oCodec["configuration"],
1063 : oInputArrayMetadata,
1064 384 : oOutputArrayMetadata))
1065 : {
1066 0 : return false;
1067 : }
1068 384 : oInputArrayMetadata = std::move(oOutputArrayMetadata);
1069 384 : eLastType = poCodec->GetOutputType();
1070 384 : osLastCodec = poCodec->GetName();
1071 :
1072 384 : if (!poCodec->IsNoOp())
1073 122 : m_apoCodecs.emplace_back(std::move(poCodec));
1074 : }
1075 :
1076 250 : InsertImplicitEndianCodecIfNeeded();
1077 :
1078 250 : m_oCodecArray = oCodecs.Clone();
1079 250 : return true;
1080 : }
1081 :
1082 : /************************************************************************/
1083 : /* ZarrV3CodecBytes::AllocateBuffer() */
1084 : /************************************************************************/
1085 :
1086 21416 : bool ZarrV3CodecSequence::AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer)
1087 : {
1088 21416 : if (!m_apoCodecs.empty())
1089 : {
1090 10595 : const size_t nRawSize = m_oInputArrayMetadata.GetEltCount() *
1091 10668 : m_oInputArrayMetadata.oElt.nativeSize;
1092 : // Grow the temporary buffer a bit beyond the uncompressed size
1093 10668 : const size_t nMaxSize = nRawSize + nRawSize / 3 + 64;
1094 : try
1095 : {
1096 10668 : m_abyTmp.resize(nMaxSize);
1097 : }
1098 0 : catch (const std::exception &e)
1099 : {
1100 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1101 0 : return false;
1102 : }
1103 10581 : m_abyTmp.resize(nRawSize);
1104 :
1105 : // Grow the input/output buffer too if we have several steps
1106 10580 : if (m_apoCodecs.size() >= 2 && abyBuffer.capacity() < nMaxSize)
1107 : {
1108 36 : const size_t nSize = abyBuffer.size();
1109 : try
1110 : {
1111 36 : abyBuffer.resize(nMaxSize);
1112 : }
1113 0 : catch (const std::exception &e)
1114 : {
1115 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1116 0 : return false;
1117 : }
1118 36 : abyBuffer.resize(nSize);
1119 : }
1120 : }
1121 21161 : return true;
1122 : }
1123 :
1124 : /************************************************************************/
1125 : /* ZarrV3CodecSequence::Encode() */
1126 : /************************************************************************/
1127 :
1128 10744 : bool ZarrV3CodecSequence::Encode(ZarrByteVectorQuickResize &abyBuffer)
1129 : {
1130 10744 : if (!AllocateBuffer(abyBuffer))
1131 0 : return false;
1132 16155 : for (const auto &poCodec : m_apoCodecs)
1133 : {
1134 5411 : if (!poCodec->Encode(abyBuffer, m_abyTmp))
1135 0 : return false;
1136 5411 : std::swap(abyBuffer, m_abyTmp);
1137 : }
1138 10744 : return true;
1139 : }
1140 :
1141 : /************************************************************************/
1142 : /* ZarrV3CodecSequence::Decode() */
1143 : /************************************************************************/
1144 :
1145 10689 : bool ZarrV3CodecSequence::Decode(ZarrByteVectorQuickResize &abyBuffer)
1146 : {
1147 10689 : if (!AllocateBuffer(abyBuffer))
1148 0 : return false;
1149 15812 : for (auto iter = m_apoCodecs.rbegin(); iter != m_apoCodecs.rend(); ++iter)
1150 : {
1151 5148 : const auto &poCodec = *iter;
1152 5141 : if (!poCodec->Decode(abyBuffer, m_abyTmp))
1153 0 : return false;
1154 5335 : std::swap(abyBuffer, m_abyTmp);
1155 : }
1156 10417 : return true;
1157 : }
|