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

Generated by: LCOV version 1.14