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

Generated by: LCOV version 1.14