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