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) 2021, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #ifndef ZARR_V3_CODEC_H
14 : #define ZARR_V3_CODEC_H
15 :
16 : #include "zarr.h"
17 :
18 : struct VSIVirtualHandle;
19 :
20 : /************************************************************************/
21 : /* ZarrArrayMetadata */
22 : /************************************************************************/
23 :
24 : /** Array-related metadata needed for the good working of Zarr V3 codecs */
25 : struct ZarrArrayMetadata
26 : {
27 : /** Data type of the array */
28 : DtypeElt oElt{};
29 :
30 : /** Shape of a block/chunk */
31 : std::vector<size_t> anBlockSizes{};
32 :
33 : /** No data value of the array. Empty or abyNoData.size() == oElt.nNativeSize */
34 : std::vector<GByte> abyNoData{};
35 : };
36 :
37 : /************************************************************************/
38 : /* ZarrV3Codec */
39 : /************************************************************************/
40 :
41 : /** Abstract class for a Zarr V3 codec */
42 4392 : class ZarrV3Codec CPL_NON_FINAL
43 : {
44 : protected:
45 : const std::string m_osName;
46 : CPLJSONObject m_oConfiguration{};
47 : ZarrArrayMetadata m_oInputArrayMetadata{};
48 :
49 : ZarrV3Codec(const std::string &osName);
50 :
51 : public:
52 : virtual ~ZarrV3Codec();
53 :
54 : enum class IOType
55 : {
56 : BYTES,
57 : ARRAY
58 : };
59 :
60 : virtual IOType GetInputType() const = 0;
61 : virtual IOType GetOutputType() const = 0;
62 :
63 : virtual bool InitFromConfiguration(
64 : const std::string &osArrayName, const CPLJSONObject &configuration,
65 : const ZarrArrayMetadata &oInputArrayMetadata,
66 : ZarrArrayMetadata &oOutputArrayMetadata, bool bEmitWarnings) = 0;
67 :
68 : virtual std::unique_ptr<ZarrV3Codec> Clone() const = 0;
69 :
70 2157 : virtual bool IsNoOp() const
71 : {
72 2157 : return false;
73 : }
74 :
75 : virtual bool Encode(const ZarrByteVectorQuickResize &abySrc,
76 : ZarrByteVectorQuickResize &abyDst) const = 0;
77 : virtual bool Decode(const ZarrByteVectorQuickResize &abySrc,
78 : ZarrByteVectorQuickResize &abyDst) const = 0;
79 :
80 : /** Partial decoding.
81 : * anStartIdx[i]: coordinate in pixel, within the array of an outer chunk,
82 : * that is < m_oInputArrayMetadata.anBlockSizes[i]
83 : * anCount[i]: number of pixels to extract <= m_oInputArrayMetadata.anBlockSizes[i]
84 : */
85 : virtual bool DecodePartial(VSIVirtualHandle *poFile,
86 : const ZarrByteVectorQuickResize &abySrc,
87 : ZarrByteVectorQuickResize &abyDst,
88 : std::vector<size_t> &anStartIdx,
89 : std::vector<size_t> &anCount);
90 :
91 12234 : const std::string &GetName() const
92 : {
93 12234 : return m_osName;
94 : }
95 :
96 : const CPLJSONObject &GetConfiguration() const
97 : {
98 : return m_oConfiguration;
99 : }
100 :
101 : virtual std::vector<size_t>
102 281 : GetInnerMostBlockSize(const std::vector<size_t> &input) const
103 : {
104 281 : return input;
105 : }
106 :
107 1028 : virtual void ChangeArrayShapeForward(std::vector<size_t> &anStartIdx,
108 : std::vector<size_t> &anCount)
109 : {
110 : (void)anStartIdx;
111 : (void)anCount;
112 1028 : }
113 : };
114 :
115 : /************************************************************************/
116 : /* ZarrV3CodecAbstractCompressor */
117 : /************************************************************************/
118 :
119 : class ZarrV3CodecAbstractCompressor CPL_NON_FINAL : public ZarrV3Codec
120 : {
121 : protected:
122 : CPLStringList m_aosCompressorOptions{};
123 : const CPLCompressor *m_pDecompressor = nullptr;
124 : const CPLCompressor *m_pCompressor = nullptr;
125 :
126 : explicit ZarrV3CodecAbstractCompressor(const std::string &osName);
127 :
128 : ZarrV3CodecAbstractCompressor(const ZarrV3CodecAbstractCompressor &) =
129 : delete;
130 : ZarrV3CodecAbstractCompressor &
131 : operator=(const ZarrV3CodecAbstractCompressor &) = delete;
132 :
133 : public:
134 620 : IOType GetInputType() const override
135 : {
136 620 : return IOType::BYTES;
137 : }
138 :
139 620 : IOType GetOutputType() const override
140 : {
141 620 : return IOType::BYTES;
142 : }
143 :
144 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
145 : ZarrByteVectorQuickResize &abyDst) const override;
146 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
147 : ZarrByteVectorQuickResize &abyDst) const override;
148 : };
149 :
150 : /************************************************************************/
151 : /* ZarrV3CodecGZip */
152 : /************************************************************************/
153 :
154 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/gzip/v1.0.html
155 : class ZarrV3CodecGZip final : public ZarrV3CodecAbstractCompressor
156 : {
157 : public:
158 : static constexpr const char *NAME = "gzip";
159 :
160 : ZarrV3CodecGZip();
161 :
162 : static CPLJSONObject GetConfiguration(int nLevel);
163 :
164 : bool InitFromConfiguration(const std::string &osArrayName,
165 : const CPLJSONObject &configuration,
166 : const ZarrArrayMetadata &oInputArrayMetadata,
167 : ZarrArrayMetadata &oOutputArrayMetadata,
168 : bool bEmitWarnings) override;
169 :
170 : std::unique_ptr<ZarrV3Codec> Clone() const override;
171 : };
172 :
173 : /************************************************************************/
174 : /* ZarrV3CodecBlosc */
175 : /************************************************************************/
176 :
177 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/blosc/v1.0.html
178 : class ZarrV3CodecBlosc final : public ZarrV3CodecAbstractCompressor
179 : {
180 : public:
181 : static constexpr const char *NAME = "blosc";
182 :
183 : ZarrV3CodecBlosc();
184 :
185 : static CPLJSONObject GetConfiguration(const char *cname, int clevel,
186 : const char *shuffle, int typesize,
187 : int blocksize);
188 :
189 : bool InitFromConfiguration(const std::string &osArrayName,
190 : const CPLJSONObject &configuration,
191 : const ZarrArrayMetadata &oInputArrayMetadata,
192 : ZarrArrayMetadata &oOutputArrayMetadata,
193 : bool bEmitWarnings) override;
194 :
195 : std::unique_ptr<ZarrV3Codec> Clone() const override;
196 : };
197 :
198 : /************************************************************************/
199 : /* ZarrV3CodecZstd */
200 : /************************************************************************/
201 :
202 : // Implements https://github.com/zarr-developers/zarr-specs/pull/256
203 : class ZarrV3CodecZstd final : public ZarrV3CodecAbstractCompressor
204 : {
205 : public:
206 : static constexpr const char *NAME = "zstd";
207 :
208 : ZarrV3CodecZstd();
209 :
210 : static CPLJSONObject GetConfiguration(int level, bool checksum);
211 :
212 : bool InitFromConfiguration(const std::string &osArrayName,
213 : const CPLJSONObject &configuration,
214 : const ZarrArrayMetadata &oInputArrayMetadata,
215 : ZarrArrayMetadata &oOutputArrayMetadata,
216 : bool bEmitWarnings) override;
217 :
218 : std::unique_ptr<ZarrV3Codec> Clone() const override;
219 : };
220 :
221 : /************************************************************************/
222 : /* ZarrV3CodecBytes */
223 : /************************************************************************/
224 :
225 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/bytes/v1.0.html
226 : class ZarrV3CodecBytes final : public ZarrV3Codec
227 : {
228 : bool m_bLittle = true;
229 :
230 : public:
231 : static constexpr const char *NAME = "bytes";
232 :
233 : ZarrV3CodecBytes();
234 :
235 2065 : IOType GetInputType() const override
236 : {
237 2065 : return IOType::ARRAY;
238 : }
239 :
240 2066 : IOType GetOutputType() const override
241 : {
242 2066 : return IOType::BYTES;
243 : }
244 :
245 : static CPLJSONObject GetConfiguration(bool bLittle);
246 :
247 : bool InitFromConfiguration(const std::string &osArrayName,
248 : const CPLJSONObject &configuration,
249 : const ZarrArrayMetadata &oInputArrayMetadata,
250 : ZarrArrayMetadata &oOutputArrayMetadata,
251 : bool bEmitWarnings) override;
252 :
253 1 : bool IsLittle() const
254 : {
255 1 : return m_bLittle;
256 : }
257 :
258 2119 : bool IsNoOp() const override
259 : {
260 : // Byte-oriented string types have no endianness concept
261 2119 : if (m_oInputArrayMetadata.oElt.nativeType ==
262 : DtypeElt::NativeType::STRING_ASCII)
263 2 : return true;
264 : if constexpr (CPL_IS_LSB)
265 2117 : return m_oInputArrayMetadata.oElt.nativeSize == 1 || m_bLittle;
266 : else
267 : return m_oInputArrayMetadata.oElt.nativeSize == 1 || !m_bLittle;
268 : }
269 :
270 : std::unique_ptr<ZarrV3Codec> Clone() const override;
271 :
272 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
273 : ZarrByteVectorQuickResize &abyDst) const override;
274 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
275 : ZarrByteVectorQuickResize &abyDst) const override;
276 : };
277 :
278 : /************************************************************************/
279 : /* ZarrV3CodecVLenUTF8 */
280 : /************************************************************************/
281 :
282 : /** Implements the vlen-utf8 array-to-bytes codec for variable-length
283 : * UTF-8 strings (zarr-extensions).
284 : *
285 : * Binary format (little-endian):
286 : * [u32 item_count] [u32 len_0][bytes_0] [u32 len_1][bytes_1] ...
287 : *
288 : * Decode produces a flat buffer of nElements * nativeSize bytes where
289 : * each slot is a null-padded string. Read-only for now.
290 : */
291 : class ZarrV3CodecVLenUTF8 final : public ZarrV3Codec
292 : {
293 : public:
294 : static constexpr const char *NAME = "vlen-utf8";
295 :
296 : ZarrV3CodecVLenUTF8();
297 :
298 14 : IOType GetInputType() const override
299 : {
300 14 : return IOType::ARRAY;
301 : }
302 :
303 14 : IOType GetOutputType() const override
304 : {
305 14 : return IOType::BYTES;
306 : }
307 :
308 : bool InitFromConfiguration(const std::string &osArrayName,
309 : const CPLJSONObject &configuration,
310 : const ZarrArrayMetadata &oInputArrayMetadata,
311 : ZarrArrayMetadata &oOutputArrayMetadata,
312 : bool bEmitWarnings) override;
313 :
314 : std::unique_ptr<ZarrV3Codec> Clone() const override;
315 :
316 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
317 : ZarrByteVectorQuickResize &abyDst) const override;
318 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
319 : ZarrByteVectorQuickResize &abyDst) const override;
320 : };
321 :
322 : /************************************************************************/
323 : /* ZarrV3CodecTranspose */
324 : /************************************************************************/
325 :
326 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/transpose/v1.0.html
327 : class ZarrV3CodecTranspose final : public ZarrV3Codec
328 : {
329 : // m_anOrder is such that dest_shape[i] = source_shape[m_anOrder[i]]
330 : // where source_shape[] is the size of the array before the Encode() operation
331 : // and dest_shape[] its size after.
332 : // m_anOrder[] describes a bijection of [0,N-1] to [0,N-1]
333 : std::vector<int> m_anOrder{};
334 :
335 : // m_anReverseOrder is such that m_anReverseOrder[m_anOrder[i]] = i
336 : std::vector<int> m_anReverseOrder{};
337 :
338 : bool Transpose(const ZarrByteVectorQuickResize &abySrc,
339 : ZarrByteVectorQuickResize &abyDst, bool bEncodeDirection,
340 : const std::vector<size_t> &anForwardBlockSizes) const;
341 :
342 : public:
343 : static constexpr const char *NAME = "transpose";
344 :
345 : ZarrV3CodecTranspose();
346 :
347 83 : IOType GetInputType() const override
348 : {
349 83 : return IOType::ARRAY;
350 : }
351 :
352 83 : IOType GetOutputType() const override
353 : {
354 83 : return IOType::ARRAY;
355 : }
356 :
357 : static CPLJSONObject GetConfiguration(const std::vector<int> &anOrder);
358 :
359 : bool InitFromConfiguration(const std::string &osArrayName,
360 : const CPLJSONObject &configuration,
361 : const ZarrArrayMetadata &oInputArrayMetadata,
362 : ZarrArrayMetadata &oOutputArrayMetadata,
363 : bool bEmitWarnings) override;
364 :
365 1 : const std::vector<int> &GetOrder() const
366 : {
367 1 : return m_anOrder;
368 : }
369 :
370 : bool IsNoOp() const override;
371 :
372 : std::unique_ptr<ZarrV3Codec> Clone() const override;
373 :
374 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
375 : ZarrByteVectorQuickResize &abyDst) const override;
376 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
377 : ZarrByteVectorQuickResize &abyDst) const override;
378 :
379 : bool DecodePartial(VSIVirtualHandle *poFile,
380 : const ZarrByteVectorQuickResize &abySrc,
381 : ZarrByteVectorQuickResize &abyDst,
382 : std::vector<size_t> &anStartIdx,
383 : std::vector<size_t> &anCount) override;
384 :
385 : std::vector<size_t>
386 : GetInnerMostBlockSize(const std::vector<size_t> &input) const override;
387 :
388 : template <class T>
389 50 : inline void Reorder1DForward(std::vector<T> &vector) const
390 : {
391 100 : std::vector<T> res;
392 150 : for (int idx : m_anOrder)
393 100 : res.push_back(vector[idx]);
394 50 : vector = std::move(res);
395 50 : }
396 :
397 : template <class T>
398 50 : inline void Reorder1DInverse(std::vector<T> &vector) const
399 : {
400 100 : std::vector<T> res;
401 150 : for (int idx : m_anReverseOrder)
402 100 : res.push_back(vector[idx]);
403 50 : vector = std::move(res);
404 50 : }
405 :
406 25 : void ChangeArrayShapeForward(std::vector<size_t> &anStartIdx,
407 : std::vector<size_t> &anCount) override
408 : {
409 25 : Reorder1DForward(anStartIdx);
410 25 : Reorder1DForward(anCount);
411 25 : }
412 : };
413 :
414 : /************************************************************************/
415 : /* ZarrV3CodecCRC32C */
416 : /************************************************************************/
417 :
418 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/crc32c/index.html
419 : class ZarrV3CodecCRC32C final : public ZarrV3Codec
420 : {
421 : bool m_bCheckCRC = true;
422 :
423 : public:
424 : static constexpr const char *NAME = "crc32c";
425 :
426 : ZarrV3CodecCRC32C();
427 :
428 831 : IOType GetInputType() const override
429 : {
430 831 : return IOType::BYTES;
431 : }
432 :
433 831 : IOType GetOutputType() const override
434 : {
435 831 : return IOType::BYTES;
436 : }
437 :
438 : bool InitFromConfiguration(const std::string &osArrayName,
439 : const CPLJSONObject &configuration,
440 : const ZarrArrayMetadata &oInputArrayMetadata,
441 : ZarrArrayMetadata &oOutputArrayMetadata,
442 : bool bEmitWarnings) override;
443 :
444 : std::unique_ptr<ZarrV3Codec> Clone() const override;
445 :
446 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
447 : ZarrByteVectorQuickResize &abyDst) const override;
448 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
449 : ZarrByteVectorQuickResize &abyDst) const override;
450 : };
451 :
452 : /************************************************************************/
453 : /* ZarrV3CodecShardingIndexed */
454 : /************************************************************************/
455 :
456 : class ZarrV3CodecSequence;
457 :
458 : // https://zarr-specs.readthedocs.io/en/latest/v3/codecs/sharding-indexed/index.html
459 : class ZarrV3CodecShardingIndexed final : public ZarrV3Codec
460 : {
461 : std::unique_ptr<ZarrV3CodecSequence> m_poCodecSequence{};
462 : std::unique_ptr<ZarrV3CodecSequence> m_poIndexCodecSequence{};
463 : bool m_bIndexLocationAtEnd = true;
464 : bool m_bIndexHasCRC32 = false;
465 : std::vector<size_t> m_anInnerBlockSize{};
466 :
467 : struct Location
468 : {
469 : uint64_t nOffset;
470 : uint64_t nSize;
471 : };
472 :
473 : public:
474 : static constexpr const char *NAME = "sharding_indexed";
475 :
476 : ZarrV3CodecShardingIndexed();
477 :
478 710 : IOType GetInputType() const override
479 : {
480 710 : return IOType::ARRAY;
481 : }
482 :
483 692 : IOType GetOutputType() const override
484 : {
485 692 : return IOType::BYTES;
486 : }
487 :
488 : bool InitFromConfiguration(const std::string &osArrayName,
489 : const CPLJSONObject &configuration,
490 : const ZarrArrayMetadata &oInputArrayMetadata,
491 : ZarrArrayMetadata &oOutputArrayMetadata,
492 : bool bEmitWarnings) override;
493 :
494 : std::unique_ptr<ZarrV3Codec> Clone() const override;
495 :
496 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
497 : ZarrByteVectorQuickResize &abyDst) const override;
498 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
499 : ZarrByteVectorQuickResize &abyDst) const override;
500 :
501 : bool DecodePartial(VSIVirtualHandle *poFile,
502 : const ZarrByteVectorQuickResize &abySrc,
503 : ZarrByteVectorQuickResize &abyDst,
504 : std::vector<size_t> &anStartIdx,
505 : std::vector<size_t> &anCount) override;
506 :
507 : /** Batch-read multiple inner chunks from the same shard via two
508 : * ReadMultiRange() passes (index entries, then data), then decode.
509 : * pszFilename is used as a cache key for the shard index; pass nullptr
510 : * to bypass the cache.
511 : */
512 : bool BatchDecodePartial(
513 : VSIVirtualHandle *poFile, const char *pszFilename,
514 : const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>
515 : &anRequests,
516 : std::vector<ZarrByteVectorQuickResize> &aResults);
517 :
518 : std::vector<size_t>
519 : GetInnerMostBlockSize(const std::vector<size_t> &input) const override;
520 : };
521 :
522 : /************************************************************************/
523 : /* ZarrV3CodecSequence */
524 : /************************************************************************/
525 :
526 : class ZarrV3CodecSequence
527 : {
528 : const ZarrArrayMetadata m_oInputArrayMetadata;
529 : std::vector<std::unique_ptr<ZarrV3Codec>> m_apoCodecs{};
530 : CPLJSONObject m_oCodecArray{};
531 : ZarrByteVectorQuickResize m_abyTmp{};
532 : bool m_bPartialDecodingPossible = false;
533 :
534 : bool AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer, size_t nEltCount);
535 :
536 : public:
537 2880 : explicit ZarrV3CodecSequence(const ZarrArrayMetadata &oInputArrayMetadata)
538 2880 : : m_oInputArrayMetadata(oInputArrayMetadata)
539 : {
540 2880 : }
541 :
542 : // This method is not thread safe due to cloning a JSON object
543 : std::unique_ptr<ZarrV3CodecSequence> Clone() const;
544 :
545 : bool InitFromJson(const std::string &osArrayName,
546 : const CPLJSONObject &oCodecs,
547 : ZarrArrayMetadata &oOutputArrayMetadata);
548 :
549 230 : const CPLJSONObject &GetJSon() const
550 : {
551 230 : return m_oCodecArray;
552 : }
553 :
554 10805 : const std::vector<std::unique_ptr<ZarrV3Codec>> &GetCodecs() const
555 : {
556 10805 : return m_apoCodecs;
557 : }
558 :
559 62353 : bool SupportsPartialDecoding() const
560 : {
561 62353 : return m_bPartialDecodingPossible;
562 : }
563 :
564 : bool Encode(ZarrByteVectorQuickResize &abyBuffer);
565 : bool Decode(ZarrByteVectorQuickResize &abyBuffer);
566 :
567 : /** Partial decoding.
568 : * anStartIdx[i]: coordinate in pixel, within the array of an outer chunk,
569 : * that is < m_oInputArrayMetadata.anBlockSizes[i]
570 : * anCount[i]: number of pixels to extract <= m_oInputArrayMetadata.anBlockSizes[i]
571 : */
572 : bool DecodePartial(VSIVirtualHandle *poFile,
573 : ZarrByteVectorQuickResize &abyBuffer,
574 : const std::vector<size_t> &anStartIdx,
575 : const std::vector<size_t> &anCount);
576 :
577 : /** Batch-read multiple inner chunks via ReadMultiRange().
578 : * Delegates to the sharding codec if present, otherwise falls back
579 : * to sequential DecodePartial() calls.
580 : * pszFilename is forwarded to the sharding codec for index caching.
581 : */
582 : bool BatchDecodePartial(
583 : VSIVirtualHandle *poFile, const char *pszFilename,
584 : const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>
585 : &anRequests,
586 : std::vector<ZarrByteVectorQuickResize> &aResults);
587 :
588 : std::vector<size_t>
589 : GetInnerMostBlockSize(const std::vector<size_t> &anOuterBlockSize) const;
590 : };
591 :
592 : /************************************************************************/
593 : /* ZarrV3CodecPcodec */
594 : /************************************************************************/
595 :
596 : /** Non-standard "pcodec" (a.k.a. "pco") from https://github.com/pcodec/pcodec
597 : *
598 : * Also see https://numcodecs.readthedocs.io/en/stable/compression/pcodec.html
599 : */
600 : class ZarrV3CodecPcodec /* final */ : public ZarrV3Codec
601 : {
602 : public:
603 : static constexpr const char *NAME = "numcodecs.pcodec";
604 :
605 : explicit ZarrV3CodecPcodec();
606 : ~ZarrV3CodecPcodec() override;
607 :
608 : IOType GetInputType() const override
609 : {
610 : return IOType::ARRAY;
611 : }
612 :
613 : IOType GetOutputType() const override
614 : {
615 : return IOType::BYTES;
616 : }
617 :
618 : bool InitFromConfiguration(const std::string &osArrayName,
619 : const CPLJSONObject &configuration,
620 : const ZarrArrayMetadata &oInputArrayMetadata,
621 : ZarrArrayMetadata &oOutputArrayMetadata,
622 : bool bEmitWarnings) override;
623 :
624 : std::unique_ptr<ZarrV3Codec> Clone() const override;
625 :
626 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
627 : ZarrByteVectorQuickResize &abyDst) const override;
628 :
629 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
630 : ZarrByteVectorQuickResize &abyDst) const override;
631 : };
632 :
633 : #endif
|