LCOV - code coverage report
Current view: top level - frmts/zarr - zarr.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 236 236 100.0 %
Date: 2026-02-27 12:28:37 Functions: 83 83 100.0 %

          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_H
      14             : #define ZARR_H
      15             : 
      16             : #include "cpl_compressor.h"
      17             : #include "cpl_json.h"
      18             : #include "gdal_priv.h"
      19             : #include "gdal_pam_multidim.h"
      20             : #include "memmultidim.h"
      21             : 
      22             : #include <array>
      23             : #include <iterator>
      24             : #include <map>
      25             : #include <memory>
      26             : #include <mutex>
      27             : #include <numeric>
      28             : #include <set>
      29             : 
      30             : #define ZARR_DEBUG_KEY "ZARR"
      31             : 
      32             : #define CRS_ATTRIBUTE_NAME "_CRS"
      33             : 
      34             : // UUID identifying the multiscales zarr convention
      35             : // (https://github.com/zarr-conventions/multiscales)
      36             : constexpr const char *ZARR_MULTISCALES_UUID =
      37             :     "d35379db-88df-4056-af3a-620245f8e347";
      38             : 
      39             : const CPLCompressor *ZarrGetShuffleCompressor();
      40             : const CPLCompressor *ZarrGetShuffleDecompressor();
      41             : const CPLCompressor *ZarrGetQuantizeDecompressor();
      42             : const CPLCompressor *ZarrGetTIFFDecompressor();
      43             : const CPLCompressor *ZarrGetFixedScaleOffsetDecompressor();
      44             : 
      45             : /************************************************************************/
      46             : /*                          MultiplyElements()                          */
      47             : /************************************************************************/
      48             : 
      49             : /** Return the product of elements in the vector
      50             :  */
      51       69598 : template <class T> inline T MultiplyElements(const std::vector<T> &vector)
      52             : {
      53       69598 :     return std::reduce(vector.begin(), vector.end(), T{1},
      54       69598 :                        std::multiplies<T>{});
      55             : }
      56             : 
      57             : /************************************************************************/
      58             : /*                             ZarrDataset                              */
      59             : /************************************************************************/
      60             : 
      61             : class ZarrArray;
      62             : class ZarrGroupBase;
      63             : 
      64             : class ZarrDataset final : public GDALDataset
      65             : {
      66             :     friend class ZarrRasterBand;
      67             : 
      68             :     std::shared_ptr<ZarrGroupBase> m_poRootGroup{};
      69             :     CPLStringList m_aosSubdatasets{};
      70             :     GDALGeoTransform m_gt{};
      71             :     bool m_bHasGT = false;
      72             :     bool m_bSpatialProjConvention = false;
      73             :     std::shared_ptr<GDALDimension> m_poDimX{};
      74             :     std::shared_ptr<GDALDimension> m_poDimY{};
      75             :     std::shared_ptr<ZarrArray> m_poSingleArray{};
      76             : 
      77             :     static GDALDataset *OpenMultidim(const char *pszFilename, bool bUpdateMode,
      78             :                                      CSLConstList papszOpenOptions);
      79             : 
      80             :   public:
      81             :     explicit ZarrDataset(const std::shared_ptr<ZarrGroupBase> &poRootGroup);
      82             :     ~ZarrDataset() override;
      83             : 
      84             :     CPLErr FlushCache(bool bAtClosing = false) override;
      85             : 
      86             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      87             :     static GDALDataset *
      88             :     CreateMultiDimensional(const char *pszFilename,
      89             :                            CSLConstList /*papszRootGroupOptions*/,
      90             :                            CSLConstList /*papszOptions*/);
      91             : 
      92             :     static GDALDataset *Create(const char *pszName, int nXSize, int nYSize,
      93             :                                int nBands, GDALDataType eType,
      94             :                                CSLConstList papszOptions);
      95             : 
      96             :     static GDALDataset *CreateCopy(const char *, GDALDataset *, int,
      97             :                                    CSLConstList papszOptions,
      98             :                                    GDALProgressFunc pfnProgress,
      99             :                                    void *pProgressData);
     100             : 
     101             :     const char *GetMetadataItem(const char *pszName,
     102             :                                 const char *pszDomain) override;
     103             :     CSLConstList GetMetadata(const char *pszDomain) override;
     104             : 
     105             :     CPLErr SetMetadata(CSLConstList papszMetadata,
     106             :                        const char *pszDomain) override;
     107             : 
     108             :     const OGRSpatialReference *GetSpatialRef() const override;
     109             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     110             : 
     111             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     112             :     CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
     113             : 
     114             :     std::shared_ptr<GDALGroup> GetRootGroup() const override;
     115             : 
     116             :   protected:
     117             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
     118             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
     119             :                      GDALDataType eBufType, int nBandCount,
     120             :                      BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     121             :                      GSpacing nLineSpace, GSpacing nBandSpace,
     122             :                      GDALRasterIOExtraArg *psExtraArg) override;
     123             : };
     124             : 
     125             : /************************************************************************/
     126             : /*                            ZarrRasterBand                            */
     127             : /************************************************************************/
     128             : 
     129             : class ZarrRasterBand final : public GDALRasterBand
     130             : {
     131             :     friend class ZarrDataset;
     132             : 
     133             :     std::shared_ptr<GDALMDArray> m_poArray;
     134             :     GDALColorInterp m_eColorInterp = GCI_Undefined;
     135             : 
     136             :   protected:
     137             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
     138             :     CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
     139             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
     140             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
     141             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
     142             :                      GSpacing nLineSpaceBuf,
     143             :                      GDALRasterIOExtraArg *psExtraArg) override;
     144             : 
     145             :   public:
     146             :     explicit ZarrRasterBand(const std::shared_ptr<GDALMDArray> &poArray);
     147             : 
     148             :     double GetNoDataValue(int *pbHasNoData) override;
     149             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
     150             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
     151             :     CPLErr SetNoDataValue(double dfNoData) override;
     152             :     CPLErr SetNoDataValueAsInt64(int64_t nNoData) override;
     153             :     CPLErr SetNoDataValueAsUInt64(uint64_t nNoData) override;
     154             :     double GetOffset(int *pbSuccess = nullptr) override;
     155             :     CPLErr SetOffset(double dfNewOffset) override;
     156             :     double GetScale(int *pbSuccess = nullptr) override;
     157             :     CPLErr SetScale(double dfNewScale) override;
     158             :     const char *GetUnitType() override;
     159             :     CPLErr SetUnitType(const char *pszNewValue) override;
     160             :     GDALColorInterp GetColorInterpretation() override;
     161             :     CPLErr SetColorInterpretation(GDALColorInterp eColorInterp) override;
     162             : };
     163             : 
     164             : /************************************************************************/
     165             : /*                         ZarrAttributeGroup()                         */
     166             : /************************************************************************/
     167             : 
     168             : class ZarrAttributeGroup
     169             : {
     170             :     // Use a MEMGroup as a convenient container for attributes.
     171             :     const bool m_bContainerIsGroup;
     172             :     std::shared_ptr<MEMGroup> m_poGroup;
     173             :     bool m_bModified = false;
     174             : 
     175             :   public:
     176             :     explicit ZarrAttributeGroup(const std::string &osParentName,
     177             :                                 bool bContainerIsGroup);
     178             : 
     179             :     bool Close();
     180             : 
     181             :     void Init(const CPLJSONObject &obj, bool bUpdatable);
     182             : 
     183        9047 :     std::shared_ptr<GDALAttribute> GetAttribute(const std::string &osName) const
     184             :     {
     185        9047 :         return m_poGroup->GetAttribute(osName);
     186             :     }
     187             : 
     188             :     std::vector<std::shared_ptr<GDALAttribute>>
     189         254 :     GetAttributes(CSLConstList papszOptions = nullptr) const
     190             :     {
     191         254 :         return m_poGroup->GetAttributes(papszOptions);
     192             :     }
     193             : 
     194             :     std::shared_ptr<GDALAttribute>
     195         229 :     CreateAttribute(const std::string &osName,
     196             :                     const std::vector<GUInt64> &anDimensions,
     197             :                     const GDALExtendedDataType &oDataType,
     198             :                     CSLConstList /* papszOptions */ = nullptr)
     199             :     {
     200         229 :         auto poAttr = m_poGroup->CreateAttribute(osName, anDimensions,
     201         229 :                                                  oDataType, nullptr);
     202         229 :         if (poAttr)
     203             :         {
     204         229 :             m_bModified = true;
     205             :         }
     206         229 :         return poAttr;
     207             :     }
     208             : 
     209          28 :     bool DeleteAttribute(const std::string &osName)
     210             :     {
     211          28 :         const bool bOK = m_poGroup->DeleteAttribute(osName, nullptr);
     212          28 :         if (bOK)
     213             :         {
     214          16 :             m_bModified = true;
     215             :         }
     216          28 :         return bOK;
     217             :     }
     218             : 
     219        4057 :     void SetUpdatable(bool bUpdatable)
     220             :     {
     221        8114 :         auto attrs = m_poGroup->GetAttributes(nullptr);
     222        4991 :         for (auto &attr : attrs)
     223             :         {
     224        1868 :             auto memAttr = std::dynamic_pointer_cast<MEMAttribute>(attr);
     225         934 :             if (memAttr)
     226         934 :                 memAttr->SetWritable(bUpdatable);
     227             :         }
     228        4057 :     }
     229             : 
     230         403 :     void UnsetModified()
     231             :     {
     232         403 :         m_bModified = false;
     233         806 :         auto attrs = m_poGroup->GetAttributes(nullptr);
     234         557 :         for (auto &attr : attrs)
     235             :         {
     236         308 :             auto memAttr = std::dynamic_pointer_cast<MEMAttribute>(attr);
     237         154 :             if (memAttr)
     238         154 :                 memAttr->SetModified(false);
     239             :         }
     240         403 :     }
     241             : 
     242       16088 :     bool IsModified() const
     243             :     {
     244       16088 :         if (m_bModified)
     245         261 :             return true;
     246       31654 :         const auto attrs = m_poGroup->GetAttributes(nullptr);
     247       18126 :         for (const auto &attr : attrs)
     248             :         {
     249        2330 :             const auto memAttr = std::dynamic_pointer_cast<MEMAttribute>(attr);
     250        2330 :             if (memAttr && memAttr->IsModified())
     251          31 :                 return true;
     252             :         }
     253       15796 :         return false;
     254             :     }
     255             : 
     256             :     CPLJSONObject Serialize() const;
     257             : 
     258             :     void ParentRenamed(const std::string &osNewParentFullName);
     259             : 
     260             :     void ParentDeleted();
     261             : };
     262             : 
     263             : /************************************************************************/
     264             : /*                          ZarrSharedResource                          */
     265             : /************************************************************************/
     266             : 
     267             : class ZarrSharedResource
     268             :     : public std::enable_shared_from_this<ZarrSharedResource>
     269             : {
     270             :   public:
     271             :     enum class ConsolidatedMetadataKind
     272             :     {
     273             :         NONE,
     274             :         EXTERNAL,  // Zarr V2 .zmetadata
     275             :         INTERNAL,  // Zarr V3 consolidated_metadata
     276             :     };
     277             : 
     278             :   private:
     279             :     bool m_bUpdatable = false;
     280             :     std::string m_osRootDirectoryName{};
     281             : 
     282             :     ConsolidatedMetadataKind m_eConsolidatedMetadataKind =
     283             :         ConsolidatedMetadataKind::NONE;
     284             :     CPLJSONObject m_oObjConsolidatedMetadata{};
     285             :     CPLJSONObject m_oRootAttributes{};
     286             :     bool m_bConsolidatedMetadataModified = false;
     287             : 
     288             :     std::shared_ptr<GDALPamMultiDim> m_poPAM{};
     289             :     CPLStringList m_aosOpenOptions{};
     290             :     std::weak_ptr<ZarrGroupBase> m_poWeakRootGroup{};
     291             :     std::set<std::string> m_oSetArrayInLoading{};
     292             :     std::map<std::string, std::shared_ptr<GDALMDArray>> m_oCacheIndexingVar{};
     293             : 
     294             :     explicit ZarrSharedResource(const std::string &osRootDirectoryName,
     295             :                                 bool bUpdatable);
     296             : 
     297             :     std::shared_ptr<ZarrGroupBase> OpenRootGroup();
     298             :     void InitConsolidatedMetadataIfNeeded();
     299             : 
     300             :   public:
     301             :     static std::shared_ptr<ZarrSharedResource>
     302             :     Create(const std::string &osRootDirectoryName, bool bUpdatable);
     303             : 
     304             :     ~ZarrSharedResource();
     305             : 
     306        4971 :     bool IsUpdatable() const
     307             :     {
     308        4971 :         return m_bUpdatable;
     309             :     }
     310             : 
     311          37 :     const CPLJSONObject &GetConsolidatedMetadataObj() const
     312             :     {
     313          37 :         return m_oObjConsolidatedMetadata;
     314             :     }
     315             : 
     316         299 :     bool IsConsolidatedMetadataEnabled() const
     317             :     {
     318         299 :         return m_eConsolidatedMetadataKind != ConsolidatedMetadataKind::NONE;
     319             :     }
     320             : 
     321         325 :     void EnableConsolidatedMetadata(ConsolidatedMetadataKind kind)
     322             :     {
     323         325 :         m_eConsolidatedMetadataKind = kind;
     324         325 :     }
     325             : 
     326             :     void SetZMetadataItem(const std::string &osFilename,
     327             :                           const CPLJSONObject &obj);
     328             : 
     329             :     void DeleteZMetadataItemRecursive(const std::string &osFilename);
     330             : 
     331             :     void RenameZMetadataRecursive(const std::string &osOldFilename,
     332             :                                   const std::string &osNewFilename);
     333             : 
     334        2377 :     const std::shared_ptr<GDALPamMultiDim> &GetPAM()
     335             :     {
     336        2377 :         return m_poPAM;
     337             :     }
     338             : 
     339          13 :     const std::string &GetRootDirectoryName() const
     340             :     {
     341          13 :         return m_osRootDirectoryName;
     342             :     }
     343             : 
     344        2835 :     const CPLStringList &GetOpenOptions() const
     345             :     {
     346        2835 :         return m_aosOpenOptions;
     347             :     }
     348             : 
     349        1594 :     void SetOpenOptions(CSLConstList papszOpenOptions)
     350             :     {
     351        1594 :         m_aosOpenOptions = papszOpenOptions;
     352        1594 :     }
     353             : 
     354             :     void
     355             :     UpdateDimensionSize(const std::shared_ptr<GDALDimension> &poUpdatedDim);
     356             : 
     357             :     std::shared_ptr<ZarrGroupBase> GetRootGroup();
     358             : 
     359          90 :     void SetRootGroup(const std::shared_ptr<ZarrGroupBase> &poRootGroup)
     360             :     {
     361          90 :         m_poWeakRootGroup = poRootGroup;
     362          90 :     }
     363             : 
     364             :     bool AddArrayInLoading(const std::string &osZarrayFilename);
     365             :     void RemoveArrayInLoading(const std::string &osZarrayFilename);
     366             : 
     367             :     struct SetFilenameAdder
     368             :     {
     369             :         std::shared_ptr<ZarrSharedResource> m_poSharedResource;
     370             :         const std::string m_osFilename;
     371             :         const bool m_bOK;
     372             : 
     373        1967 :         SetFilenameAdder(
     374             :             const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     375             :             const std::string &osFilename)
     376        1967 :             : m_poSharedResource(poSharedResource), m_osFilename(osFilename),
     377        1967 :               m_bOK(m_poSharedResource->AddArrayInLoading(m_osFilename))
     378             :         {
     379        1967 :         }
     380             : 
     381        1967 :         ~SetFilenameAdder()
     382        1967 :         {
     383        1967 :             if (m_bOK)
     384        1965 :                 m_poSharedResource->RemoveArrayInLoading(m_osFilename);
     385        1967 :         }
     386             : 
     387        1967 :         bool ok() const
     388             :         {
     389        1967 :             return m_bOK;
     390             :         }
     391             :     };
     392             : 
     393           8 :     void RegisterIndexingVariable(const std::string &osDimName,
     394             :                                   const std::shared_ptr<GDALMDArray> &poVar)
     395             :     {
     396           8 :         m_oCacheIndexingVar[osDimName] = poVar;
     397           8 :     }
     398             : };
     399             : 
     400             : /************************************************************************/
     401             : /*                              ZarrGroup                               */
     402             : /************************************************************************/
     403             : 
     404             : class ZarrArray;
     405             : class ZarrDimension;
     406             : 
     407             : class ZarrGroupBase CPL_NON_FINAL : public GDALGroup
     408             : {
     409             :   protected:
     410             :     friend class ZarrV2Group;
     411             :     friend class ZarrV3Group;
     412             : 
     413             :     // For ZarrV2, this is the directory of the group
     414             :     // For ZarrV3, this is the root directory of the dataset
     415             :     std::shared_ptr<ZarrSharedResource> m_poSharedResource;
     416             :     std::string m_osDirectoryName{};
     417             :     std::weak_ptr<ZarrGroupBase>
     418             :         m_poParent{};  // weak reference to owning parent
     419             :     std::shared_ptr<ZarrGroupBase>
     420             :         m_poParentStrongRef{};  // strong reference, used only when opening from
     421             :                                 // a subgroup
     422             :     mutable std::map<CPLString, std::shared_ptr<ZarrGroupBase>> m_oMapGroups{};
     423             :     mutable std::map<CPLString, std::shared_ptr<ZarrArray>> m_oMapMDArrays{};
     424             :     mutable std::map<CPLString, std::shared_ptr<ZarrDimension>>
     425             :         m_oMapDimensions{};
     426             :     mutable bool m_bDirectoryExplored = false;
     427             :     mutable std::set<std::string> m_oSetGroupNames{};
     428             :     mutable std::vector<std::string> m_aosGroups{};
     429             :     mutable std::set<std::string> m_oSetArrayNames{};
     430             :     mutable std::vector<std::string> m_aosArrays{};
     431             :     mutable ZarrAttributeGroup m_oAttrGroup;
     432             :     mutable bool m_bAttributesLoaded = false;
     433             :     bool m_bReadFromConsolidatedMetadata = false;
     434             :     mutable bool m_bDimensionsInstantiated = false;
     435             :     bool m_bUpdatable = false;
     436             :     bool m_bDimSizeInUpdate = false;
     437             : 
     438             :     virtual void ExploreDirectory() const = 0;
     439             :     virtual void LoadAttributes() const = 0;
     440             : 
     441        2383 :     ZarrGroupBase(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     442             :                   const std::string &osParentName, const std::string &osName)
     443        2383 :         : GDALGroup(osParentName, osName), m_poSharedResource(poSharedResource),
     444        2383 :           m_oAttrGroup(m_osFullName, /*bContainerIsGroup=*/true)
     445             :     {
     446        2383 :     }
     447             : 
     448             :   protected:
     449             :     friend class ZarrDimension;
     450             :     bool RenameDimension(const std::string &osOldName,
     451             :                          const std::string &osNewName);
     452             : 
     453             :     void NotifyChildrenOfRenaming() override;
     454             : 
     455             :     void NotifyChildrenOfDeletion() override;
     456             : 
     457             :   public:
     458             :     ~ZarrGroupBase() override;
     459             : 
     460             :     virtual bool Close();
     461             : 
     462             :     bool Flush();
     463             : 
     464             :     std::shared_ptr<GDALAttribute>
     465         408 :     GetAttribute(const std::string &osName) const override
     466             :     {
     467         408 :         LoadAttributes();
     468         408 :         return m_oAttrGroup.GetAttribute(osName);
     469             :     }
     470             : 
     471             :     std::vector<std::shared_ptr<GDALAttribute>>
     472          47 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     473             :     {
     474          47 :         LoadAttributes();
     475          47 :         return m_oAttrGroup.GetAttributes(papszOptions);
     476             :     }
     477             : 
     478             :     std::shared_ptr<GDALAttribute>
     479             :     CreateAttribute(const std::string &osName,
     480             :                     const std::vector<GUInt64> &anDimensions,
     481             :                     const GDALExtendedDataType &oDataType,
     482             :                     CSLConstList papszOptions = nullptr) override;
     483             : 
     484             :     bool DeleteAttribute(const std::string &osName,
     485             :                          CSLConstList papszOptions = nullptr) override;
     486             : 
     487             :     std::vector<std::shared_ptr<GDALDimension>>
     488             :     GetDimensions(CSLConstList papszOptions = nullptr) const override;
     489             : 
     490             :     std::shared_ptr<GDALDimension>
     491             :     CreateDimension(const std::string &osName, const std::string &osType,
     492             :                     const std::string &osDirection, GUInt64 nSize,
     493             :                     CSLConstList papszOptions = nullptr) override;
     494             : 
     495             :     std::vector<std::string>
     496             :     GetMDArrayNames(CSLConstList papszOptions = nullptr) const override;
     497             : 
     498             :     std::vector<std::string>
     499             :     GetGroupNames(CSLConstList papszOptions = nullptr) const override;
     500             : 
     501             :     virtual std::shared_ptr<ZarrGroupBase>
     502             :     OpenZarrGroup(const std::string &osName,
     503             :                   CSLConstList papszOptions = nullptr) const = 0;
     504             : 
     505             :     std::shared_ptr<GDALGroup>
     506        1432 :     OpenGroup(const std::string &osName,
     507             :               CSLConstList papszOptions = nullptr) const override
     508             :     {
     509             :         return std::static_pointer_cast<GDALGroup>(
     510        1432 :             OpenZarrGroup(osName, papszOptions));
     511             :     }
     512             : 
     513             :     bool DeleteGroup(const std::string &osName,
     514             :                      CSLConstList papszOptions = nullptr) override;
     515             : 
     516             :     std::shared_ptr<GDALMDArray>
     517        2136 :     OpenMDArray(const std::string &osName,
     518             :                 CSLConstList papszOptions = nullptr) const override
     519             :     {
     520             :         return std::static_pointer_cast<GDALMDArray>(
     521        2136 :             OpenZarrArray(osName, papszOptions));
     522             :     }
     523             : 
     524             :     bool DeleteMDArray(const std::string &osName,
     525             :                        CSLConstList papszOptions = nullptr) override;
     526             : 
     527             :     virtual std::shared_ptr<ZarrArray>
     528             :     OpenZarrArray(const std::string &osName,
     529             :                   CSLConstList papszOptions = nullptr) const = 0;
     530             : 
     531        1129 :     void SetDirectoryName(const std::string &osDirectoryName)
     532             :     {
     533        1129 :         m_osDirectoryName = osDirectoryName;
     534        1129 :     }
     535             : 
     536           9 :     const std::string &GetDirectoryName() const
     537             :     {
     538           9 :         return m_osDirectoryName;
     539             :     }
     540             : 
     541             :     void RegisterArray(const std::shared_ptr<ZarrArray> &array) const;
     542             : 
     543        2377 :     void SetUpdatable(bool bUpdatable)
     544             :     {
     545        2377 :         m_bUpdatable = bUpdatable;
     546        2377 :     }
     547             : 
     548             :     void UpdateDimensionSize(const std::shared_ptr<GDALDimension> &poDim);
     549             : 
     550             :     static bool IsValidObjectName(const std::string &osName);
     551             : 
     552             :     bool Rename(const std::string &osNewName) override;
     553             : 
     554             :     //! Returns false in case of error
     555             :     bool
     556             :     CheckArrayOrGroupWithSameNameDoesNotExist(const std::string &osName) const;
     557             : 
     558             :     void ParentRenamed(const std::string &osNewParentFullName) override;
     559             : 
     560             :     void NotifyArrayRenamed(const std::string &osOldName,
     561             :                             const std::string &osNewName);
     562             : 
     563             :     //! Return the group owning the array. Might be nullptr
     564             :     std::shared_ptr<ZarrGroupBase> GetParentGroup() const;
     565             : 
     566        4252 :     std::shared_ptr<ZarrGroupBase> Self() const
     567             :     {
     568        4252 :         return std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
     569             :     }
     570             : 
     571        2765 :     const ZarrAttributeGroup &GetAttributeGroup() const
     572             :     {
     573        2765 :         return m_oAttrGroup;
     574             :     }
     575             : };
     576             : 
     577             : /************************************************************************/
     578             : /*                             ZarrV2Group                              */
     579             : /************************************************************************/
     580             : 
     581             : class ZarrV2Group final : public ZarrGroupBase
     582             : {
     583             :     void ExploreDirectory() const override;
     584             :     void LoadAttributes() const override;
     585             : 
     586             :     std::shared_ptr<ZarrV2Group>
     587             :     GetOrCreateSubGroup(const std::string &osSubGroupFullname);
     588             : 
     589         991 :     ZarrV2Group(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     590             :                 const std::string &osParentName, const std::string &osName)
     591         991 :         : ZarrGroupBase(poSharedResource, osParentName, osName)
     592             :     {
     593         991 :     }
     594             : 
     595             :     bool Close() override;
     596             : 
     597             :   public:
     598             :     static std::shared_ptr<ZarrV2Group>
     599             :     Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     600             :            const std::string &osParentName, const std::string &osName);
     601             : 
     602             :     ~ZarrV2Group() override;
     603             : 
     604             :     static std::shared_ptr<ZarrV2Group>
     605             :     CreateOnDisk(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     606             :                  const std::string &osParentName, const std::string &osName,
     607             :                  const std::string &osDirectoryName);
     608             : 
     609             :     std::shared_ptr<ZarrArray>
     610             :     OpenZarrArray(const std::string &osName,
     611             :                   CSLConstList papszOptions = nullptr) const override;
     612             : 
     613             :     std::shared_ptr<ZarrGroupBase>
     614             :     OpenZarrGroup(const std::string &osName,
     615             :                   CSLConstList papszOptions = nullptr) const override;
     616             : 
     617             :     std::shared_ptr<GDALGroup>
     618             :     CreateGroup(const std::string &osName,
     619             :                 CSLConstList papszOptions = nullptr) override;
     620             : 
     621             :     std::shared_ptr<ZarrArray>
     622             :     LoadArray(const std::string &osArrayName,
     623             :               const std::string &osZarrayFilename, const CPLJSONObject &oRoot,
     624             :               bool bLoadedFromZMetadata,
     625             :               const CPLJSONObject &oAttributes) const;
     626             : 
     627             :     std::shared_ptr<GDALMDArray> CreateMDArray(
     628             :         const std::string &osName,
     629             :         const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     630             :         const GDALExtendedDataType &oDataType,
     631             :         CSLConstList papszOptions = nullptr) override;
     632             : 
     633             :     void InitFromConsolidatedMetadata(const CPLJSONObject &oRoot);
     634             : 
     635             :     bool InitFromZGroup(const CPLJSONObject &oRoot);
     636             : };
     637             : 
     638             : /************************************************************************/
     639             : /*                             ZarrV3Group                              */
     640             : /************************************************************************/
     641             : 
     642             : class ZarrV3Group final : public ZarrGroupBase
     643             : {
     644             :     bool m_bFileHasBeenWritten = false;
     645             : 
     646             :     void ExploreDirectory() const override;
     647             :     void LoadAttributes() const override;
     648             : 
     649             :     ZarrV3Group(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     650             :                 const std::string &osParentName, const std::string &osName,
     651             :                 const std::string &osDirectoryName);
     652             : 
     653             :     std::shared_ptr<ZarrV3Group>
     654             :     GetOrCreateSubGroup(const std::string &osSubGroupFullname);
     655             : 
     656             :     bool Close() override;
     657             : 
     658             :   public:
     659             :     ~ZarrV3Group() override;
     660             : 
     661             :     static std::shared_ptr<ZarrV3Group>
     662             :     Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     663             :            const std::string &osParentName, const std::string &osName,
     664             :            const std::string &osDirectoryName);
     665             : 
     666             :     std::shared_ptr<ZarrArray>
     667             :     OpenZarrArray(const std::string &osName,
     668             :                   CSLConstList papszOptions = nullptr) const override;
     669             : 
     670             :     std::shared_ptr<ZarrGroupBase>
     671             :     OpenZarrGroup(const std::string &osName,
     672             :                   CSLConstList papszOptions = nullptr) const override;
     673             : 
     674             :     static std::shared_ptr<ZarrV3Group>
     675             :     CreateOnDisk(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     676             :                  const std::string &osParentFullName, const std::string &osName,
     677             :                  const std::string &osDirectoryName);
     678             : 
     679             :     std::shared_ptr<GDALGroup>
     680             :     CreateGroup(const std::string &osName,
     681             :                 CSLConstList papszOptions = nullptr) override;
     682             : 
     683             :     std::shared_ptr<ZarrArray> LoadArray(const std::string &osArrayName,
     684             :                                          const std::string &osZarrayFilename,
     685             :                                          const CPLJSONObject &oRoot) const;
     686             : 
     687             :     void GenerateMultiscalesMetadata(const char *pszResampling = nullptr);
     688             : 
     689             :     std::shared_ptr<GDALMDArray> CreateMDArray(
     690             :         const std::string &osName,
     691             :         const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     692             :         const GDALExtendedDataType &oDataType,
     693             :         CSLConstList papszOptions = nullptr) override;
     694             : 
     695         723 :     void SetExplored()
     696             :     {
     697         723 :         m_bDirectoryExplored = true;
     698         723 :     }
     699             : 
     700             :     void
     701             :     InitFromConsolidatedMetadata(const CPLJSONObject &oConsolidatedMetadata,
     702             :                                  const CPLJSONObject &oRootAttributes);
     703             : };
     704             : 
     705             : /************************************************************************/
     706             : /*                            ZarrDimension                             */
     707             : /************************************************************************/
     708             : 
     709             : class ZarrDimension final : public GDALDimensionWeakIndexingVar
     710             : {
     711             :     const bool m_bUpdatable;
     712             :     std::weak_ptr<ZarrGroupBase> m_poParentGroup;
     713             :     bool m_bModified = false;
     714             :     bool m_bXArrayDim = false;
     715             : 
     716             :   public:
     717        4971 :     ZarrDimension(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     718             :                   const std::weak_ptr<ZarrGroupBase> &poParentGroup,
     719             :                   const std::string &osParentName, const std::string &osName,
     720             :                   const std::string &osType, const std::string &osDirection,
     721             :                   GUInt64 nSize)
     722        4971 :         : GDALDimensionWeakIndexingVar(osParentName, osName, osType,
     723             :                                        osDirection, nSize),
     724        4971 :           m_bUpdatable(poSharedResource->IsUpdatable()),
     725        9942 :           m_poParentGroup(poParentGroup)
     726             :     {
     727        4971 :     }
     728             : 
     729             :     bool Rename(const std::string &osNewName) override;
     730             : 
     731        9528 :     bool IsModified() const
     732             :     {
     733        9528 :         return m_bModified;
     734             :     }
     735             : 
     736        1585 :     void SetXArrayDimension()
     737             :     {
     738        1585 :         m_bXArrayDim = true;
     739        1585 :     }
     740             : 
     741       14178 :     bool IsXArrayDimension() const
     742             :     {
     743       14178 :         return m_bXArrayDim;
     744             :     }
     745             : };
     746             : 
     747             : /************************************************************************/
     748             : /*                              DtypeElt()                              */
     749             : /************************************************************************/
     750             : 
     751             : struct DtypeElt
     752             : {
     753             :     enum class NativeType
     754             :     {
     755             :         BOOLEAN,
     756             :         UNSIGNED_INT,
     757             :         SIGNED_INT,
     758             :         IEEEFP,
     759             :         COMPLEX_IEEEFP,
     760             :         STRING_ASCII,
     761             :         STRING_UNICODE
     762             :     };
     763             : 
     764             :     NativeType nativeType = NativeType::BOOLEAN;
     765             :     size_t nativeOffset = 0;
     766             :     size_t nativeSize = 0;
     767             :     bool needByteSwapping = false;
     768             :     bool gdalTypeIsApproxOfNative = false;
     769             :     GDALExtendedDataType gdalType = GDALExtendedDataType::Create(GDT_Unknown);
     770             :     size_t gdalOffset = 0;
     771             :     size_t gdalSize = 0;
     772             : };
     773             : 
     774             : /************************************************************************/
     775             : /*                      ZarrByteVectorQuickResize                       */
     776             : /************************************************************************/
     777             : 
     778             : /* std::vector<GByte> with quick resizing (ie that doesn't zero out when
     779             :  * growing back to a previously reached greater size).
     780             :  */
     781      185411 : class ZarrByteVectorQuickResize
     782             : {
     783             :     std::vector<GByte> m_oVec{};
     784             :     size_t m_nSize = 0;
     785             : 
     786             :   public:
     787      115086 :     ZarrByteVectorQuickResize() = default;
     788             : 
     789             :     ZarrByteVectorQuickResize(const ZarrByteVectorQuickResize &) = delete;
     790             :     ZarrByteVectorQuickResize &
     791             :     operator=(const ZarrByteVectorQuickResize &) = delete;
     792             : 
     793       68583 :     ZarrByteVectorQuickResize(ZarrByteVectorQuickResize &&) = default;
     794             :     ZarrByteVectorQuickResize &
     795             :     operator=(ZarrByteVectorQuickResize &&) = default;
     796             : 
     797      221704 :     void resize(size_t nNewSize)
     798             :     {
     799      221704 :         if (nNewSize > m_oVec.size())
     800       68573 :             m_oVec.resize(nNewSize);
     801      221704 :         m_nSize = nNewSize;
     802      221704 :     }
     803             : 
     804        1603 :     inline void clear()
     805             :     {
     806        1603 :         m_nSize = 0;
     807        1603 :     }
     808             : 
     809         154 :     inline std::vector<GByte>::iterator begin()
     810             :     {
     811         154 :         return m_oVec.begin();
     812             :     }
     813             : 
     814        1188 :     inline std::vector<GByte>::const_iterator begin() const
     815             :     {
     816        1188 :         return m_oVec.begin();
     817             :     }
     818             : 
     819        1031 :     inline std::vector<GByte>::iterator end()
     820             :     {
     821        1031 :         return m_oVec.begin() + m_nSize;
     822             :     }
     823             : 
     824         184 :     inline std::vector<GByte>::const_iterator end() const
     825             :     {
     826         184 :         return m_oVec.begin() + m_nSize;
     827             :     }
     828             : 
     829             :     template <class InputIt>
     830             :     inline std::vector<GByte>::iterator
     831         877 :     insert(std::vector<GByte>::const_iterator pos, InputIt first, InputIt last)
     832             :     {
     833         877 :         const size_t nCount = std::distance(first, last);
     834         877 :         const auto &oVec = m_oVec;
     835         877 :         const size_t nStart = std::distance(oVec.begin(), pos);
     836         877 :         if (nStart == m_nSize && nStart + nCount <= m_oVec.size())
     837             :         {
     838             :             // Insert at end of user-visible vector, but fully inside the
     839             :             // container vector. We can just copy
     840         591 :             std::copy(first, last, m_oVec.begin() + nStart);
     841         591 :             m_nSize += nCount;
     842         591 :             return m_oVec.begin() + nStart;
     843             :         }
     844             :         else
     845             :         {
     846             :             // Generic case
     847         286 :             auto ret = m_oVec.insert(pos, first, last);
     848         286 :             m_nSize += nCount;
     849         286 :             return ret;
     850             :         }
     851             :     }
     852             : 
     853      293184 :     inline bool empty() const
     854             :     {
     855      293184 :         return m_nSize == 0;
     856             :     }
     857             : 
     858      256117 :     inline size_t size() const
     859             :     {
     860      256117 :         return m_nSize;
     861             :     }
     862             : 
     863       46947 :     inline size_t capacity() const
     864             :     {
     865             :         // Not a typo: the capacity of this object is the size
     866             :         // of the underlying std::vector
     867       46947 :         return m_oVec.size();
     868             :     }
     869             : 
     870      162677 :     inline GByte *data()
     871             :     {
     872      162677 :         return m_oVec.data();
     873             :     }
     874             : 
     875       64898 :     inline const GByte *data() const
     876             :     {
     877       64898 :         return m_oVec.data();
     878             :     }
     879             : 
     880        1898 :     inline GByte operator[](size_t idx) const
     881             :     {
     882        1898 :         return m_oVec[idx];
     883             :     }
     884             : 
     885       60841 :     inline GByte &operator[](size_t idx)
     886             :     {
     887       60841 :         return m_oVec[idx];
     888             :     }
     889             : };
     890             : 
     891             : /************************************************************************/
     892             : /*                              ZarrArray                               */
     893             : /************************************************************************/
     894             : 
     895             : class ZarrArray CPL_NON_FINAL : public GDALPamMDArray
     896             : {
     897             :   protected:
     898             :     std::shared_ptr<ZarrSharedResource> m_poSharedResource;
     899             : 
     900             :     //! weak reference to owning parent
     901             :     std::weak_ptr<ZarrGroupBase> m_poParent{};
     902             : 
     903             :     const std::vector<std::shared_ptr<GDALDimension>> m_aoDims;
     904             :     const GDALExtendedDataType m_oType;
     905             : 
     906             :     //! Array (several in case of compound data type) of native Zarr data types
     907             :     const std::vector<DtypeElt> m_aoDtypeElts;
     908             : 
     909             :     /** m_anOuterBlockSize is the chunk_size at the Zarr array level, which
     910             :      * determines the files/objects
     911             :      */
     912             :     const std::vector<GUInt64> m_anOuterBlockSize;
     913             : 
     914             :     /** m_anInnerBlockSize is the inner most block size of sharding, which
     915             :      * is the one exposed to the user with GetBlockSize()
     916             :      * When no sharding is involved m_anOuterBlockSize == m_anInnerBlockSize
     917             :      * Note that m_anOuterBlockSize might be equal to m_anInnerBlockSize, even
     918             :      * when sharding is involved, and it is actually a common use case.
     919             :      */
     920             :     const std::vector<GUInt64> m_anInnerBlockSize;
     921             : 
     922             :     /** m_anCountInnerBlockInOuter[i] = m_anOuterBlockSize[i] / m_anInnerBlockSize[i]
     923             :      * That is the number of inner blocks in one outer block
     924             :      */
     925             :     const std::vector<GUInt64> m_anCountInnerBlockInOuter;
     926             : 
     927             :     //! Total number of inner chunks in the array
     928             :     const uint64_t m_nTotalInnerChunkCount;
     929             : 
     930             :     //! Size in bytes of a inner chunk using the Zarr native data type
     931             :     const size_t m_nInnerBlockSizeBytes;
     932             : 
     933             :     mutable ZarrAttributeGroup m_oAttrGroup;
     934             : 
     935             :     const bool m_bUseOptimizedCodePaths;
     936             : 
     937             :     CPLStringList m_aosStructuralInfo{};
     938             :     CPLJSONObject m_dtype{};
     939             :     GByte *m_pabyNoData = nullptr;
     940             :     std::string m_osDimSeparator{"."};
     941             :     std::string m_osFilename{};
     942             :     mutable ZarrByteVectorQuickResize m_abyRawBlockData{};
     943             :     mutable ZarrByteVectorQuickResize m_abyDecodedBlockData{};
     944             : 
     945             :     /** Inner block index of the cached block
     946             :      * i.e. m_anCachedBlockIndices[i] < cpl::round_up(m_aoDims[i]->GetSize, m_anInnerBlockSize[i])
     947             :      */
     948             :     mutable std::vector<uint64_t> m_anCachedBlockIndices{};
     949             : 
     950             :     mutable bool m_bCachedBlockValid = false;
     951             :     mutable bool m_bCachedBlockEmpty = false;
     952             :     mutable bool m_bDirtyBlock = false;
     953             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
     954             :     mutable bool m_bAllocateWorkingBuffersDone = false;
     955             :     mutable bool m_bWorkingBuffersOK = false;
     956             :     bool m_bUpdatable = false;
     957             :     bool m_bDefinitionModified = false;
     958             :     bool m_bSRSModified = false;
     959             :     bool m_bNew = false;
     960             :     std::string m_osUnit{};
     961             :     bool m_bUnitModified = false;
     962             :     double m_dfOffset = 0.0;
     963             :     bool m_bHasOffset = false;
     964             :     bool m_bOffsetModified = false;
     965             :     double m_dfScale = 1.0;
     966             :     bool m_bHasScale = false;
     967             :     bool m_bScaleModified = false;
     968             :     std::weak_ptr<ZarrGroupBase> m_poGroupWeak{};
     969             :     mutable bool m_bHasTriedBlockCachePresenceArray = false;
     970             :     mutable std::shared_ptr<GDALMDArray> m_poBlockCachePresenceArray{};
     971             :     mutable std::mutex m_oMutex{};
     972             :     CPLStringList m_aosCreationOptions{};
     973             : 
     974             :     struct CachedBlock
     975             :     {
     976             :         ZarrByteVectorQuickResize abyDecoded{};
     977             :     };
     978             : 
     979             :     mutable std::map<std::vector<uint64_t>, CachedBlock> m_oChunkCache{};
     980             : 
     981             :     static uint64_t
     982             :     ComputeBlockCount(const std::string &osName,
     983             :                       const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
     984             :                       const std::vector<GUInt64> &anBlockSize);
     985             : 
     986             :     ZarrArray(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     987             :               const std::shared_ptr<ZarrGroupBase> &poParent,
     988             :               const std::string &osName,
     989             :               const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
     990             :               const GDALExtendedDataType &oType,
     991             :               const std::vector<DtypeElt> &aoDtypeElts,
     992             :               const std::vector<GUInt64> &anOuterBlockSize,
     993             :               const std::vector<GUInt64> &anInnerBlockSize);
     994             : 
     995             :     virtual bool LoadBlockData(const uint64_t *blockIndices,
     996             :                                bool &bMissingBlockOut) const = 0;
     997             : 
     998             :     virtual bool AllocateWorkingBuffers() const = 0;
     999             : 
    1000             :     void SerializeNumericNoData(CPLJSONObject &oRoot) const;
    1001             : 
    1002             :     void DeallocateDecodedBlockData();
    1003             : 
    1004             :     virtual std::string GetDataDirectory() const = 0;
    1005             : 
    1006             :     virtual CPLStringList
    1007             :     GetChunkIndicesFromFilename(const char *pszFilename) const = 0;
    1008             : 
    1009             :     virtual bool FlushDirtyBlock() const = 0;
    1010             : 
    1011             :     std::shared_ptr<GDALMDArray> OpenBlockPresenceCache(bool bCanCreate) const;
    1012             : 
    1013             :     void NotifyChildrenOfRenaming() override;
    1014             : 
    1015             :     void NotifyChildrenOfDeletion() override;
    1016             : 
    1017             :     static void EncodeElt(const std::vector<DtypeElt> &elts, const GByte *pSrc,
    1018             :                           GByte *pDst);
    1019             : 
    1020             :     // Disable copy constructor and assignment operator
    1021             :     ZarrArray(const ZarrArray &) = delete;
    1022             :     ZarrArray &operator=(const ZarrArray &) = delete;
    1023             : 
    1024             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    1025             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    1026             :                const GDALExtendedDataType &bufferDataType,
    1027             :                void *pDstBuffer) const override;
    1028             : 
    1029             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    1030             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    1031             :                 const GDALExtendedDataType &bufferDataType,
    1032             :                 const void *pSrcBuffer) override;
    1033             : 
    1034             :     bool IsEmptyBlock(const ZarrByteVectorQuickResize &abyBlock) const;
    1035             : 
    1036             :     bool IAdviseReadCommon(const GUInt64 *arrayStartIdx, const size_t *count,
    1037             :                            CSLConstList papszOptions,
    1038             :                            std::vector<uint64_t> &anIndicesCur,
    1039             :                            int &nThreadsMax,
    1040             :                            std::vector<uint64_t> &anReqBlocksIndices,
    1041             :                            size_t &nReqBlocks) const;
    1042             : 
    1043             :     CPLJSONObject SerializeSpecialAttributes();
    1044             : 
    1045             :     virtual std::string
    1046             :     BuildChunkFilename(const uint64_t *blockIndices) const = 0;
    1047             : 
    1048             :     bool SetStatistics(bool bApproxStats, double dfMin, double dfMax,
    1049             :                        double dfMean, double dfStdDev, GUInt64 nValidCount,
    1050             :                        CSLConstList papszOptions) override;
    1051             : 
    1052             :     bool IsBlockMissingFromCacheInfo(const std::string &osFilename,
    1053             :                                      const uint64_t *blockIndices) const;
    1054             : 
    1055             :     virtual CPLStringList GetRawBlockInfoInfo() const = 0;
    1056             : 
    1057             :   public:
    1058             :     ~ZarrArray() override;
    1059             : 
    1060             :     static bool ParseChunkSize(const CPLJSONArray &oChunks,
    1061             :                                const GDALExtendedDataType &oType,
    1062             :                                std::vector<GUInt64> &anBlockSize);
    1063             : 
    1064             :     static bool FillBlockSize(
    1065             :         const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
    1066             :         const GDALExtendedDataType &oDataType,
    1067             :         std::vector<GUInt64> &anBlockSize, CSLConstList papszOptions);
    1068             : 
    1069         179 :     bool IsWritable() const override
    1070             :     {
    1071         179 :         return m_bUpdatable;
    1072             :     }
    1073             : 
    1074        4499 :     const std::string &GetFilename() const override
    1075             :     {
    1076        4499 :         return m_osFilename;
    1077             :     }
    1078             : 
    1079             :     const std::vector<std::shared_ptr<GDALDimension>> &
    1080       33874 :     GetDimensions() const override
    1081             :     {
    1082       33874 :         return m_aoDims;
    1083             :     }
    1084             : 
    1085       32272 :     const GDALExtendedDataType &GetDataType() const override
    1086             :     {
    1087       32272 :         return m_oType;
    1088             :     }
    1089             : 
    1090         699 :     std::vector<GUInt64> GetBlockSize() const override
    1091             :     {
    1092         699 :         return m_anInnerBlockSize;
    1093             :     }
    1094             : 
    1095          23 :     CSLConstList GetStructuralInfo() const override
    1096             :     {
    1097          23 :         return m_aosStructuralInfo.List();
    1098             :     }
    1099             : 
    1100       22781 :     const void *GetRawNoDataValue() const override
    1101             :     {
    1102       22781 :         return m_pabyNoData;
    1103             :     }
    1104             : 
    1105          85 :     const std::string &GetUnit() const override
    1106             :     {
    1107          85 :         return m_osUnit;
    1108             :     }
    1109             : 
    1110             :     bool SetUnit(const std::string &osUnit) override;
    1111             : 
    1112          55 :     void RegisterUnit(const std::string &osUnit)
    1113             :     {
    1114          55 :         m_osUnit = osUnit;
    1115          55 :     }
    1116             : 
    1117        2375 :     void RegisterGroup(const std::weak_ptr<ZarrGroupBase> &group)
    1118             :     {
    1119        2375 :         m_poGroupWeak = group;
    1120        2375 :     }
    1121             : 
    1122             :     double GetOffset(bool *pbHasOffset,
    1123             :                      GDALDataType *peStorageType) const override;
    1124             : 
    1125             :     double GetScale(bool *pbHasScale,
    1126             :                     GDALDataType *peStorageType) const override;
    1127             : 
    1128             :     bool SetOffset(double dfOffset, GDALDataType eStorageType) override;
    1129             : 
    1130             :     bool SetScale(double dfScale, GDALDataType eStorageType) override;
    1131             : 
    1132             :     std::vector<std::shared_ptr<GDALMDArray>>
    1133             :     GetCoordinateVariables() const override;
    1134             : 
    1135             :     bool IsRegularlySpaced(double &dfStart, double &dfIncrement) const override;
    1136             : 
    1137             :     bool Resize(const std::vector<GUInt64> &anNewDimSizes,
    1138             :                 CSLConstList) override;
    1139             : 
    1140           3 :     void RegisterOffset(double dfOffset)
    1141             :     {
    1142           3 :         m_bHasOffset = true;
    1143           3 :         m_dfOffset = dfOffset;
    1144           3 :     }
    1145             : 
    1146           3 :     void RegisterScale(double dfScale)
    1147             :     {
    1148           3 :         m_bHasScale = true;
    1149           3 :         m_dfScale = dfScale;
    1150           3 :     }
    1151             : 
    1152             :     bool SetRawNoDataValue(const void *pRawNoData) override;
    1153             : 
    1154             :     void RegisterNoDataValue(const void *);
    1155             : 
    1156        2375 :     void SetFilename(const std::string &osFilename)
    1157             :     {
    1158        2375 :         m_osFilename = osFilename;
    1159        2375 :     }
    1160             : 
    1161        2375 :     void SetDimSeparator(const std::string &osDimSeparator)
    1162             :     {
    1163        2375 :         m_osDimSeparator = osDimSeparator;
    1164        2375 :     }
    1165             : 
    1166             :     void SetAttributes(const std::shared_ptr<ZarrGroupBase> &poGroup,
    1167             :                        CPLJSONObject &oAttributes);
    1168             : 
    1169          51 :     void SetSRS(const std::shared_ptr<OGRSpatialReference> &srs)
    1170             :     {
    1171          51 :         m_poSRS = srs;
    1172          51 :     }
    1173             : 
    1174             :     std::shared_ptr<GDALAttribute>
    1175          77 :     GetAttribute(const std::string &osName) const override
    1176             :     {
    1177          77 :         return m_oAttrGroup.GetAttribute(osName);
    1178             :     }
    1179             : 
    1180             :     std::vector<std::shared_ptr<GDALAttribute>>
    1181         207 :     GetAttributes(CSLConstList papszOptions) const override
    1182             :     {
    1183         207 :         return m_oAttrGroup.GetAttributes(papszOptions);
    1184             :     }
    1185             : 
    1186             :     std::shared_ptr<GDALAttribute>
    1187             :     CreateAttribute(const std::string &osName,
    1188             :                     const std::vector<GUInt64> &anDimensions,
    1189             :                     const GDALExtendedDataType &oDataType,
    1190             :                     CSLConstList papszOptions = nullptr) override;
    1191             : 
    1192             :     bool DeleteAttribute(const std::string &osName,
    1193             :                          CSLConstList papszOptions = nullptr) override;
    1194             : 
    1195             :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override;
    1196             : 
    1197             :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override;
    1198             : 
    1199        2375 :     void SetUpdatable(bool bUpdatable)
    1200             :     {
    1201        2375 :         m_bUpdatable = bUpdatable;
    1202        2375 :     }
    1203             : 
    1204        2375 :     void SetDtype(const CPLJSONObject &dtype)
    1205             :     {
    1206        2375 :         m_dtype = dtype;
    1207        2375 :     }
    1208             : 
    1209         515 :     void SetDefinitionModified(bool bModified)
    1210             :     {
    1211         515 :         m_bDefinitionModified = bModified;
    1212         515 :     }
    1213             : 
    1214         500 :     void SetNew(bool bNew)
    1215             :     {
    1216         500 :         m_bNew = bNew;
    1217         500 :     }
    1218             : 
    1219             :     bool Rename(const std::string &osNewName) override;
    1220             : 
    1221             :     void ParentRenamed(const std::string &osNewParentFullName) override;
    1222             : 
    1223             :     virtual bool Flush() = 0;
    1224             : 
    1225             :     //! Return the group owning the array. Might be nullptr
    1226             :     std::shared_ptr<ZarrGroupBase> GetParentGroup() const;
    1227             : 
    1228             :     //! Return the root group. Might be nullptr
    1229        1937 :     std::shared_ptr<GDALGroup> GetRootGroup() const override
    1230             :     {
    1231        1937 :         return m_poSharedResource->GetRootGroup();
    1232             :     }
    1233             : 
    1234             :     bool GetRawBlockInfo(const uint64_t *panBlockCoordinates,
    1235             :                          GDALMDArrayRawBlockInfo &info) const override;
    1236             : 
    1237             :     bool BlockCachePresence();
    1238             : 
    1239         754 :     void SetStructuralInfo(const char *pszKey, const char *pszValue)
    1240             :     {
    1241         754 :         m_aosStructuralInfo.SetNameValue(pszKey, pszValue);
    1242         754 :     }
    1243             : 
    1244         500 :     void SetCreationOptions(CSLConstList papszOptions)
    1245             :     {
    1246         500 :         m_aosCreationOptions = papszOptions;
    1247         500 :     }
    1248             : 
    1249             :     static void DecodeSourceElt(const std::vector<DtypeElt> &elts,
    1250             :                                 const GByte *pSrc, GByte *pDst);
    1251             : 
    1252             :     static void GetDimensionTypeDirection(CPLJSONObject &oAttributes,
    1253             :                                           std::string &osType,
    1254             :                                           std::string &osDirection);
    1255             : };
    1256             : 
    1257             : /************************************************************************/
    1258             : /*                             ZarrV2Array                              */
    1259             : /************************************************************************/
    1260             : 
    1261             : class ZarrV2Array final : public ZarrArray
    1262             : {
    1263             :     CPLJSONObject m_oCompressorJSon{};
    1264             :     const CPLCompressor *m_psCompressor = nullptr;
    1265             :     std::string m_osDecompressorId{};
    1266             :     const CPLCompressor *m_psDecompressor = nullptr;
    1267             :     CPLJSONArray m_oFiltersArray{};  // ZarrV2 specific
    1268             :     bool m_bFortranOrder = false;
    1269             :     mutable ZarrByteVectorQuickResize
    1270             :         m_abyTmpRawBlockData{};  // used for Fortran order
    1271             : 
    1272             :     ZarrV2Array(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
    1273             :                 const std::shared_ptr<ZarrGroupBase> &poParent,
    1274             :                 const std::string &osName,
    1275             :                 const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
    1276             :                 const GDALExtendedDataType &oType,
    1277             :                 const std::vector<DtypeElt> &aoDtypeElts,
    1278             :                 const std::vector<GUInt64> &anOuterBlockSize,
    1279             :                 bool bFortranOrder);
    1280             : 
    1281             :     bool Serialize();
    1282             : 
    1283             :     bool LoadBlockData(const uint64_t *blockIndices, bool bUseMutex,
    1284             :                        const CPLCompressor *psDecompressor,
    1285             :                        ZarrByteVectorQuickResize &abyRawBlockData,
    1286             :                        ZarrByteVectorQuickResize &abyTmpRawBlockData,
    1287             :                        ZarrByteVectorQuickResize &abyDecodedBlockData,
    1288             :                        bool &bMissingBlockOut) const;
    1289             : 
    1290             :     bool NeedDecodedBuffer() const;
    1291             : 
    1292             :     bool AllocateWorkingBuffers(
    1293             :         ZarrByteVectorQuickResize &abyRawBlockData,
    1294             :         ZarrByteVectorQuickResize &abyTmpRawBlockData,
    1295             :         ZarrByteVectorQuickResize &abyDecodedBlockData) const;
    1296             : 
    1297             :     void BlockTranspose(const ZarrByteVectorQuickResize &abySrc,
    1298             :                         ZarrByteVectorQuickResize &abyDst, bool bDecode) const;
    1299             : 
    1300             :     // Disable copy constructor and assignment operator
    1301             :     ZarrV2Array(const ZarrV2Array &) = delete;
    1302             :     ZarrV2Array &operator=(const ZarrV2Array &) = delete;
    1303             : 
    1304             :   public:
    1305             :     ~ZarrV2Array() override;
    1306             : 
    1307             :     static std::shared_ptr<ZarrV2Array>
    1308             :     Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
    1309             :            const std::shared_ptr<ZarrGroupBase> &poParent,
    1310             :            const std::string &osName,
    1311             :            const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
    1312             :            const GDALExtendedDataType &oType,
    1313             :            const std::vector<DtypeElt> &aoDtypeElts,
    1314             :            const std::vector<GUInt64> &anBlockSize, bool bFortranOrder);
    1315             : 
    1316             :     void SetCompressorJson(const CPLJSONObject &oCompressor);
    1317             : 
    1318        1071 :     void SetCompressorDecompressor(const std::string &osDecompressorId,
    1319             :                                    const CPLCompressor *psComp,
    1320             :                                    const CPLCompressor *psDecomp)
    1321             :     {
    1322        1071 :         m_psCompressor = psComp;
    1323        1071 :         m_osDecompressorId = osDecompressorId;
    1324        1071 :         m_psDecompressor = psDecomp;
    1325        1071 :     }
    1326             : 
    1327             :     void SetFilters(const CPLJSONArray &oFiltersArray);
    1328             : 
    1329             :     bool Flush() override;
    1330             : 
    1331             :   protected:
    1332             :     std::string GetDataDirectory() const override;
    1333             : 
    1334             :     CPLStringList
    1335             :     GetChunkIndicesFromFilename(const char *pszFilename) const override;
    1336             : 
    1337             :     bool FlushDirtyBlock() const override;
    1338             : 
    1339             :     std::string BuildChunkFilename(const uint64_t *blockIndices) const override;
    1340             : 
    1341             :     bool AllocateWorkingBuffers() const override;
    1342             : 
    1343             :     bool LoadBlockData(const uint64_t *blockIndices,
    1344             :                        bool &bMissingBlockOut) const override;
    1345             : 
    1346             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    1347             :                      CSLConstList papszOptions) const override;
    1348             : 
    1349             :     CPLStringList GetRawBlockInfoInfo() const override;
    1350             : };
    1351             : 
    1352             : /************************************************************************/
    1353             : /*                             ZarrV3Array                              */
    1354             : /************************************************************************/
    1355             : 
    1356             : class ZarrV3CodecSequence;
    1357             : 
    1358             : class ZarrV3Array final : public ZarrArray
    1359             : {
    1360             :     bool m_bV2ChunkKeyEncoding = false;
    1361             :     std::unique_ptr<ZarrV3CodecSequence> m_poCodecs{};
    1362             :     CPLJSONArray m_oJSONCodecs{};
    1363             :     mutable bool m_bOverviewsLoaded = false;
    1364             :     mutable std::vector<std::shared_ptr<GDALMDArray>> m_apoOverviews{};
    1365             : 
    1366             :     /** Shard write cache: accumulates dirty inner chunks per shard, encodes
    1367             :      * each shard exactly once on FlushShardCache() (called from Flush()).
    1368             :      * Without this cache, FlushDirtyBlockSharded() would re-read, decode,
    1369             :      * overlay, re-encode, and write the entire shard for every inner chunk,
    1370             :      * resulting in O(N) encode cycles per shard where N = inner chunks/shard.
    1371             :      */
    1372             :     struct ShardWriteEntry
    1373             :     {
    1374             :         ZarrByteVectorQuickResize abyShardBuffer{};
    1375             :         std::vector<bool> abDirtyInnerChunks{};
    1376             :     };
    1377             : 
    1378             :     // Note: cache is unbounded - one entry per shard written. For very large
    1379             :     // rasters, consider adding LRU eviction in a follow-up.
    1380             :     mutable std::map<std::string, ShardWriteEntry> m_oShardWriteCache{};
    1381             : 
    1382             :     ZarrV3Array(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
    1383             :                 const std::shared_ptr<ZarrGroupBase> &poParent,
    1384             :                 const std::string &osName,
    1385             :                 const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
    1386             :                 const GDALExtendedDataType &oType,
    1387             :                 const std::vector<DtypeElt> &aoDtypeElts,
    1388             :                 const std::vector<GUInt64> &anOuterBlockSize,
    1389             :                 const std::vector<GUInt64> &anInnerBlockSize);
    1390             : 
    1391             :     bool Serialize(const CPLJSONObject &oAttrs);
    1392             : 
    1393             :     bool NeedDecodedBuffer() const;
    1394             : 
    1395             :     bool AllocateWorkingBuffers(
    1396             :         ZarrByteVectorQuickResize &abyRawBlockData,
    1397             :         ZarrByteVectorQuickResize &abyDecodedBlockData) const;
    1398             : 
    1399             :     bool LoadBlockData(const uint64_t *blockIndices, bool bUseMutex,
    1400             :                        ZarrV3CodecSequence *poCodecs,
    1401             :                        ZarrByteVectorQuickResize &abyRawBlockData,
    1402             :                        ZarrByteVectorQuickResize &abyDecodedBlockData,
    1403             :                        bool &bMissingBlockOut) const;
    1404             : 
    1405             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    1406             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    1407             :                const GDALExtendedDataType &bufferDataType,
    1408             :                void *pDstBuffer) const override;
    1409             : 
    1410             :     void PreloadShardedBlocks(const GUInt64 *arrayStartIdx,
    1411             :                               const size_t *count) const;
    1412             : 
    1413             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    1414             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    1415             :                 const GDALExtendedDataType &bufferDataType,
    1416             :                 const void *pSrcBuffer) override;
    1417             : 
    1418             :     bool WriteChunksThreadSafe(const GUInt64 *arrayStartIdx,
    1419             :                                const size_t *count, const GInt64 *arrayStep,
    1420             :                                const GPtrDiff_t *bufferStride,
    1421             :                                const GDALExtendedDataType &bufferDataType,
    1422             :                                const void *pSrcBuffer, const int iThread,
    1423             :                                const int nThreads,
    1424             :                                std::string &osErrorMsg) const;
    1425             : 
    1426             :     void LoadOverviews() const;
    1427             : 
    1428             :     void ReconstructCreationOptionsFromCodecs();
    1429             : 
    1430             :   public:
    1431             :     ~ZarrV3Array() override;
    1432             : 
    1433             :     static std::shared_ptr<ZarrV3Array>
    1434             :     Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
    1435             :            const std::shared_ptr<ZarrGroupBase> &poParent,
    1436             :            const std::string &osName,
    1437             :            const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
    1438             :            const GDALExtendedDataType &oType,
    1439             :            const std::vector<DtypeElt> &aoDtypeElts,
    1440             :            const std::vector<GUInt64> &anOuterBlockSize,
    1441             :            const std::vector<GUInt64> &anInnerBlockSize);
    1442             : 
    1443        1118 :     void SetIsV2ChunkKeyEncoding(bool b)
    1444             :     {
    1445        1118 :         m_bV2ChunkKeyEncoding = b;
    1446        1118 :     }
    1447             : 
    1448             :     void SetCodecs(const CPLJSONArray &oJSONCodecs,
    1449             :                    std::unique_ptr<ZarrV3CodecSequence> &&poCodecs);
    1450             : 
    1451             :     bool Flush() override;
    1452             : 
    1453             :     static std::unique_ptr<ZarrV3CodecSequence>
    1454             :     SetupCodecs(const CPLJSONArray &oCodecs,
    1455             :                 const std::vector<GUInt64> &anOuterBlockSize,
    1456             :                 std::vector<GUInt64> &anInnerBlockSize, DtypeElt &zarrDataType,
    1457             :                 const std::vector<GByte> &abyNoData);
    1458             :     int GetOverviewCount() const override;
    1459             : 
    1460             :     std::shared_ptr<GDALMDArray> GetOverview(int idx) const override;
    1461             : 
    1462             :     CPLErr BuildOverviews(const char *pszResampling, int nOverviews,
    1463             :                           const int *panOverviewList,
    1464             :                           GDALProgressFunc pfnProgress, void *pProgressData,
    1465             :                           CSLConstList papszOptions) override;
    1466             : 
    1467             :     static void
    1468             :     ExtractSubArrayFromLargerOne(const ZarrByteVectorQuickResize &abySrc,
    1469             :                                  const std::vector<size_t> &anSrcBlockSize,
    1470             :                                  const std::vector<size_t> &anInnerBlockSize,
    1471             :                                  const std::vector<size_t> &anInnerBlockIndices,
    1472             :                                  ZarrByteVectorQuickResize &abyChunk,
    1473             :                                  const size_t nDTSize);
    1474             : 
    1475             :   protected:
    1476             :     std::string GetDataDirectory() const override;
    1477             : 
    1478             :     CPLStringList
    1479             :     GetChunkIndicesFromFilename(const char *pszFilename) const override;
    1480             : 
    1481             :     bool AllocateWorkingBuffers() const override;
    1482             : 
    1483             :     bool FlushDirtyBlock() const override;
    1484             :     bool FlushDirtyBlockSharded() const;
    1485             :     bool FlushSingleShard(const std::string &osFilename,
    1486             :                           ShardWriteEntry &entry) const;
    1487             :     bool FlushShardCache() const;
    1488             : 
    1489             :     std::string BuildChunkFilename(const uint64_t *blockIndices) const override;
    1490             : 
    1491             :     bool LoadBlockData(const uint64_t *blockIndices,
    1492             :                        bool &bMissingBlockOut) const override;
    1493             : 
    1494             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    1495             :                      CSLConstList papszOptions) const override;
    1496             : 
    1497             :     CPLStringList GetRawBlockInfoInfo() const override;
    1498             : };
    1499             : 
    1500             : void ZarrClearCoordinateCache();
    1501             : void ZarrClearShardIndexCache();
    1502             : void ZarrEraseShardIndexFromCache(const std::string &osFilename);
    1503             : 
    1504             : #endif  // ZARR_H

Generated by: LCOV version 1.14