LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_sharedresource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 217 237 91.6 %
Date: 2026-03-05 10:33:42 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Zarr driver
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "zarr.h"
      14             : #include "vsikerchunk.h"
      15             : 
      16             : #include "cpl_json.h"
      17             : 
      18             : /************************************************************************/
      19             : /*               ZarrSharedResource::ZarrSharedResource()               */
      20             : /************************************************************************/
      21             : 
      22        1967 : ZarrSharedResource::ZarrSharedResource(const std::string &osRootDirectoryName,
      23        1967 :                                        bool bUpdatable)
      24        1967 :     : m_bUpdatable(bUpdatable)
      25             : {
      26        1967 :     m_oObjConsolidatedMetadata.Deinit();
      27             : 
      28        1967 :     m_osRootDirectoryName = osRootDirectoryName;
      29        1967 :     if (!m_osRootDirectoryName.empty() && m_osRootDirectoryName.back() == '/')
      30             :     {
      31           0 :         m_osRootDirectoryName.pop_back();
      32             :     }
      33        3934 :     m_poPAM = std::make_shared<GDALPamMultiDim>(
      34        5901 :         CPLFormFilenameSafe(m_osRootDirectoryName.c_str(), "pam", nullptr));
      35        1967 : }
      36             : 
      37             : /************************************************************************/
      38             : /*                     ZarrSharedResource::Create()                     */
      39             : /************************************************************************/
      40             : 
      41             : std::shared_ptr<ZarrSharedResource>
      42        1967 : ZarrSharedResource::Create(const std::string &osRootDirectoryName,
      43             :                            bool bUpdatable)
      44             : {
      45             :     return std::shared_ptr<ZarrSharedResource>(
      46        1967 :         new ZarrSharedResource(osRootDirectoryName, bUpdatable));
      47             : }
      48             : 
      49             : /************************************************************************/
      50             : /*              ZarrSharedResource::~ZarrSharedResource()               */
      51             : /************************************************************************/
      52             : 
      53        1967 : ZarrSharedResource::~ZarrSharedResource()
      54             : {
      55             :     // We try to clean caches at dataset closing, especially for Parquet
      56             :     // references, since closing Parquet datasets when the virtual file
      57             :     // systems are destroyed can be too late and cause crashes.
      58        1967 :     VSIKerchunkFileSystemsCleanCache();
      59             : 
      60        1967 :     if (m_bConsolidatedMetadataModified)
      61             :     {
      62         336 :         if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::EXTERNAL)
      63             :         {
      64         190 :             CPLJSONDocument oDoc;
      65         190 :             oDoc.SetRoot(m_oObjConsolidatedMetadata);
      66         190 :             oDoc.Save(CPLFormFilenameSafe(m_osRootDirectoryName.c_str(),
      67             :                                           ".zmetadata", nullptr));
      68             :         }
      69         292 :         else if (m_eConsolidatedMetadataKind ==
      70         292 :                      ConsolidatedMetadataKind::INTERNAL &&
      71         146 :                  !cpl::starts_with(m_osRootDirectoryName, "/vsizip/"))
      72             :         {
      73         290 :             CPLJSONDocument oDoc;
      74             :             const std::string osFilename = CPLFormFilenameSafe(
      75         290 :                 m_osRootDirectoryName.c_str(), "zarr.json", nullptr);
      76         145 :             if (oDoc.Load(osFilename))
      77             :             {
      78         286 :                 oDoc.GetRoot().Set("consolidated_metadata",
      79         143 :                                    m_oObjConsolidatedMetadata);
      80         143 :                 oDoc.Save(osFilename);
      81             :             }
      82             :         }
      83             :     }
      84        1967 : }
      85             : 
      86             : /************************************************************************/
      87             : /*                 ZarrSharedResource::OpenRootGroup()                  */
      88             : /************************************************************************/
      89             : 
      90        1620 : std::shared_ptr<ZarrGroupBase> ZarrSharedResource::OpenRootGroup()
      91             : {
      92             :     // Probe zarr.json first so v3 datasets skip the v2 stat cascade.
      93             :     const std::string osZarrJsonFilename(CPLFormFilenameSafe(
      94        3240 :         m_osRootDirectoryName.c_str(), "zarr.json", nullptr));
      95             :     VSIStatBufL sStat;
      96             :     const bool bHasZarrJson =
      97        1620 :         (VSIStatL(osZarrJsonFilename.c_str(), &sStat) == 0);
      98             : 
      99        1620 :     if (!bHasZarrJson)
     100             :     {
     101             :         // Detect Kerchunk Parquet reference files before creating a v2
     102             :         // group.  Kerchunk .zmetadata has a "record_size" field that
     103             :         // regular consolidated metadata does not.
     104         639 :         if (!STARTS_WITH(m_osRootDirectoryName.c_str(), "/vsikerchunk"))
     105             :         {
     106             :             const std::string osZmetadataFilename(CPLFormFilenameSafe(
     107         602 :                 m_osRootDirectoryName.c_str(), ".zmetadata", nullptr));
     108         602 :             CPLJSONDocument oDoc;
     109             :             bool bOK;
     110             :             {
     111         602 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     112         602 :                 bOK = oDoc.Load(osZmetadataFilename);
     113             :             }
     114         602 :             if (bOK && oDoc.GetRoot().GetObj("record_size").IsValid())
     115             :             {
     116             :                 m_osKerchunkParquetPath =
     117             :                     CPLSPrintf("%s{%s}", PARQUET_REF_FS_PREFIX,
     118           5 :                                m_osRootDirectoryName.c_str());
     119           5 :                 CPLDebugOnly("ZARR", "Opening %s",
     120             :                              m_osKerchunkParquetPath.c_str());
     121           5 :                 return nullptr;
     122             :             }
     123             :         }
     124             : 
     125        1268 :         auto poRG = ZarrV2Group::Create(shared_from_this(), std::string(), "/");
     126             :         // Prevents potential recursion
     127         634 :         m_poWeakRootGroup = poRG;
     128         634 :         poRG->SetUpdatable(m_bUpdatable);
     129         634 :         poRG->SetDirectoryName(m_osRootDirectoryName);
     130             : 
     131             :         const std::string osZarrayFilename(CPLFormFilenameSafe(
     132         634 :             m_osRootDirectoryName.c_str(), ".zarray", nullptr));
     133         634 :         const auto nErrorCount = CPLGetErrorCounter();
     134         634 :         if (VSIStatL(osZarrayFilename.c_str(), &sStat) == 0)
     135             :         {
     136         662 :             CPLJSONDocument oDoc;
     137         331 :             if (!oDoc.Load(osZarrayFilename))
     138           0 :                 return nullptr;
     139         662 :             const auto oRoot = oDoc.GetRoot();
     140         331 :             if (oRoot["_NCZARR_ARRAY"].IsValid())
     141             :             {
     142             :                 // If opening a NCZarr array, initialize its group from NCZarr
     143             :                 // metadata.
     144             :                 const std::string osGroupFilename(CPLFormFilenameSafe(
     145           3 :                     CPLGetDirnameSafe(m_osRootDirectoryName.c_str()).c_str(),
     146           3 :                     ".zgroup", nullptr));
     147           3 :                 if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
     148             :                 {
     149           2 :                     CPLJSONDocument oDocGroup;
     150           2 :                     if (oDocGroup.Load(osGroupFilename))
     151             :                     {
     152           2 :                         if (!poRG->InitFromZGroup(oDocGroup.GetRoot()))
     153           1 :                             return nullptr;
     154             :                     }
     155             :                 }
     156             :             }
     157             :             const std::string osArrayName(
     158         660 :                 CPLGetBasenameSafe(m_osRootDirectoryName.c_str()));
     159             : 
     160         990 :             if (!poRG->LoadArray(osArrayName, osZarrayFilename, oRoot, false,
     161         990 :                                  CPLJSONObject()))
     162          39 :                 return nullptr;
     163             : 
     164         291 :             return poRG;
     165             :         }
     166         304 :         else if (CPLGetErrorCounter() > nErrorCount &&
     167           1 :                  strstr(CPLGetLastErrorMsg(),
     168             :                         "Generation of Kerchunk Parquet cache"))
     169             :         {
     170           0 :             return nullptr;
     171             :         }
     172             : 
     173             :         const std::string osZmetadataFilename(CPLFormFilenameSafe(
     174         303 :             m_osRootDirectoryName.c_str(), ".zmetadata", nullptr));
     175         606 :         if (CPLTestBool(CSLFetchNameValueDef(
     176         303 :                 GetOpenOptions(), "USE_CONSOLIDATED_METADATA",
     177         303 :                 CSLFetchNameValueDef(GetOpenOptions(), "USE_ZMETADATA",
     178         600 :                                      "YES"))) &&
     179         297 :             VSIStatL(osZmetadataFilename.c_str(), &sStat) == 0)
     180             :         {
     181         195 :             if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::NONE)
     182             :             {
     183         195 :                 CPLJSONDocument oDoc;
     184         195 :                 if (!oDoc.Load(osZmetadataFilename))
     185           0 :                     return nullptr;
     186             : 
     187         195 :                 m_eConsolidatedMetadataKind =
     188             :                     ConsolidatedMetadataKind::EXTERNAL;
     189         195 :                 m_oObjConsolidatedMetadata = oDoc.GetRoot();
     190             :             }
     191             : 
     192         195 :             poRG->InitFromConsolidatedMetadata(m_oObjConsolidatedMetadata);
     193             : 
     194         195 :             return poRG;
     195             :         }
     196             : 
     197             :         const std::string osGroupFilename(CPLFormFilenameSafe(
     198         108 :             m_osRootDirectoryName.c_str(), ".zgroup", nullptr));
     199         108 :         if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
     200             :         {
     201         196 :             CPLJSONDocument oDoc;
     202          98 :             if (!oDoc.Load(osGroupFilename))
     203           0 :                 return nullptr;
     204             : 
     205          98 :             if (!poRG->InitFromZGroup(oDoc.GetRoot()))
     206           3 :                 return nullptr;
     207          95 :             return poRG;
     208             :         }
     209             :     }
     210             : 
     211             :     // Zarr v3
     212        1982 :     auto poRG_V3 = ZarrV3Group::Create(shared_from_this(), std::string(), "/",
     213        3964 :                                        m_osRootDirectoryName);
     214             :     // Prevents potential recursion
     215         991 :     m_poWeakRootGroup = poRG_V3;
     216         991 :     poRG_V3->SetUpdatable(m_bUpdatable);
     217             : 
     218         991 :     if (bHasZarrJson)
     219             :     {
     220        1962 :         CPLJSONDocument oDoc;
     221         981 :         if (!oDoc.Load(osZarrJsonFilename))
     222           0 :             return nullptr;
     223        1962 :         const auto oRoot = oDoc.GetRoot();
     224         981 :         if (oRoot.GetInteger("zarr_format") != 3)
     225             :         {
     226           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     227             :                      "Unhandled zarr_format value");
     228           0 :             return nullptr;
     229             :         }
     230             : 
     231             :         // Not yet adopted, but described at https://github.com/zarr-developers/zarr-specs/pull/309/files
     232             :         // and used for example by
     233             :         // https://s3.explorer.eopf.copernicus.eu/esa-zarr-sentinel-explorer-fra/tests-output/sentinel-2-l2a/S2B_MSIL2A_20251218T110359_N0511_R094_T32VLK_20251218T115223.zarr/measurements/reflectance/zarr.json
     234             :         const auto oConsolidatedMetadata =
     235        2943 :             oRoot.GetObj("consolidated_metadata");
     236         181 :         if (oConsolidatedMetadata.GetType() == CPLJSONObject::Type::Object &&
     237        1162 :             oConsolidatedMetadata.GetString("kind") == "inline" &&
     238         362 :             CPLTestBool(CSLFetchNameValueDef(
     239         181 :                 GetOpenOptions(), "USE_CONSOLIDATED_METADATA",
     240         181 :                 CSLFetchNameValueDef(GetOpenOptions(), "USE_ZMETADATA",
     241             :                                      "YES"))))
     242             :         {
     243         181 :             if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::NONE)
     244             :             {
     245         169 :                 CPLDebug("JSON", "Using consolidated_metadata");
     246         169 :                 m_eConsolidatedMetadataKind =
     247             :                     ConsolidatedMetadataKind::INTERNAL;
     248         169 :                 m_oObjConsolidatedMetadata = oConsolidatedMetadata;
     249         169 :                 m_oRootAttributes = oRoot.GetObj("attributes");
     250             :             }
     251             : 
     252         181 :             poRG_V3->InitFromConsolidatedMetadata(m_oObjConsolidatedMetadata,
     253         181 :                                                   m_oRootAttributes);
     254             :         }
     255             : 
     256        2943 :         const std::string osNodeType = oRoot.GetString("node_type");
     257         981 :         if (osNodeType == "array")
     258             :         {
     259             :             const std::string osArrayName(
     260        1446 :                 CPLGetBasenameSafe(m_osRootDirectoryName.c_str()));
     261         723 :             poRG_V3->SetExplored();
     262         723 :             if (!poRG_V3->LoadArray(osArrayName, osZarrJsonFilename, oRoot))
     263          51 :                 return nullptr;
     264             : 
     265         672 :             return poRG_V3;
     266             :         }
     267         258 :         else if (osNodeType == "group")
     268             :         {
     269         258 :             return poRG_V3;
     270             :         }
     271             :         else
     272             :         {
     273           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unhandled node_type value");
     274           0 :             return nullptr;
     275             :         }
     276             :     }
     277             : 
     278             :     // No explicit zarr.json in root directory ? Then recurse until we find
     279             :     // one.
     280          10 :     auto psDir = VSIOpenDir(m_osRootDirectoryName.c_str(), -1, nullptr);
     281          10 :     if (!psDir)
     282           9 :         return nullptr;
     283           1 :     bool bZarrJsonFound = false;
     284           1 :     while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
     285             :     {
     286           0 :         if (!VSI_ISDIR(psEntry->nMode) &&
     287           0 :             strcmp(CPLGetFilename(psEntry->pszName), "zarr.json") == 0)
     288             :         {
     289           0 :             bZarrJsonFound = true;
     290           0 :             break;
     291             :         }
     292           0 :     }
     293           1 :     VSICloseDir(psDir);
     294           1 :     if (bZarrJsonFound)
     295           0 :         return poRG_V3;
     296             : 
     297           1 :     return nullptr;
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                  ZarrSharedResource::GetRootGroup()                  */
     302             : /************************************************************************/
     303             : 
     304        3559 : std::shared_ptr<ZarrGroupBase> ZarrSharedResource::GetRootGroup()
     305             : {
     306        3559 :     auto poRootGroup = m_poWeakRootGroup.lock();
     307        3559 :     if (poRootGroup)
     308        1939 :         return poRootGroup;
     309        1620 :     poRootGroup = OpenRootGroup();
     310        1620 :     m_poWeakRootGroup = poRootGroup;
     311        1620 :     return poRootGroup;
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*        ZarrSharedResource::InitConsolidatedMetadataIfNeeded()        */
     316             : /************************************************************************/
     317             : 
     318        1260 : void ZarrSharedResource::InitConsolidatedMetadataIfNeeded()
     319             : {
     320        1260 :     if (!m_oObjConsolidatedMetadata.IsValid())
     321             :     {
     322         326 :         m_oObjConsolidatedMetadata = CPLJSONObject();
     323         326 :         if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::EXTERNAL)
     324             :         {
     325         173 :             m_oObjConsolidatedMetadata.Add("zarr_consolidated_format", 1);
     326         173 :             m_oObjConsolidatedMetadata.Add("metadata", CPLJSONObject());
     327             :         }
     328             :         else
     329             :         {
     330         153 :             m_oObjConsolidatedMetadata.Add("kind", "inline");
     331         153 :             m_oObjConsolidatedMetadata.Add("must_understand", false);
     332         153 :             m_oObjConsolidatedMetadata.Add("metadata", CPLJSONObject());
     333             :         }
     334             :     }
     335        1260 : }
     336             : 
     337             : /************************************************************************/
     338             : /*                ZarrSharedResource::SetZMetadataItem()                */
     339             : /************************************************************************/
     340             : 
     341        1542 : void ZarrSharedResource::SetZMetadataItem(const std::string &osFilename,
     342             :                                           const CPLJSONObject &obj)
     343             : {
     344        1542 :     if (m_eConsolidatedMetadataKind != ConsolidatedMetadataKind::NONE)
     345             :     {
     346        1250 :         InitConsolidatedMetadataIfNeeded();
     347             : 
     348        1250 :         CPLString osNormalizedFilename(osFilename);
     349        1250 :         osNormalizedFilename.replaceAll('\\', '/');
     350        1250 :         CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
     351             :                               (m_osRootDirectoryName + '/').c_str()));
     352             : 
     353        1250 :         if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::INTERNAL)
     354             :         {
     355         370 :             const auto nPos = osNormalizedFilename.rfind('/');
     356         370 :             if (nPos == std::string::npos)
     357           0 :                 return;
     358         370 :             osNormalizedFilename.resize(nPos);
     359             :         }
     360             : 
     361             :         const char *pszKey =
     362        1250 :             osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
     363        1250 :         if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::EXTERNAL ||
     364         370 :             strcmp(pszKey, "zarr.json") != 0)
     365             :         {
     366        1079 :             m_bConsolidatedMetadataModified = true;
     367        2158 :             auto oMetadata = m_oObjConsolidatedMetadata["metadata"];
     368        1079 :             oMetadata.DeleteNoSplitName(pszKey);
     369        1079 :             oMetadata.AddNoSplitName(pszKey, obj);
     370             :         }
     371             :     }
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*          ZarrSharedResource::DeleteZMetadataItemRecursive()          */
     376             : /************************************************************************/
     377             : 
     378          14 : void ZarrSharedResource::DeleteZMetadataItemRecursive(
     379             :     const std::string &osFilename)
     380             : {
     381          14 :     if (m_eConsolidatedMetadataKind != ConsolidatedMetadataKind::NONE)
     382             :     {
     383           6 :         InitConsolidatedMetadataIfNeeded();
     384             : 
     385           6 :         CPLString osNormalizedFilename(osFilename);
     386           6 :         osNormalizedFilename.replaceAll('\\', '/');
     387           6 :         CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
     388             :                               (m_osRootDirectoryName + '/').c_str()));
     389             : 
     390           6 :         if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::INTERNAL)
     391             :         {
     392           2 :             const auto nPos = osNormalizedFilename.rfind('/');
     393           2 :             if (nPos == std::string::npos)
     394           0 :                 return;
     395           2 :             osNormalizedFilename.resize(nPos);
     396             :         }
     397             : 
     398             :         const char *pszKey =
     399           6 :             osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
     400           6 :         if (m_eConsolidatedMetadataKind == ConsolidatedMetadataKind::EXTERNAL ||
     401           2 :             strcmp(pszKey, "zarr.json") != 0)
     402             :         {
     403           6 :             m_bConsolidatedMetadataModified = true;
     404          18 :             auto oMetadata = m_oObjConsolidatedMetadata["metadata"];
     405          34 :             for (auto &item : oMetadata.GetChildren())
     406             :             {
     407          28 :                 if (STARTS_WITH(item.GetName().c_str(), pszKey))
     408             :                 {
     409          17 :                     oMetadata.DeleteNoSplitName(item.GetName());
     410             :                 }
     411             :             }
     412             :         }
     413             :     }
     414             : }
     415             : 
     416             : /************************************************************************/
     417             : /*            ZarrSharedResource::RenameZMetadataRecursive()            */
     418             : /************************************************************************/
     419             : 
     420          12 : void ZarrSharedResource::RenameZMetadataRecursive(
     421             :     const std::string &osOldFilename, const std::string &osNewFilename)
     422             : {
     423          12 :     if (m_eConsolidatedMetadataKind != ConsolidatedMetadataKind::NONE)
     424             :     {
     425           4 :         InitConsolidatedMetadataIfNeeded();
     426             : 
     427           8 :         CPLString osNormalizedOldFilename(osOldFilename);
     428           4 :         osNormalizedOldFilename.replaceAll('\\', '/');
     429           4 :         CPLAssert(STARTS_WITH(osNormalizedOldFilename.c_str(),
     430             :                               (m_osRootDirectoryName + '/').c_str()));
     431             : 
     432           8 :         CPLString osNormalizedNewFilename(osNewFilename);
     433           4 :         osNormalizedNewFilename.replaceAll('\\', '/');
     434           4 :         CPLAssert(STARTS_WITH(osNormalizedNewFilename.c_str(),
     435             :                               (m_osRootDirectoryName + '/').c_str()));
     436             : 
     437           4 :         m_bConsolidatedMetadataModified = true;
     438             : 
     439             :         const char *pszOldKeyRadix =
     440           4 :             osNormalizedOldFilename.c_str() + m_osRootDirectoryName.size() + 1;
     441             :         const char *pszNewKeyRadix =
     442           4 :             osNormalizedNewFilename.c_str() + m_osRootDirectoryName.size() + 1;
     443             : 
     444          12 :         auto oMetadata = m_oObjConsolidatedMetadata["metadata"];
     445          28 :         for (auto &item : oMetadata.GetChildren())
     446             :         {
     447          24 :             if (STARTS_WITH(item.GetName().c_str(), pszOldKeyRadix))
     448             :             {
     449          11 :                 oMetadata.DeleteNoSplitName(item.GetName());
     450          22 :                 std::string osNewKey(pszNewKeyRadix);
     451          11 :                 osNewKey += (item.GetName().c_str() + strlen(pszOldKeyRadix));
     452          11 :                 oMetadata.AddNoSplitName(osNewKey, item);
     453             :             }
     454             :         }
     455             :     }
     456          12 : }
     457             : 
     458             : /************************************************************************/
     459             : /*              ZarrSharedResource::UpdateDimensionSize()               */
     460             : /************************************************************************/
     461             : 
     462           7 : void ZarrSharedResource::UpdateDimensionSize(
     463             :     const std::shared_ptr<GDALDimension> &poDim)
     464             : {
     465          14 :     auto poRG = m_poWeakRootGroup.lock();
     466           7 :     if (!poRG)
     467           0 :         poRG = OpenRootGroup();
     468           7 :     if (poRG)
     469             :     {
     470           7 :         poRG->UpdateDimensionSize(poDim);
     471             :     }
     472             :     else
     473             :     {
     474           0 :         CPLError(CE_Failure, CPLE_AppDefined, "UpdateDimensionSize() failed");
     475             :     }
     476           7 :     poRG.reset();
     477           7 : }
     478             : 
     479             : /************************************************************************/
     480             : /*               ZarrSharedResource::AddArrayInLoading()                */
     481             : /************************************************************************/
     482             : 
     483        1975 : bool ZarrSharedResource::AddArrayInLoading(const std::string &osZarrayFilename)
     484             : {
     485             :     // Prevent too deep or recursive array loading
     486        1975 :     if (m_oSetArrayInLoading.find(osZarrayFilename) !=
     487        3950 :         m_oSetArrayInLoading.end())
     488             :     {
     489           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     490             :                  "Attempt at recursively loading %s", osZarrayFilename.c_str());
     491           1 :         return false;
     492             :     }
     493        1974 :     if (m_oSetArrayInLoading.size() == 32)
     494             :     {
     495           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     496             :                  "Too deep call stack in LoadArray()");
     497           1 :         return false;
     498             :     }
     499        1973 :     m_oSetArrayInLoading.insert(osZarrayFilename);
     500        1973 :     return true;
     501             : }
     502             : 
     503             : /************************************************************************/
     504             : /*              ZarrSharedResource::RemoveArrayInLoading()              */
     505             : /************************************************************************/
     506             : 
     507        1973 : void ZarrSharedResource::RemoveArrayInLoading(
     508             :     const std::string &osZarrayFilename)
     509             : {
     510        1973 :     m_oSetArrayInLoading.erase(osZarrayFilename);
     511        1973 : }

Generated by: LCOV version 1.14