LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_group.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 220 242 90.9 %
Date: 2025-08-01 10:10:57 Functions: 20 21 95.2 %

          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             : /*                          ~ZarrGroupBase()                            */
      23             : /************************************************************************/
      24             : 
      25        1518 : ZarrGroupBase::~ZarrGroupBase()
      26             : {
      27        1518 :     CPL_IGNORE_RET_VAL(ZarrGroupBase::Close());
      28        1518 : }
      29             : 
      30             : /************************************************************************/
      31             : /*                            Close()                                   */
      32             : /************************************************************************/
      33             : 
      34        4782 : bool ZarrGroupBase::Close()
      35             : {
      36        4782 :     bool ret = true;
      37             : 
      38        5544 :     for (auto &kv : m_oMapGroups)
      39             :     {
      40         762 :         ret = kv.second->Close() && ret;
      41             :     }
      42             : 
      43        8835 :     for (auto &kv : m_oMapMDArrays)
      44             :     {
      45        4053 :         ret = kv.second->Flush() && ret;
      46             :     }
      47        4782 :     return ret;
      48             : }
      49             : 
      50             : /************************************************************************/
      51             : /*                           GetMDArrayNames()                          */
      52             : /************************************************************************/
      53             : 
      54        1145 : std::vector<std::string> ZarrGroupBase::GetMDArrayNames(CSLConstList) const
      55             : {
      56        1145 :     if (!CheckValidAndErrorOutIfNot())
      57           0 :         return {};
      58             : 
      59        1145 :     if (!m_bDirectoryExplored)
      60         269 :         ExploreDirectory();
      61             : 
      62        1145 :     return m_aosArrays;
      63             : }
      64             : 
      65             : /************************************************************************/
      66             : /*                            RegisterArray()                           */
      67             : /************************************************************************/
      68             : 
      69        1251 : void ZarrGroupBase::RegisterArray(const std::shared_ptr<ZarrArray> &array) const
      70             : {
      71        1251 :     m_oMapMDArrays[array->GetName()] = array;
      72        1251 :     if (std::find(m_aosArrays.begin(), m_aosArrays.end(), array->GetName()) ==
      73        2502 :         m_aosArrays.end())
      74             :     {
      75        1167 :         m_aosArrays.emplace_back(array->GetName());
      76             :     }
      77        2502 :     array->RegisterGroup(
      78        2502 :         std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock()));
      79        1251 : }
      80             : 
      81             : /************************************************************************/
      82             : /*                            GetGroupNames()                           */
      83             : /************************************************************************/
      84             : 
      85         297 : std::vector<std::string> ZarrGroupBase::GetGroupNames(CSLConstList) const
      86             : {
      87         297 :     if (!CheckValidAndErrorOutIfNot())
      88           0 :         return {};
      89             : 
      90         297 :     if (!m_bDirectoryExplored)
      91          38 :         ExploreDirectory();
      92             : 
      93         297 :     return m_aosGroups;
      94             : }
      95             : 
      96             : /************************************************************************/
      97             : /*                             DeleteGroup()                            */
      98             : /************************************************************************/
      99             : 
     100          18 : bool ZarrGroupBase::DeleteGroup(const std::string &osName,
     101             :                                 CSLConstList /*papszOptions*/)
     102             : {
     103          18 :     if (!CheckValidAndErrorOutIfNot())
     104           0 :         return false;
     105             : 
     106          18 :     if (!m_bUpdatable)
     107             :     {
     108           6 :         CPLError(CE_Failure, CPLE_NotSupported,
     109             :                  "Dataset not open in update mode");
     110           6 :         return false;
     111             :     }
     112             : 
     113          12 :     GetGroupNames();
     114             : 
     115          12 :     auto oIterNames = std::find(m_aosGroups.begin(), m_aosGroups.end(), osName);
     116          12 :     if (oIterNames == m_aosGroups.end())
     117             :     {
     118           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     119             :                  "Group %s is not a sub-group of this group", osName.c_str());
     120           6 :         return false;
     121             :     }
     122             : 
     123             :     const std::string osSubDirName =
     124          12 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
     125           6 :     if (VSIRmdirRecursive(osSubDirName.c_str()) != 0)
     126             :     {
     127           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot delete %s",
     128             :                  osSubDirName.c_str());
     129           0 :         return false;
     130             :     }
     131             : 
     132           6 :     m_poSharedResource->DeleteZMetadataItemRecursive(osSubDirName);
     133             : 
     134           6 :     m_aosGroups.erase(oIterNames);
     135             : 
     136           6 :     auto oIter = m_oMapGroups.find(osName);
     137           6 :     if (oIter != m_oMapGroups.end())
     138             :     {
     139           4 :         oIter->second->Deleted();
     140           4 :         m_oMapGroups.erase(oIter);
     141             :     }
     142             : 
     143           6 :     return true;
     144             : }
     145             : 
     146             : /************************************************************************/
     147             : /*                       NotifyChildrenOfDeletion()                     */
     148             : /************************************************************************/
     149             : 
     150           6 : void ZarrGroupBase::NotifyChildrenOfDeletion()
     151             : {
     152           8 :     for (const auto &oIter : m_oMapGroups)
     153           2 :         oIter.second->ParentDeleted();
     154             : 
     155          10 :     for (const auto &oIter : m_oMapMDArrays)
     156           4 :         oIter.second->ParentDeleted();
     157             : 
     158           6 :     m_oAttrGroup.ParentDeleted();
     159             : 
     160          10 :     for (const auto &oIter : m_oMapDimensions)
     161           4 :         oIter.second->ParentDeleted();
     162           6 : }
     163             : 
     164             : /************************************************************************/
     165             : /*                  ZarrGroupBase::CreateAttribute()                    */
     166             : /************************************************************************/
     167             : 
     168          78 : std::shared_ptr<GDALAttribute> ZarrGroupBase::CreateAttribute(
     169             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
     170             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
     171             : {
     172          78 :     if (!CheckValidAndErrorOutIfNot())
     173           0 :         return nullptr;
     174             : 
     175          78 :     if (!m_bUpdatable)
     176             :     {
     177           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     178             :                  "Dataset not open in update mode");
     179           3 :         return nullptr;
     180             :     }
     181          75 :     if (anDimensions.size() >= 2)
     182             :     {
     183           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     184             :                  "Cannot create attributes of dimension >= 2");
     185           3 :         return nullptr;
     186             :     }
     187          72 :     LoadAttributes();
     188             :     return m_oAttrGroup.CreateAttribute(osName, anDimensions, oDataType,
     189          72 :                                         papszOptions);
     190             : }
     191             : 
     192             : /************************************************************************/
     193             : /*                  ZarrGroupBase::DeleteAttribute()                   */
     194             : /************************************************************************/
     195             : 
     196          18 : bool ZarrGroupBase::DeleteAttribute(const std::string &osName, CSLConstList)
     197             : {
     198          18 :     if (!CheckValidAndErrorOutIfNot())
     199           0 :         return false;
     200             : 
     201          18 :     if (!m_bUpdatable)
     202             :     {
     203           6 :         CPLError(CE_Failure, CPLE_NotSupported,
     204             :                  "Dataset not open in update mode");
     205           6 :         return false;
     206             :     }
     207             : 
     208          12 :     LoadAttributes();
     209          12 :     return m_oAttrGroup.DeleteAttribute(osName);
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                            GetDimensions()                           */
     214             : /************************************************************************/
     215             : 
     216             : std::vector<std::shared_ptr<GDALDimension>>
     217         582 : ZarrGroupBase::GetDimensions(CSLConstList) const
     218             : {
     219         582 :     if (!CheckValidAndErrorOutIfNot())
     220           0 :         return {};
     221             : 
     222         582 :     if (!m_bReadFromZMetadata && !m_bDimensionsInstantiated)
     223             :     {
     224         290 :         m_bDimensionsInstantiated = true;
     225             :         // We need to instantiate arrays to discover dimensions
     226         580 :         const auto aosArrays = GetMDArrayNames();
     227         324 :         for (const auto &osArray : aosArrays)
     228             :         {
     229          34 :             OpenMDArray(osArray);
     230             :         }
     231             :     }
     232             : 
     233        1164 :     std::vector<std::shared_ptr<GDALDimension>> oRes;
     234        1025 :     for (const auto &oIter : m_oMapDimensions)
     235             :     {
     236         443 :         oRes.push_back(oIter.second);
     237             :     }
     238         582 :     return oRes;
     239             : }
     240             : 
     241             : /************************************************************************/
     242             : /*                            DeleteMDArray()                           */
     243             : /************************************************************************/
     244             : 
     245          18 : bool ZarrGroupBase::DeleteMDArray(const std::string &osName,
     246             :                                   CSLConstList /*papszOptions*/)
     247             : {
     248          18 :     if (!CheckValidAndErrorOutIfNot())
     249           0 :         return false;
     250             : 
     251          18 :     if (!m_bUpdatable)
     252             :     {
     253           6 :         CPLError(CE_Failure, CPLE_NotSupported,
     254             :                  "Dataset not open in update mode");
     255           6 :         return false;
     256             :     }
     257             : 
     258          12 :     GetMDArrayNames();
     259             : 
     260          12 :     auto oIterNames = std::find(m_aosArrays.begin(), m_aosArrays.end(), osName);
     261          12 :     if (oIterNames == m_aosArrays.end())
     262             :     {
     263           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     264             :                  "Array %s is not an array of this group", osName.c_str());
     265           6 :         return false;
     266             :     }
     267             : 
     268             :     const std::string osSubDirName =
     269          12 :         CPLFormFilenameSafe(m_osDirectoryName.c_str(), osName.c_str(), nullptr);
     270           6 :     if (VSIRmdirRecursive(osSubDirName.c_str()) != 0)
     271             :     {
     272           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot delete %s",
     273             :                  osSubDirName.c_str());
     274           0 :         return false;
     275             :     }
     276             : 
     277           6 :     m_poSharedResource->DeleteZMetadataItemRecursive(osSubDirName);
     278             : 
     279           6 :     m_aosArrays.erase(oIterNames);
     280             : 
     281           6 :     auto oIter = m_oMapMDArrays.find(osName);
     282           6 :     if (oIter != m_oMapMDArrays.end())
     283             :     {
     284           4 :         oIter->second->Deleted();
     285           4 :         m_oMapMDArrays.erase(oIter);
     286             :     }
     287             : 
     288           6 :     return true;
     289             : }
     290             : 
     291             : /************************************************************************/
     292             : /*                             CreateDimension()                        */
     293             : /************************************************************************/
     294             : 
     295         508 : std::shared_ptr<GDALDimension> ZarrGroupBase::CreateDimension(
     296             :     const std::string &osName, const std::string &osType,
     297             :     const std::string &osDirection, GUInt64 nSize, CSLConstList)
     298             : {
     299         508 :     if (!CheckValidAndErrorOutIfNot())
     300           0 :         return nullptr;
     301             : 
     302         508 :     if (osName.empty())
     303             :     {
     304           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     305             :                  "Empty dimension name not supported");
     306           0 :         return nullptr;
     307             :     }
     308         508 :     GetDimensions(nullptr);
     309             : 
     310         508 :     if (m_oMapDimensions.find(osName) != m_oMapDimensions.end())
     311             :     {
     312           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     313             :                  "A dimension with same name already exists");
     314           0 :         return nullptr;
     315             :     }
     316             :     auto newDim(std::make_shared<ZarrDimension>(
     317         508 :         m_poSharedResource,
     318        1016 :         std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock()), GetFullName(),
     319        1016 :         osName, osType, osDirection, nSize));
     320         508 :     newDim->SetXArrayDimension();
     321         508 :     m_oMapDimensions[osName] = newDim;
     322         508 :     return newDim;
     323             : }
     324             : 
     325             : /************************************************************************/
     326             : /*                          RenameDimension()                           */
     327             : /************************************************************************/
     328             : 
     329          12 : bool ZarrGroupBase::RenameDimension(const std::string &osOldName,
     330             :                                     const std::string &osNewName)
     331             : {
     332          12 :     if (m_oMapDimensions.find(osNewName) != m_oMapDimensions.end())
     333             :     {
     334           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     335             :                  "A dimension with same name already exists");
     336           6 :         return false;
     337             :     }
     338           6 :     auto oIter = m_oMapDimensions.find(osOldName);
     339           6 :     if (oIter == m_oMapDimensions.end())
     340             :     {
     341           0 :         CPLAssert(false);
     342             :         return false;
     343             :     }
     344           6 :     auto poDim = std::move(oIter->second);
     345           6 :     m_oMapDimensions.erase(oIter);
     346           6 :     m_oMapDimensions[osNewName] = std::move(poDim);
     347           6 :     return true;
     348             : }
     349             : 
     350             : /************************************************************************/
     351             : /*                  ZarrGroupBase::UpdateDimensionSize()                */
     352             : /************************************************************************/
     353             : 
     354           7 : void ZarrGroupBase::UpdateDimensionSize(
     355             :     const std::shared_ptr<GDALDimension> &poUpdatedDim)
     356             : {
     357          14 :     const auto aosGroupNames = GetGroupNames();
     358           7 :     for (const auto &osGroupName : aosGroupNames)
     359             :     {
     360           0 :         auto poSubGroup = OpenZarrGroup(osGroupName);
     361           0 :         if (poSubGroup)
     362             :         {
     363           0 :             poSubGroup->UpdateDimensionSize(poUpdatedDim);
     364             :         }
     365             :     }
     366          14 :     const auto aosArrayNames = GetMDArrayNames();
     367          22 :     for (const auto &osArrayName : aosArrayNames)
     368             :     {
     369             :         // Disable checks that size of variables referenced by _ARRAY_DIMENSIONS
     370             :         // are consistent with array shapes, as we are in the middle of updating
     371             :         // things
     372          15 :         m_bDimSizeInUpdate = true;
     373          30 :         auto poArray = OpenZarrArray(osArrayName);
     374          15 :         m_bDimSizeInUpdate = false;
     375          15 :         if (poArray)
     376             :         {
     377          39 :             for (auto &poDim : poArray->GetDimensions())
     378             :             {
     379          24 :                 if (poDim->GetFullName() == poUpdatedDim->GetFullName())
     380             :                 {
     381             :                     auto poModifiableDim =
     382          30 :                         std::dynamic_pointer_cast<ZarrDimension>(poDim);
     383          15 :                     CPLAssert(poModifiableDim);
     384          15 :                     poModifiableDim->SetSize(poUpdatedDim->GetSize());
     385          15 :                     poArray->SetDefinitionModified(true);
     386             :                 }
     387             :             }
     388             :         }
     389             :     }
     390           7 : }
     391             : 
     392             : /************************************************************************/
     393             : /*                  ZarrGroupBase::NotifyArrayRenamed()                 */
     394             : /************************************************************************/
     395             : 
     396           6 : void ZarrGroupBase::NotifyArrayRenamed(const std::string &osOldName,
     397             :                                        const std::string &osNewName)
     398             : {
     399           6 :     for (auto &osName : m_aosArrays)
     400             :     {
     401           6 :         if (osName == osOldName)
     402             :         {
     403           6 :             osName = osNewName;
     404           6 :             break;
     405             :         }
     406             :     }
     407             : 
     408           6 :     auto oIter = m_oMapMDArrays.find(osOldName);
     409           6 :     if (oIter != m_oMapMDArrays.end())
     410             :     {
     411           6 :         auto poArray = std::move(oIter->second);
     412           6 :         m_oMapMDArrays.erase(oIter);
     413           6 :         m_oMapMDArrays[osNewName] = std::move(poArray);
     414             :     }
     415           6 : }
     416             : 
     417             : /************************************************************************/
     418             : /*                         IsValidObjectName()                          */
     419             : /************************************************************************/
     420             : 
     421             : /* static */
     422         679 : bool ZarrGroupBase::IsValidObjectName(const std::string &osName)
     423             : {
     424        1951 :     return !(osName.empty() || osName == "." || osName == ".." ||
     425        1272 :              osName.find('/') != std::string::npos ||
     426        1256 :              osName.find('\\') != std::string::npos ||
     427         624 :              osName.find(':') != std::string::npos ||
     428        1295 :              STARTS_WITH(osName.c_str(), ".z"));
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                 CheckArrayOrGroupWithSameNameDoesNotExist()          */
     433             : /************************************************************************/
     434             : 
     435          27 : bool ZarrGroupBase::CheckArrayOrGroupWithSameNameDoesNotExist(
     436             :     const std::string &osName) const
     437             : {
     438          54 :     const auto groupNames = GetGroupNames();
     439          27 :     if (std::find(groupNames.begin(), groupNames.end(), osName) !=
     440          54 :         groupNames.end())
     441             :     {
     442           9 :         CPLError(CE_Failure, CPLE_AppDefined,
     443             :                  "A group with same name already exists");
     444           9 :         return false;
     445             :     }
     446             : 
     447          36 :     const auto arrayNames = GetMDArrayNames();
     448          18 :     if (std::find(arrayNames.begin(), arrayNames.end(), osName) !=
     449          36 :         arrayNames.end())
     450             :     {
     451           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     452             :                  "An array with same name already exists");
     453           6 :         return false;
     454             :     }
     455             : 
     456          12 :     return true;
     457             : }
     458             : 
     459             : /************************************************************************/
     460             : /*                              Rename()                                */
     461             : /************************************************************************/
     462             : 
     463          36 : bool ZarrGroupBase::Rename(const std::string &osNewName)
     464             : {
     465          36 :     if (!CheckValidAndErrorOutIfNot())
     466           3 :         return false;
     467             : 
     468          33 :     if (!m_bUpdatable)
     469             :     {
     470           6 :         CPLError(CE_Failure, CPLE_NotSupported,
     471             :                  "Dataset not open in update mode");
     472           6 :         return false;
     473             :     }
     474          27 :     if (!IsValidObjectName(osNewName))
     475             :     {
     476           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid group name");
     477           6 :         return false;
     478             :     }
     479          21 :     if (m_osName == "/")
     480             :     {
     481           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot rename root group");
     482           6 :         return false;
     483             :     }
     484             : 
     485          30 :     auto pParent = std::dynamic_pointer_cast<ZarrGroupBase>(m_poParent.lock());
     486          15 :     if (pParent)
     487             :     {
     488          15 :         if (!pParent->CheckArrayOrGroupWithSameNameDoesNotExist(osNewName))
     489           9 :             return false;
     490             :     }
     491             : 
     492          12 :     std::string osNewDirectoryName(m_osDirectoryName);
     493           6 :     osNewDirectoryName.resize(osNewDirectoryName.size() - m_osName.size());
     494           6 :     osNewDirectoryName += osNewName;
     495             : 
     496           6 :     if (VSIRename(m_osDirectoryName.c_str(), osNewDirectoryName.c_str()) != 0)
     497             :     {
     498           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Renaming of %s to %s failed",
     499             :                  m_osDirectoryName.c_str(), osNewDirectoryName.c_str());
     500           0 :         return false;
     501             :     }
     502             : 
     503           6 :     if (pParent)
     504             :     {
     505           6 :         auto oIter = pParent->m_oMapGroups.find(m_osName);
     506           6 :         if (oIter != pParent->m_oMapGroups.end())
     507             :         {
     508           6 :             pParent->m_oMapGroups.erase(oIter);
     509           6 :             CPLAssert(m_pSelf.lock());
     510          12 :             pParent->m_oMapGroups[osNewName] =
     511          18 :                 std::dynamic_pointer_cast<ZarrGroupBase>(m_pSelf.lock());
     512             :         }
     513             : 
     514           6 :         for (auto &osName : pParent->m_aosGroups)
     515             :         {
     516           6 :             if (osName == m_osName)
     517             :             {
     518           6 :                 osName = osNewName;
     519           6 :                 break;
     520             :             }
     521             :         }
     522             :     }
     523             : 
     524           6 :     m_poSharedResource->RenameZMetadataRecursive(m_osDirectoryName,
     525             :                                                  osNewDirectoryName);
     526             : 
     527           6 :     m_osDirectoryName = std::move(osNewDirectoryName);
     528             : 
     529           6 :     BaseRename(osNewName);
     530             : 
     531           6 :     return true;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                          ParentRenamed()                             */
     536             : /************************************************************************/
     537             : 
     538           4 : void ZarrGroupBase::ParentRenamed(const std::string &osNewParentFullName)
     539             : {
     540           8 :     auto pParent = std::dynamic_pointer_cast<ZarrGroupBase>(m_poParent.lock());
     541             :     // The parent necessarily exist, since it notified us
     542           4 :     CPLAssert(pParent);
     543             : 
     544           8 :     m_osDirectoryName = CPLFormFilenameSafe(pParent->m_osDirectoryName.c_str(),
     545           4 :                                             m_osName.c_str(), nullptr);
     546             : 
     547           4 :     GDALGroup::ParentRenamed(osNewParentFullName);
     548           4 : }
     549             : 
     550             : /************************************************************************/
     551             : /*                       NotifyChildrenOfRenaming()                     */
     552             : /************************************************************************/
     553             : 
     554          10 : void ZarrGroupBase::NotifyChildrenOfRenaming()
     555             : {
     556          14 :     for (const auto &oIter : m_oMapGroups)
     557           4 :         oIter.second->ParentRenamed(m_osFullName);
     558             : 
     559          19 :     for (const auto &oIter : m_oMapMDArrays)
     560           9 :         oIter.second->ParentRenamed(m_osFullName);
     561             : 
     562          10 :     m_oAttrGroup.ParentRenamed(m_osFullName);
     563             : 
     564          16 :     for (const auto &oIter : m_oMapDimensions)
     565           6 :         oIter.second->ParentRenamed(m_osFullName);
     566          10 : }

Generated by: LCOV version 1.14