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

Generated by: LCOV version 1.14