LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_sharedresource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 140 162 86.4 %
Date: 2024-11-21 22:18:42 Functions: 10 10 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             : 
      15             : #include "cpl_json.h"
      16             : 
      17             : /************************************************************************/
      18             : /*              ZarrSharedResource::ZarrSharedResource()                */
      19             : /************************************************************************/
      20             : 
      21         985 : ZarrSharedResource::ZarrSharedResource(const std::string &osRootDirectoryName,
      22         985 :                                        bool bUpdatable)
      23         985 :     : m_bUpdatable(bUpdatable)
      24             : {
      25         985 :     m_oObj.Add("zarr_consolidated_format", 1);
      26         985 :     m_oObj.Add("metadata", CPLJSONObject());
      27             : 
      28         985 :     m_osRootDirectoryName = osRootDirectoryName;
      29         985 :     if (!m_osRootDirectoryName.empty() && m_osRootDirectoryName.back() == '/')
      30             :     {
      31           0 :         m_osRootDirectoryName.pop_back();
      32             :     }
      33         985 :     m_poPAM = std::make_shared<GDALPamMultiDim>(
      34         985 :         CPLFormFilename(m_osRootDirectoryName.c_str(), "pam", nullptr));
      35         985 : }
      36             : 
      37             : /************************************************************************/
      38             : /*              ZarrSharedResource::Create()                            */
      39             : /************************************************************************/
      40             : 
      41             : std::shared_ptr<ZarrSharedResource>
      42         985 : ZarrSharedResource::Create(const std::string &osRootDirectoryName,
      43             :                            bool bUpdatable)
      44             : {
      45             :     return std::shared_ptr<ZarrSharedResource>(
      46         985 :         new ZarrSharedResource(osRootDirectoryName, bUpdatable));
      47             : }
      48             : 
      49             : /************************************************************************/
      50             : /*              ZarrSharedResource::~ZarrSharedResource()               */
      51             : /************************************************************************/
      52             : 
      53         985 : ZarrSharedResource::~ZarrSharedResource()
      54             : {
      55         985 :     if (m_bZMetadataModified)
      56             :     {
      57         178 :         CPLJSONDocument oDoc;
      58         178 :         oDoc.SetRoot(m_oObj);
      59         178 :         oDoc.Save(CPLFormFilename(m_osRootDirectoryName.c_str(), ".zmetadata",
      60             :                                   nullptr));
      61             :     }
      62         985 : }
      63             : 
      64             : /************************************************************************/
      65             : /*             ZarrSharedResource::OpenRootGroup()                      */
      66             : /************************************************************************/
      67             : 
      68         677 : std::shared_ptr<ZarrGroupBase> ZarrSharedResource::OpenRootGroup()
      69             : {
      70             :     {
      71        1354 :         auto poRG = ZarrV2Group::Create(shared_from_this(), std::string(), "/");
      72         677 :         poRG->SetUpdatable(m_bUpdatable);
      73         677 :         poRG->SetDirectoryName(m_osRootDirectoryName);
      74             : 
      75             :         const std::string osZarrayFilename(
      76         677 :             CPLFormFilename(m_osRootDirectoryName.c_str(), ".zarray", nullptr));
      77             :         VSIStatBufL sStat;
      78         677 :         if (VSIStatL(osZarrayFilename.c_str(), &sStat) == 0)
      79             :         {
      80         494 :             CPLJSONDocument oDoc;
      81         247 :             if (!oDoc.Load(osZarrayFilename))
      82           0 :                 return nullptr;
      83         494 :             const auto oRoot = oDoc.GetRoot();
      84         247 :             if (oRoot["_NCZARR_ARRAY"].IsValid())
      85             :             {
      86             :                 // If opening a NCZarr array, initialize its group from NCZarr
      87             :                 // metadata.
      88             :                 const std::string osGroupFilename(CPLFormFilename(
      89             :                     CPLGetDirname(m_osRootDirectoryName.c_str()), ".zgroup",
      90           3 :                     nullptr));
      91           3 :                 if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
      92             :                 {
      93           2 :                     CPLJSONDocument oDocGroup;
      94           2 :                     if (oDocGroup.Load(osGroupFilename))
      95             :                     {
      96           2 :                         if (!poRG->InitFromZGroup(oDocGroup.GetRoot()))
      97           1 :                             return nullptr;
      98             :                     }
      99             :                 }
     100             :             }
     101             :             const std::string osArrayName(
     102         492 :                 CPLGetBasename(m_osRootDirectoryName.c_str()));
     103         738 :             if (!poRG->LoadArray(osArrayName, osZarrayFilename, oRoot, false,
     104         738 :                                  CPLJSONObject()))
     105          39 :                 return nullptr;
     106             : 
     107         207 :             return poRG;
     108             :         }
     109             : 
     110             :         const std::string osZmetadataFilename(CPLFormFilename(
     111         430 :             m_osRootDirectoryName.c_str(), ".zmetadata", nullptr));
     112         430 :         if (CPLTestBool(CSLFetchNameValueDef(GetOpenOptions(), "USE_ZMETADATA",
     113         854 :                                              "YES")) &&
     114         424 :             VSIStatL(osZmetadataFilename.c_str(), &sStat) == 0)
     115             :         {
     116         166 :             if (!m_bZMetadataEnabled)
     117             :             {
     118         166 :                 CPLJSONDocument oDoc;
     119         166 :                 if (!oDoc.Load(osZmetadataFilename))
     120           0 :                     return nullptr;
     121             : 
     122         166 :                 m_bZMetadataEnabled = true;
     123         166 :                 m_oObj = oDoc.GetRoot();
     124             :             }
     125         166 :             poRG->InitFromZMetadata(m_oObj);
     126             : 
     127         166 :             return poRG;
     128             :         }
     129             : 
     130             :         const std::string osGroupFilename(
     131         264 :             CPLFormFilename(m_osRootDirectoryName.c_str(), ".zgroup", nullptr));
     132         264 :         if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
     133             :         {
     134         110 :             CPLJSONDocument oDoc;
     135          55 :             if (!oDoc.Load(osGroupFilename))
     136           0 :                 return nullptr;
     137             : 
     138          55 :             if (!poRG->InitFromZGroup(oDoc.GetRoot()))
     139           3 :                 return nullptr;
     140          52 :             return poRG;
     141             :         }
     142             :     }
     143             : 
     144             :     // Zarr v3
     145         418 :     auto poRG_V3 = ZarrV3Group::Create(shared_from_this(), std::string(), "/",
     146         836 :                                        m_osRootDirectoryName);
     147         209 :     poRG_V3->SetUpdatable(m_bUpdatable);
     148             : 
     149             :     const std::string osZarrJsonFilename(
     150         418 :         CPLFormFilename(m_osRootDirectoryName.c_str(), "zarr.json", nullptr));
     151             :     VSIStatBufL sStat;
     152         209 :     if (VSIStatL(osZarrJsonFilename.c_str(), &sStat) == 0)
     153             :     {
     154         414 :         CPLJSONDocument oDoc;
     155         207 :         if (!oDoc.Load(osZarrJsonFilename))
     156           0 :             return nullptr;
     157         414 :         const auto oRoot = oDoc.GetRoot();
     158         207 :         if (oRoot.GetInteger("zarr_format") != 3)
     159             :         {
     160           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     161             :                      "Unhandled zarr_format value");
     162           0 :             return nullptr;
     163             :         }
     164         621 :         const std::string osNodeType = oRoot.GetString("node_type");
     165         207 :         if (osNodeType == "array")
     166             :         {
     167             :             const std::string osArrayName(
     168         106 :                 CPLGetBasename(m_osRootDirectoryName.c_str()));
     169          53 :             poRG_V3->SetExplored();
     170          53 :             if (!poRG_V3->LoadArray(osArrayName, osZarrJsonFilename, oRoot))
     171          32 :                 return nullptr;
     172             : 
     173          21 :             return poRG_V3;
     174             :         }
     175         154 :         else if (osNodeType == "group")
     176             :         {
     177         154 :             return poRG_V3;
     178             :         }
     179             :         else
     180             :         {
     181           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unhandled node_type value");
     182           0 :             return nullptr;
     183             :         }
     184             :     }
     185             : 
     186             :     // No explicit zarr.json in root directory ? Then recurse until we find
     187             :     // one.
     188           2 :     auto psDir = VSIOpenDir(m_osRootDirectoryName.c_str(), -1, nullptr);
     189           2 :     if (!psDir)
     190           2 :         return nullptr;
     191           0 :     bool bZarrJsonFound = false;
     192           0 :     while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
     193             :     {
     194           0 :         if (!VSI_ISDIR(psEntry->nMode) &&
     195           0 :             strcmp(CPLGetFilename(psEntry->pszName), "zarr.json") == 0)
     196             :         {
     197           0 :             bZarrJsonFound = true;
     198           0 :             break;
     199             :         }
     200           0 :     }
     201           0 :     VSICloseDir(psDir);
     202           0 :     if (bZarrJsonFound)
     203           0 :         return poRG_V3;
     204             : 
     205           0 :     return nullptr;
     206             : }
     207             : 
     208             : /************************************************************************/
     209             : /*             ZarrSharedResource::SetZMetadataItem()                   */
     210             : /************************************************************************/
     211             : 
     212         944 : void ZarrSharedResource::SetZMetadataItem(const std::string &osFilename,
     213             :                                           const CPLJSONObject &obj)
     214             : {
     215         944 :     if (m_bZMetadataEnabled)
     216             :     {
     217        1650 :         CPLString osNormalizedFilename(osFilename);
     218         825 :         osNormalizedFilename.replaceAll('\\', '/');
     219         825 :         CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
     220             :                               (m_osRootDirectoryName + '/').c_str()));
     221         825 :         m_bZMetadataModified = true;
     222             :         const char *pszKey =
     223         825 :             osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
     224        1650 :         auto oMetadata = m_oObj["metadata"];
     225         825 :         oMetadata.DeleteNoSplitName(pszKey);
     226         825 :         oMetadata.AddNoSplitName(pszKey, obj);
     227             :     }
     228         944 : }
     229             : 
     230             : /************************************************************************/
     231             : /*         ZarrSharedResource::DeleteZMetadataItemRecursive()           */
     232             : /************************************************************************/
     233             : 
     234          12 : void ZarrSharedResource::DeleteZMetadataItemRecursive(
     235             :     const std::string &osFilename)
     236             : {
     237          12 :     if (m_bZMetadataEnabled)
     238             :     {
     239           8 :         CPLString osNormalizedFilename(osFilename);
     240           4 :         osNormalizedFilename.replaceAll('\\', '/');
     241           4 :         CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
     242             :                               (m_osRootDirectoryName + '/').c_str()));
     243           4 :         m_bZMetadataModified = true;
     244             :         const char *pszKey =
     245           4 :             osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
     246             : 
     247          12 :         auto oMetadata = m_oObj["metadata"];
     248          28 :         for (auto &item : oMetadata.GetChildren())
     249             :         {
     250          24 :             if (STARTS_WITH(item.GetName().c_str(), pszKey))
     251             :             {
     252          14 :                 oMetadata.DeleteNoSplitName(item.GetName());
     253             :             }
     254             :         }
     255             :     }
     256          12 : }
     257             : 
     258             : /************************************************************************/
     259             : /*             ZarrSharedResource::RenameZMetadataRecursive()           */
     260             : /************************************************************************/
     261             : 
     262          12 : void ZarrSharedResource::RenameZMetadataRecursive(
     263             :     const std::string &osOldFilename, const std::string &osNewFilename)
     264             : {
     265          12 :     if (m_bZMetadataEnabled)
     266             :     {
     267           8 :         CPLString osNormalizedOldFilename(osOldFilename);
     268           4 :         osNormalizedOldFilename.replaceAll('\\', '/');
     269           4 :         CPLAssert(STARTS_WITH(osNormalizedOldFilename.c_str(),
     270             :                               (m_osRootDirectoryName + '/').c_str()));
     271             : 
     272           8 :         CPLString osNormalizedNewFilename(osNewFilename);
     273           4 :         osNormalizedNewFilename.replaceAll('\\', '/');
     274           4 :         CPLAssert(STARTS_WITH(osNormalizedNewFilename.c_str(),
     275             :                               (m_osRootDirectoryName + '/').c_str()));
     276             : 
     277           4 :         m_bZMetadataModified = true;
     278             : 
     279             :         const char *pszOldKeyRadix =
     280           4 :             osNormalizedOldFilename.c_str() + m_osRootDirectoryName.size() + 1;
     281             :         const char *pszNewKeyRadix =
     282           4 :             osNormalizedNewFilename.c_str() + m_osRootDirectoryName.size() + 1;
     283             : 
     284          12 :         auto oMetadata = m_oObj["metadata"];
     285          32 :         for (auto &item : oMetadata.GetChildren())
     286             :         {
     287          28 :             if (STARTS_WITH(item.GetName().c_str(), pszOldKeyRadix))
     288             :             {
     289          15 :                 oMetadata.DeleteNoSplitName(item.GetName());
     290          30 :                 std::string osNewKey(pszNewKeyRadix);
     291          15 :                 osNewKey += (item.GetName().c_str() + strlen(pszOldKeyRadix));
     292          15 :                 oMetadata.AddNoSplitName(osNewKey, item);
     293             :             }
     294             :         }
     295             :     }
     296          12 : }
     297             : 
     298             : /************************************************************************/
     299             : /*             ZarrSharedResource::UpdateDimensionSize()                */
     300             : /************************************************************************/
     301             : 
     302           7 : void ZarrSharedResource::UpdateDimensionSize(
     303             :     const std::shared_ptr<GDALDimension> &poDim)
     304             : {
     305          14 :     auto poRG = m_poWeakRootGroup.lock();
     306           7 :     if (!poRG)
     307           0 :         poRG = OpenRootGroup();
     308           7 :     if (poRG)
     309             :     {
     310           7 :         poRG->UpdateDimensionSize(poDim);
     311             :     }
     312             :     else
     313             :     {
     314           0 :         CPLError(CE_Failure, CPLE_AppDefined, "UpdateDimensionSize() failed");
     315             :     }
     316           7 :     poRG.reset();
     317           7 : }
     318             : 
     319             : /************************************************************************/
     320             : /*             ZarrSharedResource::AddArrayInLoading()                  */
     321             : /************************************************************************/
     322             : 
     323         816 : bool ZarrSharedResource::AddArrayInLoading(const std::string &osZarrayFilename)
     324             : {
     325             :     // Prevent too deep or recursive array loading
     326         816 :     if (m_oSetArrayInLoading.find(osZarrayFilename) !=
     327        1632 :         m_oSetArrayInLoading.end())
     328             :     {
     329           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     330             :                  "Attempt at recursively loading %s", osZarrayFilename.c_str());
     331           1 :         return false;
     332             :     }
     333         815 :     if (m_oSetArrayInLoading.size() == 32)
     334             :     {
     335           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     336             :                  "Too deep call stack in LoadArray()");
     337           1 :         return false;
     338             :     }
     339         814 :     m_oSetArrayInLoading.insert(osZarrayFilename);
     340         814 :     return true;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*             ZarrSharedResource::RemoveArrayInLoading()               */
     345             : /************************************************************************/
     346             : 
     347         814 : void ZarrSharedResource::RemoveArrayInLoading(
     348             :     const std::string &osZarrayFilename)
     349             : {
     350         814 :     m_oSetArrayInLoading.erase(osZarrayFilename);
     351         814 : }

Generated by: LCOV version 1.14