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 4226 : 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
64 : InitFromConfiguration(const CPLJSONObject &configuration,
65 : const ZarrArrayMetadata &oInputArrayMetadata,
66 : ZarrArrayMetadata &oOutputArrayMetadata,
67 : bool bEmitWarnings) = 0;
68 :
69 : virtual std::unique_ptr<ZarrV3Codec> Clone() const = 0;
70 :
71 2083 : virtual bool IsNoOp() const
72 : {
73 2083 : return false;
74 : }
75 :
76 : virtual bool Encode(const ZarrByteVectorQuickResize &abySrc,
77 : ZarrByteVectorQuickResize &abyDst) const = 0;
78 : virtual bool Decode(const ZarrByteVectorQuickResize &abySrc,
79 : ZarrByteVectorQuickResize &abyDst) const = 0;
80 :
81 : /** Partial decoding.
82 : * anStartIdx[i]: coordinate in pixel, within the array of an outer chunk,
83 : * that is < m_oInputArrayMetadata.anBlockSizes[i]
84 : * anCount[i]: number of pixels to extract <= m_oInputArrayMetadata.anBlockSizes[i]
85 : */
86 : virtual bool DecodePartial(VSIVirtualHandle *poFile,
87 : const ZarrByteVectorQuickResize &abySrc,
88 : ZarrByteVectorQuickResize &abyDst,
89 : std::vector<size_t> &anStartIdx,
90 : std::vector<size_t> &anCount);
91 :
92 11971 : const std::string &GetName() const
93 : {
94 11971 : return m_osName;
95 : }
96 :
97 : const CPLJSONObject &GetConfiguration() const
98 : {
99 : return m_oConfiguration;
100 : }
101 :
102 : virtual std::vector<size_t>
103 271 : GetInnerMostBlockSize(const std::vector<size_t> &input) const
104 : {
105 271 : return input;
106 : }
107 :
108 1027 : virtual void ChangeArrayShapeForward(std::vector<size_t> &anStartIdx,
109 : std::vector<size_t> &anCount)
110 : {
111 : (void)anStartIdx;
112 : (void)anCount;
113 1027 : }
114 : };
115 :
116 : /************************************************************************/
117 : /* ZarrV3CodecAbstractCompressor */
118 : /************************************************************************/
119 :
120 : class ZarrV3CodecAbstractCompressor CPL_NON_FINAL : public ZarrV3Codec
121 : {
122 : protected:
123 : CPLStringList m_aosCompressorOptions{};
124 : const CPLCompressor *m_pDecompressor = nullptr;
125 : const CPLCompressor *m_pCompressor = nullptr;
126 :
127 : explicit ZarrV3CodecAbstractCompressor(const std::string &osName);
128 :
129 : ZarrV3CodecAbstractCompressor(const ZarrV3CodecAbstractCompressor &) =
130 : delete;
131 : ZarrV3CodecAbstractCompressor &
132 : operator=(const ZarrV3CodecAbstractCompressor &) = delete;
133 :
134 : public:
135 590 : IOType GetInputType() const override
136 : {
137 590 : return IOType::BYTES;
138 : }
139 :
140 590 : IOType GetOutputType() const override
141 : {
142 590 : return IOType::BYTES;
143 : }
144 :
145 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
146 : ZarrByteVectorQuickResize &abyDst) const override;
147 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
148 : ZarrByteVectorQuickResize &abyDst) const override;
149 : };
150 :
151 : /************************************************************************/
152 : /* ZarrV3CodecGZip */
153 : /************************************************************************/
154 :
155 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/gzip/v1.0.html
156 : class ZarrV3CodecGZip final : public ZarrV3CodecAbstractCompressor
157 : {
158 : public:
159 : static constexpr const char *NAME = "gzip";
160 :
161 : ZarrV3CodecGZip();
162 :
163 : static CPLJSONObject GetConfiguration(int nLevel);
164 :
165 : bool InitFromConfiguration(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 CPLJSONObject &configuration,
190 : const ZarrArrayMetadata &oInputArrayMetadata,
191 : ZarrArrayMetadata &oOutputArrayMetadata,
192 : bool bEmitWarnings) override;
193 :
194 : std::unique_ptr<ZarrV3Codec> Clone() const override;
195 : };
196 :
197 : /************************************************************************/
198 : /* ZarrV3CodecZstd */
199 : /************************************************************************/
200 :
201 : // Implements https://github.com/zarr-developers/zarr-specs/pull/256
202 : class ZarrV3CodecZstd final : public ZarrV3CodecAbstractCompressor
203 : {
204 : public:
205 : static constexpr const char *NAME = "zstd";
206 :
207 : ZarrV3CodecZstd();
208 :
209 : static CPLJSONObject GetConfiguration(int level, bool checksum);
210 :
211 : bool InitFromConfiguration(const CPLJSONObject &configuration,
212 : const ZarrArrayMetadata &oInputArrayMetadata,
213 : ZarrArrayMetadata &oOutputArrayMetadata,
214 : bool bEmitWarnings) override;
215 :
216 : std::unique_ptr<ZarrV3Codec> Clone() const override;
217 : };
218 :
219 : /************************************************************************/
220 : /* ZarrV3CodecBytes */
221 : /************************************************************************/
222 :
223 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/bytes/v1.0.html
224 : class ZarrV3CodecBytes final : public ZarrV3Codec
225 : {
226 : bool m_bLittle = true;
227 :
228 : public:
229 : static constexpr const char *NAME = "bytes";
230 :
231 : ZarrV3CodecBytes();
232 :
233 1973 : IOType GetInputType() const override
234 : {
235 1973 : return IOType::ARRAY;
236 : }
237 :
238 1974 : IOType GetOutputType() const override
239 : {
240 1974 : return IOType::BYTES;
241 : }
242 :
243 : static CPLJSONObject GetConfiguration(bool bLittle);
244 :
245 : bool InitFromConfiguration(const CPLJSONObject &configuration,
246 : const ZarrArrayMetadata &oInputArrayMetadata,
247 : ZarrArrayMetadata &oOutputArrayMetadata,
248 : bool bEmitWarnings) override;
249 :
250 1 : bool IsLittle() const
251 : {
252 1 : return m_bLittle;
253 : }
254 :
255 2027 : bool IsNoOp() const override
256 : {
257 : // Byte-oriented string types have no endianness concept
258 2027 : if (m_oInputArrayMetadata.oElt.nativeType ==
259 : DtypeElt::NativeType::STRING_ASCII)
260 2 : return true;
261 : if constexpr (CPL_IS_LSB)
262 2025 : return m_oInputArrayMetadata.oElt.nativeSize == 1 || m_bLittle;
263 : else
264 : return m_oInputArrayMetadata.oElt.nativeSize == 1 || !m_bLittle;
265 : }
266 :
267 : std::unique_ptr<ZarrV3Codec> Clone() const override;
268 :
269 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
270 : ZarrByteVectorQuickResize &abyDst) const override;
271 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
272 : ZarrByteVectorQuickResize &abyDst) const override;
273 : };
274 :
275 : /************************************************************************/
276 : /* ZarrV3CodecVLenUTF8 */
277 : /************************************************************************/
278 :
279 : /** Implements the vlen-utf8 array-to-bytes codec for variable-length
280 : * UTF-8 strings (zarr-extensions).
281 : *
282 : * Binary format (little-endian):
283 : * [u32 item_count] [u32 len_0][bytes_0] [u32 len_1][bytes_1] ...
284 : *
285 : * Decode produces a flat buffer of nElements * nativeSize bytes where
286 : * each slot is a null-padded string. Read-only for now.
287 : */
288 : class ZarrV3CodecVLenUTF8 final : public ZarrV3Codec
289 : {
290 : public:
291 : static constexpr const char *NAME = "vlen-utf8";
292 :
293 : ZarrV3CodecVLenUTF8();
294 :
295 14 : IOType GetInputType() const override
296 : {
297 14 : return IOType::ARRAY;
298 : }
299 :
300 14 : IOType GetOutputType() const override
301 : {
302 14 : return IOType::BYTES;
303 : }
304 :
305 : bool InitFromConfiguration(const CPLJSONObject &configuration,
306 : const ZarrArrayMetadata &oInputArrayMetadata,
307 : ZarrArrayMetadata &oOutputArrayMetadata,
308 : bool bEmitWarnings) override;
309 :
310 : std::unique_ptr<ZarrV3Codec> Clone() const override;
311 :
312 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
313 : ZarrByteVectorQuickResize &abyDst) const override;
314 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
315 : ZarrByteVectorQuickResize &abyDst) const override;
316 : };
317 :
318 : /************************************************************************/
319 : /* ZarrV3CodecTranspose */
320 : /************************************************************************/
321 :
322 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/transpose/v1.0.html
323 : class ZarrV3CodecTranspose final : public ZarrV3Codec
324 : {
325 : // m_anOrder is such that dest_shape[i] = source_shape[m_anOrder[i]]
326 : // where source_shape[] is the size of the array before the Encode() operation
327 : // and dest_shape[] its size after.
328 : // m_anOrder[] describes a bijection of [0,N-1] to [0,N-1]
329 : std::vector<int> m_anOrder{};
330 :
331 : // m_anReverseOrder is such that m_anReverseOrder[m_anOrder[i]] = i
332 : std::vector<int> m_anReverseOrder{};
333 :
334 : bool Transpose(const ZarrByteVectorQuickResize &abySrc,
335 : ZarrByteVectorQuickResize &abyDst, bool bEncodeDirection,
336 : const std::vector<size_t> &anForwardBlockSizes) const;
337 :
338 : public:
339 : static constexpr const char *NAME = "transpose";
340 :
341 : ZarrV3CodecTranspose();
342 :
343 83 : IOType GetInputType() const override
344 : {
345 83 : return IOType::ARRAY;
346 : }
347 :
348 83 : IOType GetOutputType() const override
349 : {
350 83 : return IOType::ARRAY;
351 : }
352 :
353 : static CPLJSONObject GetConfiguration(const std::vector<int> &anOrder);
354 :
355 : bool InitFromConfiguration(const CPLJSONObject &configuration,
356 : const ZarrArrayMetadata &oInputArrayMetadata,
357 : ZarrArrayMetadata &oOutputArrayMetadata,
358 : bool bEmitWarnings) override;
359 :
360 1 : const std::vector<int> &GetOrder() const
361 : {
362 1 : return m_anOrder;
363 : }
364 :
365 : bool IsNoOp() const override;
366 :
367 : std::unique_ptr<ZarrV3Codec> Clone() const override;
368 :
369 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
370 : ZarrByteVectorQuickResize &abyDst) const override;
371 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
372 : ZarrByteVectorQuickResize &abyDst) const override;
373 :
374 : bool DecodePartial(VSIVirtualHandle *poFile,
375 : const ZarrByteVectorQuickResize &abySrc,
376 : ZarrByteVectorQuickResize &abyDst,
377 : std::vector<size_t> &anStartIdx,
378 : std::vector<size_t> &anCount) override;
379 :
380 : std::vector<size_t>
381 : GetInnerMostBlockSize(const std::vector<size_t> &input) const override;
382 :
383 : template <class T>
384 50 : inline void Reorder1DForward(std::vector<T> &vector) const
385 : {
386 100 : std::vector<T> res;
387 150 : for (int idx : m_anOrder)
388 100 : res.push_back(vector[idx]);
389 50 : vector = std::move(res);
390 50 : }
391 :
392 : template <class T>
393 50 : inline void Reorder1DInverse(std::vector<T> &vector) const
394 : {
395 100 : std::vector<T> res;
396 150 : for (int idx : m_anReverseOrder)
397 100 : res.push_back(vector[idx]);
398 50 : vector = std::move(res);
399 50 : }
400 :
401 25 : void ChangeArrayShapeForward(std::vector<size_t> &anStartIdx,
402 : std::vector<size_t> &anCount) override
403 : {
404 25 : Reorder1DForward(anStartIdx);
405 25 : Reorder1DForward(anCount);
406 25 : }
407 : };
408 :
409 : /************************************************************************/
410 : /* ZarrV3CodecCRC32C */
411 : /************************************************************************/
412 :
413 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/crc32c/index.html
414 : class ZarrV3CodecCRC32C final : public ZarrV3Codec
415 : {
416 : bool m_bCheckCRC = true;
417 :
418 : public:
419 : static constexpr const char *NAME = "crc32c";
420 :
421 : ZarrV3CodecCRC32C();
422 :
423 809 : IOType GetInputType() const override
424 : {
425 809 : return IOType::BYTES;
426 : }
427 :
428 809 : IOType GetOutputType() const override
429 : {
430 809 : return IOType::BYTES;
431 : }
432 :
433 : bool InitFromConfiguration(const CPLJSONObject &configuration,
434 : const ZarrArrayMetadata &oInputArrayMetadata,
435 : ZarrArrayMetadata &oOutputArrayMetadata,
436 : bool bEmitWarnings) override;
437 :
438 : std::unique_ptr<ZarrV3Codec> Clone() const override;
439 :
440 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
441 : ZarrByteVectorQuickResize &abyDst) const override;
442 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
443 : ZarrByteVectorQuickResize &abyDst) const override;
444 : };
445 :
446 : /************************************************************************/
447 : /* ZarrV3CodecShardingIndexed */
448 : /************************************************************************/
449 :
450 : class ZarrV3CodecSequence;
451 :
452 : // https://zarr-specs.readthedocs.io/en/latest/v3/codecs/sharding-indexed/index.html
453 : class ZarrV3CodecShardingIndexed final : public ZarrV3Codec
454 : {
455 : std::unique_ptr<ZarrV3CodecSequence> m_poCodecSequence{};
456 : std::unique_ptr<ZarrV3CodecSequence> m_poIndexCodecSequence{};
457 : bool m_bIndexLocationAtEnd = true;
458 : bool m_bIndexHasCRC32 = false;
459 : std::vector<size_t> m_anInnerBlockSize{};
460 :
461 : struct Location
462 : {
463 : uint64_t nOffset;
464 : uint64_t nSize;
465 : };
466 :
467 : public:
468 : static constexpr const char *NAME = "sharding_indexed";
469 :
470 : ZarrV3CodecShardingIndexed();
471 :
472 688 : IOType GetInputType() const override
473 : {
474 688 : return IOType::ARRAY;
475 : }
476 :
477 670 : IOType GetOutputType() const override
478 : {
479 670 : return IOType::BYTES;
480 : }
481 :
482 : bool InitFromConfiguration(const CPLJSONObject &configuration,
483 : const ZarrArrayMetadata &oInputArrayMetadata,
484 : ZarrArrayMetadata &oOutputArrayMetadata,
485 : bool bEmitWarnings) override;
486 :
487 : std::unique_ptr<ZarrV3Codec> Clone() const override;
488 :
489 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
490 : ZarrByteVectorQuickResize &abyDst) const override;
491 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
492 : ZarrByteVectorQuickResize &abyDst) const override;
493 :
494 : bool DecodePartial(VSIVirtualHandle *poFile,
495 : const ZarrByteVectorQuickResize &abySrc,
496 : ZarrByteVectorQuickResize &abyDst,
497 : std::vector<size_t> &anStartIdx,
498 : std::vector<size_t> &anCount) override;
499 :
500 : /** Batch-read multiple inner chunks from the same shard via two
501 : * ReadMultiRange() passes (index entries, then data), then decode.
502 : * pszFilename is used as a cache key for the shard index; pass nullptr
503 : * to bypass the cache.
504 : */
505 : bool BatchDecodePartial(
506 : VSIVirtualHandle *poFile, const char *pszFilename,
507 : const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>
508 : &anRequests,
509 : std::vector<ZarrByteVectorQuickResize> &aResults);
510 :
511 : std::vector<size_t>
512 : GetInnerMostBlockSize(const std::vector<size_t> &input) const override;
513 : };
514 :
515 : /************************************************************************/
516 : /* ZarrV3CodecSequence */
517 : /************************************************************************/
518 :
519 : class ZarrV3CodecSequence
520 : {
521 : const ZarrArrayMetadata m_oInputArrayMetadata;
522 : std::vector<std::unique_ptr<ZarrV3Codec>> m_apoCodecs{};
523 : CPLJSONObject m_oCodecArray{};
524 : ZarrByteVectorQuickResize m_abyTmp{};
525 : bool m_bPartialDecodingPossible = false;
526 :
527 : bool AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer, size_t nEltCount);
528 :
529 : public:
530 2774 : explicit ZarrV3CodecSequence(const ZarrArrayMetadata &oInputArrayMetadata)
531 2774 : : m_oInputArrayMetadata(oInputArrayMetadata)
532 : {
533 2774 : }
534 :
535 : // This method is not thread safe due to cloning a JSON object
536 : std::unique_ptr<ZarrV3CodecSequence> Clone() const;
537 :
538 : bool InitFromJson(const CPLJSONObject &oCodecs,
539 : ZarrArrayMetadata &oOutputArrayMetadata);
540 :
541 226 : const CPLJSONObject &GetJSon() const
542 : {
543 226 : return m_oCodecArray;
544 : }
545 :
546 10781 : const std::vector<std::unique_ptr<ZarrV3Codec>> &GetCodecs() const
547 : {
548 10781 : return m_apoCodecs;
549 : }
550 :
551 62083 : bool SupportsPartialDecoding() const
552 : {
553 62083 : return m_bPartialDecodingPossible;
554 : }
555 :
556 : bool Encode(ZarrByteVectorQuickResize &abyBuffer);
557 : bool Decode(ZarrByteVectorQuickResize &abyBuffer);
558 :
559 : /** Partial decoding.
560 : * anStartIdx[i]: coordinate in pixel, within the array of an outer chunk,
561 : * that is < m_oInputArrayMetadata.anBlockSizes[i]
562 : * anCount[i]: number of pixels to extract <= m_oInputArrayMetadata.anBlockSizes[i]
563 : */
564 : bool DecodePartial(VSIVirtualHandle *poFile,
565 : ZarrByteVectorQuickResize &abyBuffer,
566 : const std::vector<size_t> &anStartIdx,
567 : const std::vector<size_t> &anCount);
568 :
569 : /** Batch-read multiple inner chunks via ReadMultiRange().
570 : * Delegates to the sharding codec if present, otherwise falls back
571 : * to sequential DecodePartial() calls.
572 : * pszFilename is forwarded to the sharding codec for index caching.
573 : */
574 : bool BatchDecodePartial(
575 : VSIVirtualHandle *poFile, const char *pszFilename,
576 : const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>
577 : &anRequests,
578 : std::vector<ZarrByteVectorQuickResize> &aResults);
579 :
580 : std::vector<size_t>
581 : GetInnerMostBlockSize(const std::vector<size_t> &anOuterBlockSize) const;
582 : };
583 :
584 : #endif
|