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

Generated by: LCOV version 1.14