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

Generated by: LCOV version 1.14