LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_v3_group.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 337 377 89.4 %
Date: 2025-10-22 21:46:17 Functions: 13 13 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             : #include "zarr.h"
      14             : 
      15             : #include <algorithm>
      16             : #include <cassert>
      17             : #include <limits>
      18             : #include <map>
      19             : #include <set>
      20             : 
      21             : /************************************************************************/
      22             : /*                      ZarrV3Group::Create()                           */
      23             : /************************************************************************/
      24             : 
      25             : std::shared_ptr<ZarrV3Group>
      26         433 : ZarrV3Group::Create(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
      27             :                     const std::string &osParentName, const std::string &osName,
      28             :                     const std::string &osRootDirectoryName)
      29             : {
      30             :     auto poGroup = std::shared_ptr<ZarrV3Group>(new ZarrV3Group(
      31         433 :         poSharedResource, osParentName, osName, osRootDirectoryName));
      32         433 :     poGroup->SetSelf(poGroup);
      33         433 :     return poGroup;
      34             : }
      35             : 
      36             : /************************************************************************/
      37             : /*                             OpenZarrArray()                          */
      38             : /************************************************************************/
      39             : 
      40         196 : std::shared_ptr<ZarrArray> ZarrV3Group::OpenZarrArray(const std::string &osName,
      41             :                                                       CSLConstList) const
      42             : {
      43         196 :     if (!CheckValidAndErrorOutIfNot())
      44           0 :         return nullptr;
      45             : 
      46         196 :     auto oIter = m_oMapMDArrays.find(osName);
      47         196 :     if (oIter != m_oMapMDArrays.end())
      48          40 :         return oIter->second;
      49             : 
      50             :     const std::string osSubDir =
      51         312 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
      52             :     const std::string osZarrayFilename =
      53         312 :         CPLFormFilenameSafe(osSubDir.c_str(), "zarr.json", nullptr);
      54             : 
      55             :     VSIStatBufL sStat;
      56         156 :     if (VSIStatL(osZarrayFilename.c_str(), &sStat) == 0)
      57             :     {
      58         292 :         CPLJSONDocument oDoc;
      59         146 :         if (!oDoc.Load(osZarrayFilename))
      60           0 :             return nullptr;
      61         292 :         const auto oRoot = oDoc.GetRoot();
      62         146 :         return LoadArray(osName, osZarrayFilename, oRoot);
      63             :     }
      64             : 
      65          10 :     return nullptr;
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /*                   ZarrV3Group::LoadAttributes()                      */
      70             : /************************************************************************/
      71             : 
      72          67 : void ZarrV3Group::LoadAttributes() const
      73             : {
      74          67 :     if (m_bAttributesLoaded)
      75          35 :         return;
      76          32 :     m_bAttributesLoaded = true;
      77             : 
      78             :     const std::string osFilename =
      79          32 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), "zarr.json", nullptr);
      80             : 
      81             :     VSIStatBufL sStat;
      82          32 :     if (VSIStatL(osFilename.c_str(), &sStat) == 0)
      83             :     {
      84          32 :         CPLJSONDocument oDoc;
      85          32 :         if (!oDoc.Load(osFilename))
      86           0 :             return;
      87          32 :         auto oRoot = oDoc.GetRoot();
      88          32 :         m_oAttrGroup.Init(oRoot["attributes"], m_bUpdatable);
      89             :     }
      90             : }
      91             : 
      92             : /************************************************************************/
      93             : /*                        ExploreDirectory()                            */
      94             : /************************************************************************/
      95             : 
      96          69 : void ZarrV3Group::ExploreDirectory() const
      97             : {
      98          69 :     if (m_bDirectoryExplored)
      99           0 :         return;
     100          69 :     m_bDirectoryExplored = true;
     101             : 
     102          69 :     auto psDir = VSIOpenDir(m_osDirectoryName.c_str(), 0, nullptr);
     103          69 :     if (!psDir)
     104           0 :         return;
     105         242 :     while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
     106             :     {
     107         173 :         if (VSI_ISDIR(psEntry->nMode))
     108             :         {
     109         208 :             std::string osName(psEntry->pszName);
     110         208 :             while (!osName.empty() &&
     111         104 :                    (osName.back() == '/' || osName.back() == '\\'))
     112           0 :                 osName.pop_back();
     113         104 :             if (osName.empty())
     114           0 :                 continue;
     115             :             const std::string osSubDir = CPLFormFilenameSafe(
     116         104 :                 m_osDirectoryName.c_str(), osName.c_str(), nullptr);
     117             :             VSIStatBufL sStat;
     118             :             const std::string osZarrJsonFilename =
     119         104 :                 CPLFormFilenameSafe(osSubDir.c_str(), "zarr.json", nullptr);
     120         104 :             if (VSIStatL(osZarrJsonFilename.c_str(), &sStat) == 0)
     121             :             {
     122          98 :                 CPLJSONDocument oDoc;
     123          98 :                 if (oDoc.Load(osZarrJsonFilename.c_str()))
     124             :                 {
     125          98 :                     const auto oRoot = oDoc.GetRoot();
     126          98 :                     if (oRoot.GetInteger("zarr_format") != 3)
     127             :                     {
     128           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     129             :                                  "Unhandled zarr_format value");
     130           0 :                         continue;
     131             :                     }
     132         196 :                     const std::string osNodeType = oRoot.GetString("node_type");
     133          98 :                     if (osNodeType == "array")
     134             :                     {
     135          63 :                         if (!cpl::contains(m_oSetArrayNames, osName))
     136             :                         {
     137          62 :                             m_oSetArrayNames.insert(osName);
     138          62 :                             m_aosArrays.emplace_back(std::move(osName));
     139             :                         }
     140             :                     }
     141          35 :                     else if (osNodeType == "group")
     142             :                     {
     143          35 :                         if (!cpl::contains(m_oSetGroupNames, osName))
     144             :                         {
     145          35 :                             m_oSetGroupNames.insert(osName);
     146          35 :                             m_aosGroups.emplace_back(std::move(osName));
     147             :                         }
     148             :                     }
     149             :                     else
     150             :                     {
     151           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     152             :                                  "Unhandled node_type value");
     153           0 :                         continue;
     154             :                     }
     155             :                 }
     156             :             }
     157             :             else
     158             :             {
     159             :                 // Implicit group (deprecated)
     160           6 :                 if (!cpl::contains(m_oSetGroupNames, osName))
     161             :                 {
     162           6 :                     m_oSetGroupNames.insert(osName);
     163           6 :                     m_aosGroups.emplace_back(std::move(osName));
     164             :                 }
     165             :             }
     166             :         }
     167         173 :     }
     168          69 :     VSICloseDir(psDir);
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                      ZarrV3Group::ZarrV3Group()                      */
     173             : /************************************************************************/
     174             : 
     175         433 : ZarrV3Group::ZarrV3Group(
     176             :     const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     177             :     const std::string &osParentName, const std::string &osName,
     178         433 :     const std::string &osDirectoryName)
     179         433 :     : ZarrGroupBase(poSharedResource, osParentName, osName)
     180             : {
     181         433 :     m_osDirectoryName = osDirectoryName;
     182         433 : }
     183             : 
     184             : /************************************************************************/
     185             : /*                      ZarrV3Group::~ZarrV3Group()                     */
     186             : /************************************************************************/
     187             : 
     188         866 : ZarrV3Group::~ZarrV3Group()
     189             : {
     190         433 :     ZarrV3Group::Close();
     191         866 : }
     192             : 
     193             : /************************************************************************/
     194             : /*                            Close()                                   */
     195             : /************************************************************************/
     196             : 
     197        1006 : bool ZarrV3Group::Close()
     198             : {
     199        1006 :     bool bRet = ZarrGroupBase::Close();
     200             : 
     201        1006 :     if (m_bValid && m_oAttrGroup.IsModified())
     202             :     {
     203         108 :         CPLJSONDocument oDoc;
     204         108 :         auto oRoot = oDoc.GetRoot();
     205          54 :         oRoot.Add("zarr_format", 3);
     206          54 :         oRoot.Add("node_type", "group");
     207          54 :         oRoot.Add("attributes", m_oAttrGroup.Serialize());
     208             :         const std::string osZarrJsonFilename = CPLFormFilenameSafe(
     209          54 :             m_osDirectoryName.c_str(), "zarr.json", nullptr);
     210          54 :         bRet = oDoc.Save(osZarrJsonFilename) && bRet;
     211             :     }
     212             : 
     213        1006 :     return bRet;
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                            OpenZarrGroup()                           */
     218             : /************************************************************************/
     219             : 
     220             : std::shared_ptr<ZarrGroupBase>
     221          55 : ZarrV3Group::OpenZarrGroup(const std::string &osName, CSLConstList) const
     222             : {
     223          55 :     if (!CheckValidAndErrorOutIfNot())
     224           0 :         return nullptr;
     225             : 
     226          55 :     auto oIter = m_oMapGroups.find(osName);
     227          55 :     if (oIter != m_oMapGroups.end())
     228           7 :         return oIter->second;
     229             : 
     230             :     const std::string osSubDir =
     231          96 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
     232             :     const std::string osSubDirZarrJsonFilename =
     233          96 :         CPLFormFilenameSafe(osSubDir.c_str(), "zarr.json", nullptr);
     234             : 
     235             :     VSIStatBufL sStat;
     236             :     // Explicit group
     237          48 :     if (VSIStatL(osSubDirZarrJsonFilename.c_str(), &sStat) == 0)
     238             :     {
     239          80 :         CPLJSONDocument oDoc;
     240          40 :         if (oDoc.Load(osSubDirZarrJsonFilename.c_str()))
     241             :         {
     242          80 :             const auto oRoot = oDoc.GetRoot();
     243          40 :             if (oRoot.GetInteger("zarr_format") != 3)
     244             :             {
     245           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     246             :                          "Unhandled zarr_format value");
     247           0 :                 return nullptr;
     248             :             }
     249         120 :             const std::string osNodeType = oRoot.GetString("node_type");
     250          40 :             if (osNodeType != "group")
     251             :             {
     252           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s is a %s, not a group",
     253             :                          osName.c_str(), osNodeType.c_str());
     254           0 :                 return nullptr;
     255             :             }
     256             :             auto poSubGroup = ZarrV3Group::Create(
     257          80 :                 m_poSharedResource, GetFullName(), osName, osSubDir);
     258          40 :             poSubGroup->m_poParent =
     259          80 :                 std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
     260          40 :             poSubGroup->SetUpdatable(m_bUpdatable);
     261          40 :             m_oMapGroups[osName] = poSubGroup;
     262          40 :             return poSubGroup;
     263             :         }
     264           0 :         return nullptr;
     265             :     }
     266             : 
     267             :     // Implicit group
     268           8 :     if (VSIStatL(osSubDir.c_str(), &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
     269             :     {
     270             :         // Note: Python zarr v3.0.2 still generates implicit groups
     271             :         // See https://github.com/zarr-developers/zarr-python/issues/2794
     272           2 :         CPLError(CE_Warning, CPLE_AppDefined,
     273             :                  "Support for Zarr V3 implicit group is now deprecated, and "
     274             :                  "may be removed in a future version");
     275           2 :         auto poSubGroup = ZarrV3Group::Create(m_poSharedResource, GetFullName(),
     276           4 :                                               osName, osSubDir);
     277           2 :         poSubGroup->m_poParent =
     278           4 :             std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
     279           2 :         poSubGroup->SetUpdatable(m_bUpdatable);
     280           2 :         m_oMapGroups[osName] = poSubGroup;
     281           2 :         return poSubGroup;
     282             :     }
     283             : 
     284           6 :     return nullptr;
     285             : }
     286             : 
     287             : /************************************************************************/
     288             : /*                   ZarrV3Group::CreateOnDisk()                        */
     289             : /************************************************************************/
     290             : 
     291         169 : std::shared_ptr<ZarrV3Group> ZarrV3Group::CreateOnDisk(
     292             :     const std::shared_ptr<ZarrSharedResource> &poSharedResource,
     293             :     const std::string &osParentFullName, const std::string &osName,
     294             :     const std::string &osDirectoryName)
     295             : {
     296         169 :     if (VSIMkdir(osDirectoryName.c_str(), 0755) != 0)
     297             :     {
     298             :         VSIStatBufL sStat;
     299           2 :         if (VSIStatL(osDirectoryName.c_str(), &sStat) == 0)
     300             :         {
     301           2 :             CPLError(CE_Failure, CPLE_FileIO, "Directory %s already exists.",
     302             :                      osDirectoryName.c_str());
     303             :         }
     304             :         else
     305             :         {
     306           0 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s.",
     307             :                      osDirectoryName.c_str());
     308             :         }
     309           2 :         return nullptr;
     310             :     }
     311             : 
     312             :     const std::string osZarrJsonFilename(
     313         334 :         CPLFormFilenameSafe(osDirectoryName.c_str(), "zarr.json", nullptr));
     314         167 :     VSILFILE *fp = VSIFOpenL(osZarrJsonFilename.c_str(), "wb");
     315         167 :     if (!fp)
     316             :     {
     317           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create file %s.",
     318             :                  osZarrJsonFilename.c_str());
     319           0 :         return nullptr;
     320             :     }
     321         167 :     VSIFPrintfL(fp, "{\n"
     322             :                     "    \"zarr_format\": 3,\n"
     323             :                     "    \"node_type\": \"group\",\n"
     324             :                     "    \"attributes\": {}\n"
     325             :                     "}\n");
     326         167 :     VSIFCloseL(fp);
     327             : 
     328             :     auto poGroup = ZarrV3Group::Create(poSharedResource, osParentFullName,
     329         334 :                                        osName, osDirectoryName);
     330         167 :     poGroup->SetUpdatable(true);
     331         167 :     poGroup->m_bDirectoryExplored = true;
     332         167 :     return poGroup;
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                      ZarrV3Group::CreateGroup()                      */
     337             : /************************************************************************/
     338             : 
     339             : std::shared_ptr<GDALGroup>
     340          52 : ZarrV3Group::CreateGroup(const std::string &osName,
     341             :                          CSLConstList /* papszOptions */)
     342             : {
     343          52 :     if (!CheckValidAndErrorOutIfNot())
     344           0 :         return nullptr;
     345             : 
     346          52 :     if (!m_bUpdatable)
     347             :     {
     348           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     349             :                  "Dataset not open in update mode");
     350           1 :         return nullptr;
     351             :     }
     352          51 :     if (!IsValidObjectName(osName))
     353             :     {
     354          14 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid group name");
     355          14 :         return nullptr;
     356             :     }
     357             : 
     358          37 :     GetGroupNames();
     359             : 
     360          37 :     if (cpl::contains(m_oSetGroupNames, osName))
     361             :     {
     362           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     363             :                  "A group with same name already exists");
     364           2 :         return nullptr;
     365             :     }
     366             : 
     367             :     const std::string osDirectoryName =
     368          70 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
     369          35 :     auto poGroup = CreateOnDisk(m_poSharedResource, GetFullName(), osName,
     370          70 :                                 osDirectoryName);
     371          35 :     if (!poGroup)
     372           2 :         return nullptr;
     373          33 :     poGroup->m_poParent =
     374          66 :         std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
     375          33 :     m_oMapGroups[osName] = poGroup;
     376          33 :     m_aosGroups.emplace_back(osName);
     377          33 :     return poGroup;
     378             : }
     379             : 
     380             : /************************************************************************/
     381             : /*                          FillDTypeElts()                             */
     382             : /************************************************************************/
     383             : 
     384         133 : static CPLJSONObject FillDTypeElts(const GDALExtendedDataType &oDataType,
     385             :                                    std::vector<DtypeElt> &aoDtypeElts)
     386             : {
     387         133 :     CPLJSONObject dtype;
     388         266 :     const std::string dummy("dummy");
     389             : 
     390         133 :     const auto eDT = oDataType.GetNumericDataType();
     391         266 :     DtypeElt elt;
     392         133 :     bool bUnsupported = false;
     393         133 :     switch (eDT)
     394             :     {
     395          60 :         case GDT_Byte:
     396             :         {
     397          60 :             elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
     398          60 :             dtype.Set(dummy, "uint8");
     399          60 :             break;
     400             :         }
     401           6 :         case GDT_Int8:
     402             :         {
     403           6 :             elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
     404           6 :             dtype.Set(dummy, "int8");
     405           6 :             break;
     406             :         }
     407           7 :         case GDT_UInt16:
     408             :         {
     409           7 :             elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
     410           7 :             dtype.Set(dummy, "uint16");
     411           7 :             break;
     412             :         }
     413           9 :         case GDT_Int16:
     414             :         {
     415           9 :             elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
     416           9 :             dtype.Set(dummy, "int16");
     417           9 :             break;
     418             :         }
     419           7 :         case GDT_UInt32:
     420             :         {
     421           7 :             elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
     422           7 :             dtype.Set(dummy, "uint32");
     423           7 :             break;
     424             :         }
     425           7 :         case GDT_Int32:
     426             :         {
     427           7 :             elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
     428           7 :             dtype.Set(dummy, "int32");
     429           7 :             break;
     430             :         }
     431           6 :         case GDT_UInt64:
     432             :         {
     433           6 :             elt.nativeType = DtypeElt::NativeType::UNSIGNED_INT;
     434           6 :             dtype.Set(dummy, "uint64");
     435           6 :             break;
     436             :         }
     437           6 :         case GDT_Int64:
     438             :         {
     439           6 :             elt.nativeType = DtypeElt::NativeType::SIGNED_INT;
     440           6 :             dtype.Set(dummy, "int64");
     441           6 :             break;
     442             :         }
     443           1 :         case GDT_Float16:
     444             :         {
     445           1 :             elt.nativeType = DtypeElt::NativeType::IEEEFP;
     446           1 :             dtype.Set(dummy, "float16");
     447           1 :             break;
     448             :         }
     449           7 :         case GDT_Float32:
     450             :         {
     451           7 :             elt.nativeType = DtypeElt::NativeType::IEEEFP;
     452           7 :             dtype.Set(dummy, "float32");
     453           7 :             break;
     454             :         }
     455          13 :         case GDT_Float64:
     456             :         {
     457          13 :             elt.nativeType = DtypeElt::NativeType::IEEEFP;
     458          13 :             dtype.Set(dummy, "float64");
     459          13 :             break;
     460             :         }
     461           2 :         case GDT_Unknown:
     462             :         case GDT_CInt16:
     463             :         case GDT_CInt32:
     464             :         {
     465           2 :             bUnsupported = true;
     466           2 :             break;
     467             :         }
     468           0 :         case GDT_CFloat16:
     469             :         {
     470           0 :             elt.nativeType = DtypeElt::NativeType::COMPLEX_IEEEFP;
     471           0 :             dtype.Set(dummy, "complex32");
     472           0 :             break;
     473             :         }
     474           1 :         case GDT_CFloat32:
     475             :         {
     476           1 :             elt.nativeType = DtypeElt::NativeType::COMPLEX_IEEEFP;
     477           1 :             dtype.Set(dummy, "complex64");
     478           1 :             break;
     479             :         }
     480           1 :         case GDT_CFloat64:
     481             :         {
     482           1 :             elt.nativeType = DtypeElt::NativeType::COMPLEX_IEEEFP;
     483           1 :             dtype.Set(dummy, "complex128");
     484           1 :             break;
     485             :         }
     486           0 :         case GDT_TypeCount:
     487             :         {
     488             :             static_assert(GDT_TypeCount == GDT_CFloat16 + 1,
     489             :                           "GDT_TypeCount == GDT_CFloat16 + 1");
     490           0 :             break;
     491             :         }
     492             :     }
     493         133 :     if (bUnsupported)
     494             :     {
     495           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type: %s",
     496             :                  GDALGetDataTypeName(eDT));
     497           2 :         dtype = CPLJSONObject();
     498           2 :         dtype.Deinit();
     499           2 :         return dtype;
     500             :     }
     501         131 :     elt.nativeOffset = 0;
     502         131 :     elt.nativeSize = GDALGetDataTypeSizeBytes(eDT);
     503         131 :     elt.gdalOffset = 0;
     504         131 :     elt.gdalSize = elt.nativeSize;
     505             : #ifdef CPL_MSB
     506             :     elt.needByteSwapping = elt.nativeSize > 1;
     507             : #endif
     508         131 :     aoDtypeElts.emplace_back(elt);
     509             : 
     510         131 :     return dtype;
     511             : }
     512             : 
     513             : /************************************************************************/
     514             : /*                     ZarrV3Group::CreateMDArray()                     */
     515             : /************************************************************************/
     516             : 
     517         151 : std::shared_ptr<GDALMDArray> ZarrV3Group::CreateMDArray(
     518             :     const std::string &osName,
     519             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     520             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
     521             : {
     522         151 :     if (!CheckValidAndErrorOutIfNot())
     523           0 :         return nullptr;
     524             : 
     525         151 :     if (!m_bUpdatable)
     526             :     {
     527           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     528             :                  "Dataset not open in update mode");
     529           0 :         return nullptr;
     530             :     }
     531         151 :     if (!IsValidObjectName(osName))
     532             :     {
     533          14 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid array name");
     534          14 :         return nullptr;
     535             :     }
     536             : 
     537         137 :     if (oDataType.GetClass() != GEDTC_NUMERIC)
     538             :     {
     539           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     540             :                  "Unsupported data type with Zarr V3");
     541           4 :         return nullptr;
     542             :     }
     543             : 
     544         133 :     if (!EQUAL(CSLFetchNameValueDef(papszOptions, "FILTER", "NONE"), "NONE"))
     545             :     {
     546           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     547             :                  "FILTER option not supported with Zarr V3");
     548           0 :         return nullptr;
     549             :     }
     550             : 
     551         266 :     std::vector<DtypeElt> aoDtypeElts;
     552         399 :     const auto dtype = FillDTypeElts(oDataType, aoDtypeElts)["dummy"];
     553         133 :     if (!dtype.IsValid() || aoDtypeElts.empty())
     554           2 :         return nullptr;
     555             : 
     556         131 :     GetMDArrayNames();
     557             : 
     558         131 :     if (cpl::contains(m_oSetArrayNames, osName))
     559             :     {
     560           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     561             :                  "An array with same name already exists");
     562           2 :         return nullptr;
     563             :     }
     564             : 
     565         258 :     std::vector<GUInt64> anBlockSize;
     566         129 :     if (!ZarrArray::FillBlockSize(aoDimensions, oDataType, anBlockSize,
     567             :                                   papszOptions))
     568           1 :         return nullptr;
     569             : 
     570             :     const char *pszDimSeparator =
     571         128 :         CSLFetchNameValueDef(papszOptions, "DIM_SEPARATOR", "/");
     572             : 
     573             :     const std::string osArrayDirectory =
     574         256 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
     575         128 :     if (VSIMkdir(osArrayDirectory.c_str(), 0755) != 0)
     576             :     {
     577             :         VSIStatBufL sStat;
     578           2 :         if (VSIStatL(osArrayDirectory.c_str(), &sStat) == 0)
     579             :         {
     580           2 :             CPLError(CE_Failure, CPLE_FileIO, "Directory %s already exists.",
     581             :                      osArrayDirectory.c_str());
     582             :         }
     583             :         else
     584             :         {
     585           0 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create directory %s.",
     586             :                      osArrayDirectory.c_str());
     587             :         }
     588           2 :         return nullptr;
     589             :     }
     590             : 
     591         126 :     std::unique_ptr<ZarrV3CodecSequence> poCodecs;
     592         252 :     CPLJSONArray oCodecs;
     593             : 
     594         126 :     const bool bFortranOrder = EQUAL(
     595             :         CSLFetchNameValueDef(papszOptions, "CHUNK_MEMORY_LAYOUT", "C"), "F");
     596         126 :     if (bFortranOrder && aoDimensions.size() > 1)
     597             :     {
     598          80 :         CPLJSONObject oCodec;
     599          40 :         oCodec.Add("name", "transpose");
     600          80 :         std::vector<int> anOrder;
     601          40 :         const int nDims = static_cast<int>(aoDimensions.size());
     602         130 :         for (int i = 0; i < nDims; ++i)
     603             :         {
     604          90 :             anOrder.push_back(nDims - 1 - i);
     605             :         }
     606          40 :         oCodec.Add("configuration",
     607          80 :                    ZarrV3CodecTranspose::GetConfiguration(anOrder));
     608          40 :         oCodecs.Add(oCodec);
     609             :     }
     610             : 
     611             :     // Not documented option, but 'bytes' codec is required
     612             :     const char *pszEndian =
     613         126 :         CSLFetchNameValueDef(papszOptions, "@ENDIAN", "little");
     614             :     {
     615         252 :         CPLJSONObject oCodec;
     616         126 :         oCodec.Add("name", "bytes");
     617         126 :         oCodec.Add("configuration", ZarrV3CodecBytes::GetConfiguration(
     618         126 :                                         EQUAL(pszEndian, "little")));
     619         126 :         oCodecs.Add(oCodec);
     620             :     }
     621             : 
     622             :     const char *pszCompressor =
     623         126 :         CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE");
     624         126 :     if (EQUAL(pszCompressor, "GZIP"))
     625             :     {
     626          46 :         CPLJSONObject oCodec;
     627          23 :         oCodec.Add("name", "gzip");
     628             :         const char *pszLevel =
     629          23 :             CSLFetchNameValueDef(papszOptions, "GZIP_LEVEL", "6");
     630          23 :         oCodec.Add("configuration",
     631          46 :                    ZarrV3CodecGZip::GetConfiguration(atoi(pszLevel)));
     632          23 :         oCodecs.Add(oCodec);
     633             :     }
     634         103 :     else if (EQUAL(pszCompressor, "BLOSC"))
     635             :     {
     636           2 :         const auto psCompressor = CPLGetCompressor("blosc");
     637           2 :         if (!psCompressor)
     638           0 :             return nullptr;
     639             :         const char *pszOptions =
     640           2 :             CSLFetchNameValueDef(psCompressor->papszMetadata, "OPTIONS", "");
     641           2 :         CPLXMLTreeCloser oTreeCompressor(CPLParseXMLString(pszOptions));
     642             :         const auto psRoot =
     643           2 :             oTreeCompressor.get()
     644           2 :                 ? CPLGetXMLNode(oTreeCompressor.get(), "=Options")
     645           2 :                 : nullptr;
     646           2 :         if (!psRoot)
     647           0 :             return nullptr;
     648             : 
     649           2 :         const char *cname = "zlib";
     650          14 :         for (const CPLXMLNode *psNode = psRoot->psChild; psNode != nullptr;
     651          12 :              psNode = psNode->psNext)
     652             :         {
     653          12 :             if (psNode->eType == CXT_Element)
     654             :             {
     655          12 :                 const char *pszName = CPLGetXMLValue(psNode, "name", "");
     656          12 :                 if (EQUAL(pszName, "CNAME"))
     657             :                 {
     658           2 :                     cname = CPLGetXMLValue(psNode, "default", cname);
     659             :                 }
     660             :             }
     661             :         }
     662             : 
     663           4 :         CPLJSONObject oCodec;
     664           2 :         oCodec.Add("name", "blosc");
     665           2 :         cname = CSLFetchNameValueDef(papszOptions, "BLOSC_CNAME", cname);
     666             :         const int clevel =
     667           2 :             atoi(CSLFetchNameValueDef(papszOptions, "BLOSC_CLEVEL", "5"));
     668             :         const char *shuffle =
     669           2 :             CSLFetchNameValueDef(papszOptions, "BLOSC_SHUFFLE", "BYTE");
     670           3 :         shuffle = (EQUAL(shuffle, "0") || EQUAL(shuffle, "NONE")) ? "noshuffle"
     671           1 :                   : (EQUAL(shuffle, "1") || EQUAL(shuffle, "BYTE")) ? "shuffle"
     672           0 :                   : (EQUAL(shuffle, "2") || EQUAL(shuffle, "BIT"))
     673           0 :                       ? "bitshuffle"
     674             :                       : "invalid";
     675           2 :         const int typesize = atoi(CSLFetchNameValueDef(
     676             :             papszOptions, "BLOSC_TYPESIZE",
     677             :             CPLSPrintf("%d", GDALGetDataTypeSizeBytes(GDALGetNonComplexDataType(
     678             :                                  oDataType.GetNumericDataType())))));
     679             :         const int blocksize =
     680           2 :             atoi(CSLFetchNameValueDef(papszOptions, "BLOSC_BLOCKSIZE", "0"));
     681           2 :         oCodec.Add("configuration",
     682           4 :                    ZarrV3CodecBlosc::GetConfiguration(cname, clevel, shuffle,
     683             :                                                       typesize, blocksize));
     684           2 :         oCodecs.Add(oCodec);
     685             :     }
     686         101 :     else if (EQUAL(pszCompressor, "ZSTD"))
     687             :     {
     688           4 :         CPLJSONObject oCodec;
     689           2 :         oCodec.Add("name", "zstd");
     690             :         const char *pszLevel =
     691           2 :             CSLFetchNameValueDef(papszOptions, "ZSTD_LEVEL", "13");
     692           2 :         const bool bChecksum = CPLTestBool(
     693             :             CSLFetchNameValueDef(papszOptions, "ZSTD_CHECKSUM", "FALSE"));
     694           2 :         oCodec.Add("configuration", ZarrV3CodecZstd::GetConfiguration(
     695             :                                         atoi(pszLevel), bChecksum));
     696           2 :         oCodecs.Add(oCodec);
     697             :     }
     698          99 :     else if (!EQUAL(pszCompressor, "NONE"))
     699             :     {
     700           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     701             :                  "COMPRESS = %s not implemented with Zarr V3", pszCompressor);
     702           1 :         return nullptr;
     703             :     }
     704             : 
     705         125 :     if (oCodecs.Size() > 0)
     706             :     {
     707             :         // Byte swapping will be done by the codec chain
     708         125 :         aoDtypeElts.back().needByteSwapping = false;
     709             : 
     710         125 :         ZarrArrayMetadata oInputArrayMetadata;
     711         332 :         for (auto &nSize : anBlockSize)
     712         207 :             oInputArrayMetadata.anBlockSizes.push_back(
     713         207 :                 static_cast<size_t>(nSize));
     714         125 :         oInputArrayMetadata.oElt = aoDtypeElts.back();
     715         125 :         poCodecs = std::make_unique<ZarrV3CodecSequence>(oInputArrayMetadata);
     716         125 :         if (!poCodecs->InitFromJson(oCodecs))
     717           0 :             return nullptr;
     718             :     }
     719             : 
     720             :     auto poArray =
     721         125 :         ZarrV3Array::Create(m_poSharedResource, GetFullName(), osName,
     722         250 :                             aoDimensions, oDataType, aoDtypeElts, anBlockSize);
     723             : 
     724         125 :     if (!poArray)
     725           0 :         return nullptr;
     726         125 :     poArray->SetNew(true);
     727             :     const std::string osFilename =
     728         250 :         CPLFormFilenameSafe(osArrayDirectory.c_str(), "zarr.json", nullptr);
     729         125 :     poArray->SetFilename(osFilename);
     730         125 :     poArray->SetDimSeparator(pszDimSeparator);
     731         125 :     poArray->SetDtype(dtype);
     732         250 :     if (oCodecs.Size() > 0 &&
     733         250 :         oCodecs[oCodecs.Size() - 1].GetString("name") != "bytes")
     734             :     {
     735          54 :         poArray->SetStructuralInfo(
     736          54 :             "COMPRESSOR", oCodecs[oCodecs.Size() - 1].ToString().c_str());
     737             :     }
     738         125 :     if (poCodecs)
     739         125 :         poArray->SetCodecs(oCodecs, std::move(poCodecs));
     740         125 :     poArray->SetUpdatable(true);
     741         125 :     poArray->SetDefinitionModified(true);
     742         125 :     if (!cpl::starts_with(osFilename, "/vsi") && !poArray->Flush())
     743           0 :         return nullptr;
     744         125 :     RegisterArray(poArray);
     745             : 
     746         125 :     return poArray;
     747             : }

Generated by: LCOV version 1.14