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 3879 : 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 1966 : virtual bool IsNoOp() const
72 : {
73 1966 : 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 11558 : const std::string &GetName() const
93 : {
94 11558 : return m_osName;
95 : }
96 :
97 : const CPLJSONObject &GetConfiguration() const
98 : {
99 : return m_oConfiguration;
100 : }
101 :
102 : virtual std::vector<size_t>
103 243 : GetInnerMostBlockSize(const std::vector<size_t> &input) const
104 : {
105 243 : return input;
106 : }
107 :
108 1156 : virtual void ChangeArrayShapeForward(std::vector<size_t> &anStartIdx,
109 : std::vector<size_t> &anCount)
110 : {
111 : (void)anStartIdx;
112 : (void)anCount;
113 1156 : }
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 555 : IOType GetInputType() const override
136 : {
137 555 : return IOType::BYTES;
138 : }
139 :
140 555 : IOType GetOutputType() const override
141 : {
142 555 : 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 1799 : IOType GetInputType() const override
234 : {
235 1799 : return IOType::ARRAY;
236 : }
237 :
238 1800 : IOType GetOutputType() const override
239 : {
240 1800 : 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 1853 : bool IsNoOp() const override
256 : {
257 : if constexpr (CPL_IS_LSB)
258 1853 : return m_oInputArrayMetadata.oElt.nativeSize == 1 || m_bLittle;
259 : else
260 : return m_oInputArrayMetadata.oElt.nativeSize == 1 || !m_bLittle;
261 : }
262 :
263 : std::unique_ptr<ZarrV3Codec> Clone() const override;
264 :
265 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
266 : ZarrByteVectorQuickResize &abyDst) const override;
267 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
268 : ZarrByteVectorQuickResize &abyDst) const override;
269 : };
270 :
271 : /************************************************************************/
272 : /* ZarrV3CodecTranspose */
273 : /************************************************************************/
274 :
275 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/transpose/v1.0.html
276 : class ZarrV3CodecTranspose final : public ZarrV3Codec
277 : {
278 : // m_anOrder is such that dest_shape[i] = source_shape[m_anOrder[i]]
279 : // where source_shape[] is the size of the array before the Encode() operation
280 : // and dest_shape[] its size after.
281 : // m_anOrder[] describes a bijection of [0,N-1] to [0,N-1]
282 : std::vector<int> m_anOrder{};
283 :
284 : // m_anReverseOrder is such that m_anReverseOrder[m_anOrder[i]] = i
285 : std::vector<int> m_anReverseOrder{};
286 :
287 : bool Transpose(const ZarrByteVectorQuickResize &abySrc,
288 : ZarrByteVectorQuickResize &abyDst, bool bEncodeDirection,
289 : const std::vector<size_t> &anForwardBlockSizes) const;
290 :
291 : public:
292 : static constexpr const char *NAME = "transpose";
293 :
294 : ZarrV3CodecTranspose();
295 :
296 83 : IOType GetInputType() const override
297 : {
298 83 : return IOType::ARRAY;
299 : }
300 :
301 83 : IOType GetOutputType() const override
302 : {
303 83 : return IOType::ARRAY;
304 : }
305 :
306 : static CPLJSONObject GetConfiguration(const std::vector<int> &anOrder);
307 :
308 : bool InitFromConfiguration(const CPLJSONObject &configuration,
309 : const ZarrArrayMetadata &oInputArrayMetadata,
310 : ZarrArrayMetadata &oOutputArrayMetadata,
311 : bool bEmitWarnings) override;
312 :
313 1 : const std::vector<int> &GetOrder() const
314 : {
315 1 : return m_anOrder;
316 : }
317 :
318 : bool IsNoOp() const override;
319 :
320 : std::unique_ptr<ZarrV3Codec> Clone() const override;
321 :
322 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
323 : ZarrByteVectorQuickResize &abyDst) const override;
324 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
325 : ZarrByteVectorQuickResize &abyDst) const override;
326 :
327 : bool DecodePartial(VSIVirtualHandle *poFile,
328 : const ZarrByteVectorQuickResize &abySrc,
329 : ZarrByteVectorQuickResize &abyDst,
330 : std::vector<size_t> &anStartIdx,
331 : std::vector<size_t> &anCount) override;
332 :
333 : std::vector<size_t>
334 : GetInnerMostBlockSize(const std::vector<size_t> &input) const override;
335 :
336 : template <class T>
337 50 : inline void Reorder1DForward(std::vector<T> &vector) const
338 : {
339 100 : std::vector<T> res;
340 150 : for (int idx : m_anOrder)
341 100 : res.push_back(vector[idx]);
342 50 : vector = std::move(res);
343 50 : }
344 :
345 : template <class T>
346 50 : inline void Reorder1DInverse(std::vector<T> &vector) const
347 : {
348 100 : std::vector<T> res;
349 150 : for (int idx : m_anReverseOrder)
350 100 : res.push_back(vector[idx]);
351 50 : vector = std::move(res);
352 50 : }
353 :
354 25 : void ChangeArrayShapeForward(std::vector<size_t> &anStartIdx,
355 : std::vector<size_t> &anCount) override
356 : {
357 25 : Reorder1DForward(anStartIdx);
358 25 : Reorder1DForward(anCount);
359 25 : }
360 : };
361 :
362 : /************************************************************************/
363 : /* ZarrV3CodecCRC32C */
364 : /************************************************************************/
365 :
366 : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/crc32c/index.html
367 : class ZarrV3CodecCRC32C final : public ZarrV3Codec
368 : {
369 : bool m_bCheckCRC = true;
370 :
371 : public:
372 : static constexpr const char *NAME = "crc32c";
373 :
374 : ZarrV3CodecCRC32C();
375 :
376 773 : IOType GetInputType() const override
377 : {
378 773 : return IOType::BYTES;
379 : }
380 :
381 773 : IOType GetOutputType() const override
382 : {
383 773 : return IOType::BYTES;
384 : }
385 :
386 : bool InitFromConfiguration(const CPLJSONObject &configuration,
387 : const ZarrArrayMetadata &oInputArrayMetadata,
388 : ZarrArrayMetadata &oOutputArrayMetadata,
389 : bool bEmitWarnings) override;
390 :
391 : std::unique_ptr<ZarrV3Codec> Clone() const override;
392 :
393 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
394 : ZarrByteVectorQuickResize &abyDst) const override;
395 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
396 : ZarrByteVectorQuickResize &abyDst) const override;
397 : };
398 :
399 : /************************************************************************/
400 : /* ZarrV3CodecShardingIndexed */
401 : /************************************************************************/
402 :
403 : class ZarrV3CodecSequence;
404 :
405 : // https://zarr-specs.readthedocs.io/en/latest/v3/codecs/sharding-indexed/index.html
406 : class ZarrV3CodecShardingIndexed final : public ZarrV3Codec
407 : {
408 : std::unique_ptr<ZarrV3CodecSequence> m_poCodecSequence{};
409 : std::unique_ptr<ZarrV3CodecSequence> m_poIndexCodecSequence{};
410 : bool m_bIndexLocationAtEnd = true;
411 : bool m_bIndexHasCRC32 = false;
412 : std::vector<size_t> m_anInnerBlockSize{};
413 :
414 : struct Location
415 : {
416 : uint64_t nOffset;
417 : uint64_t nSize;
418 : };
419 :
420 : public:
421 : static constexpr const char *NAME = "sharding_indexed";
422 :
423 : ZarrV3CodecShardingIndexed();
424 :
425 656 : IOType GetInputType() const override
426 : {
427 656 : return IOType::ARRAY;
428 : }
429 :
430 638 : IOType GetOutputType() const override
431 : {
432 638 : return IOType::BYTES;
433 : }
434 :
435 : bool InitFromConfiguration(const CPLJSONObject &configuration,
436 : const ZarrArrayMetadata &oInputArrayMetadata,
437 : ZarrArrayMetadata &oOutputArrayMetadata,
438 : bool bEmitWarnings) override;
439 :
440 : std::unique_ptr<ZarrV3Codec> Clone() const override;
441 :
442 : bool Encode(const ZarrByteVectorQuickResize &abySrc,
443 : ZarrByteVectorQuickResize &abyDst) const override;
444 : bool Decode(const ZarrByteVectorQuickResize &abySrc,
445 : ZarrByteVectorQuickResize &abyDst) const override;
446 :
447 : bool DecodePartial(VSIVirtualHandle *poFile,
448 : const ZarrByteVectorQuickResize &abySrc,
449 : ZarrByteVectorQuickResize &abyDst,
450 : std::vector<size_t> &anStartIdx,
451 : std::vector<size_t> &anCount) override;
452 :
453 : /** Batch-read multiple inner chunks from the same shard via two
454 : * ReadMultiRange() passes (index entries, then data), then decode.
455 : * No persistent state is kept — each call reads the needed index
456 : * entries on demand.
457 : */
458 : bool BatchDecodePartial(
459 : VSIVirtualHandle *poFile,
460 : const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>
461 : &anRequests,
462 : std::vector<ZarrByteVectorQuickResize> &aResults);
463 :
464 : std::vector<size_t>
465 : GetInnerMostBlockSize(const std::vector<size_t> &input) const override;
466 : };
467 :
468 : /************************************************************************/
469 : /* ZarrV3CodecSequence */
470 : /************************************************************************/
471 :
472 : class ZarrV3CodecSequence
473 : {
474 : const ZarrArrayMetadata m_oInputArrayMetadata;
475 : std::vector<std::unique_ptr<ZarrV3Codec>> m_apoCodecs{};
476 : CPLJSONObject m_oCodecArray{};
477 : ZarrByteVectorQuickResize m_abyTmp{};
478 : bool m_bPartialDecodingPossible = false;
479 :
480 : bool AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer, size_t nEltCount);
481 :
482 : public:
483 2478 : explicit ZarrV3CodecSequence(const ZarrArrayMetadata &oInputArrayMetadata)
484 2478 : : m_oInputArrayMetadata(oInputArrayMetadata)
485 : {
486 2478 : }
487 :
488 : // This method is not thread safe due to cloning a JSON object
489 : std::unique_ptr<ZarrV3CodecSequence> Clone() const;
490 :
491 : bool InitFromJson(const CPLJSONObject &oCodecs,
492 : ZarrArrayMetadata &oOutputArrayMetadata);
493 :
494 147 : const CPLJSONObject &GetJSon() const
495 : {
496 147 : return m_oCodecArray;
497 : }
498 :
499 10837 : const std::vector<std::unique_ptr<ZarrV3Codec>> &GetCodecs() const
500 : {
501 10837 : return m_apoCodecs;
502 : }
503 :
504 38282 : bool SupportsPartialDecoding() const
505 : {
506 38282 : return m_bPartialDecodingPossible;
507 : }
508 :
509 : bool Encode(ZarrByteVectorQuickResize &abyBuffer);
510 : bool Decode(ZarrByteVectorQuickResize &abyBuffer);
511 :
512 : /** Partial decoding.
513 : * anStartIdx[i]: coordinate in pixel, within the array of an outer chunk,
514 : * that is < m_oInputArrayMetadata.anBlockSizes[i]
515 : * anCount[i]: number of pixels to extract <= m_oInputArrayMetadata.anBlockSizes[i]
516 : */
517 : bool DecodePartial(VSIVirtualHandle *poFile,
518 : ZarrByteVectorQuickResize &abyBuffer,
519 : const std::vector<size_t> &anStartIdx,
520 : const std::vector<size_t> &anCount);
521 :
522 : /** Batch-read multiple inner chunks via ReadMultiRange().
523 : * Delegates to the sharding codec if present, otherwise falls back
524 : * to sequential DecodePartial() calls.
525 : */
526 : bool BatchDecodePartial(
527 : VSIVirtualHandle *poFile,
528 : const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>
529 : &anRequests,
530 : std::vector<ZarrByteVectorQuickResize> &aResults);
531 :
532 : std::vector<size_t>
533 : GetInnerMostBlockSize(const std::vector<size_t> &anOuterBlockSize) const;
534 : };
535 :
536 : #endif
|