LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_group.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 239 265 90.2 %
Date: 2026-03-05 10:33:42 Functions: 22 23 95.7 %

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

Generated by: LCOV version 1.14