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