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-05-13 13:33:37 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "zarr.h"
      30             : 
      31             : #include "cpl_json.h"
      32             : 
      33             : /************************************************************************/
      34             : /*              ZarrSharedResource::ZarrSharedResource()                */
      35             : /************************************************************************/
      36             : 
      37         979 : ZarrSharedResource::ZarrSharedResource(const std::string &osRootDirectoryName,
      38         979 :                                        bool bUpdatable)
      39         979 :     : m_bUpdatable(bUpdatable)
      40             : {
      41         979 :     m_oObj.Add("zarr_consolidated_format", 1);
      42         979 :     m_oObj.Add("metadata", CPLJSONObject());
      43             : 
      44         979 :     m_osRootDirectoryName = osRootDirectoryName;
      45         979 :     if (!m_osRootDirectoryName.empty() && m_osRootDirectoryName.back() == '/')
      46             :     {
      47           0 :         m_osRootDirectoryName.resize(m_osRootDirectoryName.size() - 1);
      48             :     }
      49         979 :     m_poPAM = std::make_shared<GDALPamMultiDim>(
      50         979 :         CPLFormFilename(m_osRootDirectoryName.c_str(), "pam", nullptr));
      51         979 : }
      52             : 
      53             : /************************************************************************/
      54             : /*              ZarrSharedResource::Create()                            */
      55             : /************************************************************************/
      56             : 
      57             : std::shared_ptr<ZarrSharedResource>
      58         979 : ZarrSharedResource::Create(const std::string &osRootDirectoryName,
      59             :                            bool bUpdatable)
      60             : {
      61             :     return std::shared_ptr<ZarrSharedResource>(
      62         979 :         new ZarrSharedResource(osRootDirectoryName, bUpdatable));
      63             : }
      64             : 
      65             : /************************************************************************/
      66             : /*              ZarrSharedResource::~ZarrSharedResource()               */
      67             : /************************************************************************/
      68             : 
      69         979 : ZarrSharedResource::~ZarrSharedResource()
      70             : {
      71         979 :     if (m_bZMetadataModified)
      72             :     {
      73         174 :         CPLJSONDocument oDoc;
      74         174 :         oDoc.SetRoot(m_oObj);
      75         174 :         oDoc.Save(CPLFormFilename(m_osRootDirectoryName.c_str(), ".zmetadata",
      76             :                                   nullptr));
      77             :     }
      78         979 : }
      79             : 
      80             : /************************************************************************/
      81             : /*             ZarrSharedResource::OpenRootGroup()                      */
      82             : /************************************************************************/
      83             : 
      84         676 : std::shared_ptr<ZarrGroupBase> ZarrSharedResource::OpenRootGroup()
      85             : {
      86             :     {
      87        1352 :         auto poRG = ZarrV2Group::Create(shared_from_this(), std::string(), "/");
      88         676 :         poRG->SetUpdatable(m_bUpdatable);
      89         676 :         poRG->SetDirectoryName(m_osRootDirectoryName);
      90             : 
      91             :         const std::string osZarrayFilename(
      92         676 :             CPLFormFilename(m_osRootDirectoryName.c_str(), ".zarray", nullptr));
      93             :         VSIStatBufL sStat;
      94         676 :         if (VSIStatL(osZarrayFilename.c_str(), &sStat) == 0)
      95             :         {
      96         494 :             CPLJSONDocument oDoc;
      97         247 :             if (!oDoc.Load(osZarrayFilename))
      98           0 :                 return nullptr;
      99         494 :             const auto oRoot = oDoc.GetRoot();
     100         247 :             if (oRoot["_NCZARR_ARRAY"].IsValid())
     101             :             {
     102             :                 // If opening a NCZarr array, initialize its group from NCZarr
     103             :                 // metadata.
     104             :                 const std::string osGroupFilename(CPLFormFilename(
     105             :                     CPLGetDirname(m_osRootDirectoryName.c_str()), ".zgroup",
     106           3 :                     nullptr));
     107           3 :                 if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
     108             :                 {
     109           2 :                     CPLJSONDocument oDocGroup;
     110           2 :                     if (oDocGroup.Load(osGroupFilename))
     111             :                     {
     112           2 :                         if (!poRG->InitFromZGroup(oDocGroup.GetRoot()))
     113           1 :                             return nullptr;
     114             :                     }
     115             :                 }
     116             :             }
     117             :             const std::string osArrayName(
     118         492 :                 CPLGetBasename(m_osRootDirectoryName.c_str()));
     119         738 :             if (!poRG->LoadArray(osArrayName, osZarrayFilename, oRoot, false,
     120         738 :                                  CPLJSONObject()))
     121          39 :                 return nullptr;
     122             : 
     123         207 :             return poRG;
     124             :         }
     125             : 
     126             :         const std::string osZmetadataFilename(CPLFormFilename(
     127         429 :             m_osRootDirectoryName.c_str(), ".zmetadata", nullptr));
     128         429 :         if (CPLTestBool(CSLFetchNameValueDef(GetOpenOptions(), "USE_ZMETADATA",
     129         852 :                                              "YES")) &&
     130         423 :             VSIStatL(osZmetadataFilename.c_str(), &sStat) == 0)
     131             :         {
     132         165 :             if (!m_bZMetadataEnabled)
     133             :             {
     134         165 :                 CPLJSONDocument oDoc;
     135         165 :                 if (!oDoc.Load(osZmetadataFilename))
     136           0 :                     return nullptr;
     137             : 
     138         165 :                 m_bZMetadataEnabled = true;
     139         165 :                 m_oObj = oDoc.GetRoot();
     140             :             }
     141         165 :             poRG->InitFromZMetadata(m_oObj);
     142             : 
     143         165 :             return poRG;
     144             :         }
     145             : 
     146             :         const std::string osGroupFilename(
     147         264 :             CPLFormFilename(m_osRootDirectoryName.c_str(), ".zgroup", nullptr));
     148         264 :         if (VSIStatL(osGroupFilename.c_str(), &sStat) == 0)
     149             :         {
     150         110 :             CPLJSONDocument oDoc;
     151          55 :             if (!oDoc.Load(osGroupFilename))
     152           0 :                 return nullptr;
     153             : 
     154          55 :             if (!poRG->InitFromZGroup(oDoc.GetRoot()))
     155           3 :                 return nullptr;
     156          52 :             return poRG;
     157             :         }
     158             :     }
     159             : 
     160             :     // Zarr v3
     161         418 :     auto poRG_V3 = ZarrV3Group::Create(shared_from_this(), std::string(), "/",
     162         836 :                                        m_osRootDirectoryName);
     163         209 :     poRG_V3->SetUpdatable(m_bUpdatable);
     164             : 
     165             :     const std::string osZarrJsonFilename(
     166         418 :         CPLFormFilename(m_osRootDirectoryName.c_str(), "zarr.json", nullptr));
     167             :     VSIStatBufL sStat;
     168         209 :     if (VSIStatL(osZarrJsonFilename.c_str(), &sStat) == 0)
     169             :     {
     170         414 :         CPLJSONDocument oDoc;
     171         207 :         if (!oDoc.Load(osZarrJsonFilename))
     172           0 :             return nullptr;
     173         414 :         const auto oRoot = oDoc.GetRoot();
     174         207 :         if (oRoot.GetInteger("zarr_format") != 3)
     175             :         {
     176           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     177             :                      "Unhandled zarr_format value");
     178           0 :             return nullptr;
     179             :         }
     180         621 :         const std::string osNodeType = oRoot.GetString("node_type");
     181         207 :         if (osNodeType == "array")
     182             :         {
     183             :             const std::string osArrayName(
     184         106 :                 CPLGetBasename(m_osRootDirectoryName.c_str()));
     185          53 :             poRG_V3->SetExplored();
     186          53 :             if (!poRG_V3->LoadArray(osArrayName, osZarrJsonFilename, oRoot))
     187          32 :                 return nullptr;
     188             : 
     189          21 :             return poRG_V3;
     190             :         }
     191         154 :         else if (osNodeType == "group")
     192             :         {
     193         154 :             return poRG_V3;
     194             :         }
     195             :         else
     196             :         {
     197           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unhandled node_type value");
     198           0 :             return nullptr;
     199             :         }
     200             :     }
     201             : 
     202             :     // No explicit zarr.json in root directory ? Then recurse until we find
     203             :     // one.
     204           2 :     auto psDir = VSIOpenDir(m_osRootDirectoryName.c_str(), -1, nullptr);
     205           2 :     if (!psDir)
     206           2 :         return nullptr;
     207           0 :     bool bZarrJsonFound = false;
     208           0 :     while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
     209             :     {
     210           0 :         if (!VSI_ISDIR(psEntry->nMode) &&
     211           0 :             strcmp(CPLGetFilename(psEntry->pszName), "zarr.json") == 0)
     212             :         {
     213           0 :             bZarrJsonFound = true;
     214           0 :             break;
     215             :         }
     216           0 :     }
     217           0 :     VSICloseDir(psDir);
     218           0 :     if (bZarrJsonFound)
     219           0 :         return poRG_V3;
     220             : 
     221           0 :     return nullptr;
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*             ZarrSharedResource::SetZMetadataItem()                   */
     226             : /************************************************************************/
     227             : 
     228         919 : void ZarrSharedResource::SetZMetadataItem(const std::string &osFilename,
     229             :                                           const CPLJSONObject &obj)
     230             : {
     231         919 :     if (m_bZMetadataEnabled)
     232             :     {
     233        1600 :         CPLString osNormalizedFilename(osFilename);
     234         800 :         osNormalizedFilename.replaceAll('\\', '/');
     235         800 :         CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
     236             :                               (m_osRootDirectoryName + '/').c_str()));
     237         800 :         m_bZMetadataModified = true;
     238             :         const char *pszKey =
     239         800 :             osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
     240        1600 :         auto oMetadata = m_oObj["metadata"];
     241         800 :         oMetadata.DeleteNoSplitName(pszKey);
     242         800 :         oMetadata.AddNoSplitName(pszKey, obj);
     243             :     }
     244         919 : }
     245             : 
     246             : /************************************************************************/
     247             : /*         ZarrSharedResource::DeleteZMetadataItemRecursive()           */
     248             : /************************************************************************/
     249             : 
     250          12 : void ZarrSharedResource::DeleteZMetadataItemRecursive(
     251             :     const std::string &osFilename)
     252             : {
     253          12 :     if (m_bZMetadataEnabled)
     254             :     {
     255           8 :         CPLString osNormalizedFilename(osFilename);
     256           4 :         osNormalizedFilename.replaceAll('\\', '/');
     257           4 :         CPLAssert(STARTS_WITH(osNormalizedFilename.c_str(),
     258             :                               (m_osRootDirectoryName + '/').c_str()));
     259           4 :         m_bZMetadataModified = true;
     260             :         const char *pszKey =
     261           4 :             osNormalizedFilename.c_str() + m_osRootDirectoryName.size() + 1;
     262             : 
     263          12 :         auto oMetadata = m_oObj["metadata"];
     264          28 :         for (auto &item : oMetadata.GetChildren())
     265             :         {
     266          24 :             if (STARTS_WITH(item.GetName().c_str(), pszKey))
     267             :             {
     268          14 :                 oMetadata.DeleteNoSplitName(item.GetName());
     269             :             }
     270             :         }
     271             :     }
     272          12 : }
     273             : 
     274             : /************************************************************************/
     275             : /*             ZarrSharedResource::RenameZMetadataRecursive()           */
     276             : /************************************************************************/
     277             : 
     278          12 : void ZarrSharedResource::RenameZMetadataRecursive(
     279             :     const std::string &osOldFilename, const std::string &osNewFilename)
     280             : {
     281          12 :     if (m_bZMetadataEnabled)
     282             :     {
     283           8 :         CPLString osNormalizedOldFilename(osOldFilename);
     284           4 :         osNormalizedOldFilename.replaceAll('\\', '/');
     285           4 :         CPLAssert(STARTS_WITH(osNormalizedOldFilename.c_str(),
     286             :                               (m_osRootDirectoryName + '/').c_str()));
     287             : 
     288           8 :         CPLString osNormalizedNewFilename(osNewFilename);
     289           4 :         osNormalizedNewFilename.replaceAll('\\', '/');
     290           4 :         CPLAssert(STARTS_WITH(osNormalizedNewFilename.c_str(),
     291             :                               (m_osRootDirectoryName + '/').c_str()));
     292             : 
     293           4 :         m_bZMetadataModified = true;
     294             : 
     295             :         const char *pszOldKeyRadix =
     296           4 :             osNormalizedOldFilename.c_str() + m_osRootDirectoryName.size() + 1;
     297             :         const char *pszNewKeyRadix =
     298           4 :             osNormalizedNewFilename.c_str() + m_osRootDirectoryName.size() + 1;
     299             : 
     300          12 :         auto oMetadata = m_oObj["metadata"];
     301          32 :         for (auto &item : oMetadata.GetChildren())
     302             :         {
     303          28 :             if (STARTS_WITH(item.GetName().c_str(), pszOldKeyRadix))
     304             :             {
     305          15 :                 oMetadata.DeleteNoSplitName(item.GetName());
     306          30 :                 std::string osNewKey(pszNewKeyRadix);
     307          15 :                 osNewKey += (item.GetName().c_str() + strlen(pszOldKeyRadix));
     308          15 :                 oMetadata.AddNoSplitName(osNewKey, item);
     309             :             }
     310             :         }
     311             :     }
     312          12 : }
     313             : 
     314             : /************************************************************************/
     315             : /*             ZarrSharedResource::UpdateDimensionSize()                */
     316             : /************************************************************************/
     317             : 
     318           7 : void ZarrSharedResource::UpdateDimensionSize(
     319             :     const std::shared_ptr<GDALDimension> &poDim)
     320             : {
     321          14 :     auto poRG = m_poWeakRootGroup.lock();
     322           7 :     if (!poRG)
     323           0 :         poRG = OpenRootGroup();
     324           7 :     if (poRG)
     325             :     {
     326           7 :         poRG->UpdateDimensionSize(poDim);
     327             :     }
     328             :     else
     329             :     {
     330           0 :         CPLError(CE_Failure, CPLE_AppDefined, "UpdateDimensionSize() failed");
     331             :     }
     332           7 :     poRG.reset();
     333           7 : }
     334             : 
     335             : /************************************************************************/
     336             : /*             ZarrSharedResource::AddArrayInLoading()                  */
     337             : /************************************************************************/
     338             : 
     339         801 : bool ZarrSharedResource::AddArrayInLoading(const std::string &osZarrayFilename)
     340             : {
     341             :     // Prevent too deep or recursive array loading
     342         801 :     if (m_oSetArrayInLoading.find(osZarrayFilename) !=
     343        1602 :         m_oSetArrayInLoading.end())
     344             :     {
     345           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     346             :                  "Attempt at recursively loading %s", osZarrayFilename.c_str());
     347           1 :         return false;
     348             :     }
     349         800 :     if (m_oSetArrayInLoading.size() == 32)
     350             :     {
     351           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     352             :                  "Too deep call stack in LoadArray()");
     353           1 :         return false;
     354             :     }
     355         799 :     m_oSetArrayInLoading.insert(osZarrayFilename);
     356         799 :     return true;
     357             : }
     358             : 
     359             : /************************************************************************/
     360             : /*             ZarrSharedResource::RemoveArrayInLoading()               */
     361             : /************************************************************************/
     362             : 
     363         799 : void ZarrSharedResource::RemoveArrayInLoading(
     364             :     const std::string &osZarrayFilename)
     365             : {
     366         799 :     m_oSetArrayInLoading.erase(osZarrayFilename);
     367         799 : }

Generated by: LCOV version 1.14