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

Generated by: LCOV version 1.14