LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_v3_codec.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 62 62 100.0 %
Date: 2026-03-05 10:33:42 Functions: 25 26 96.2 %

          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

Generated by: LCOV version 1.14