LCOV - code coverage report
Current view: top level - frmts/icechunk - icechunkrepo.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 213 249 85.5 %
Date: 2026-07-24 18:27:47 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Icechunk driver
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "icechunkrepo.h"
      14             : #include "icechunkmanifest.h"
      15             : #include "icechunksnapshot.h"
      16             : #include "icechunkutils.h"
      17             : #include "icechunkdrivercore.h"
      18             : 
      19             : #include "cpl_json.h"
      20             : #include "cpl_mem_cache.h"
      21             : 
      22             : #include <cinttypes>
      23             : #include <limits>
      24             : #include <mutex>
      25             : 
      26             : /* ------------------------------------------------------------------------- */
      27             : 
      28             : #if defined(__GNUC__)
      29             : #pragma GCC diagnostic push
      30             : #pragma GCC diagnostic ignored "-Weffc++"
      31             : #endif
      32             : 
      33             : #if defined(__clang__)
      34             : #pragma clang diagnostic push
      35             : #pragma clang diagnostic ignored "-Wweak-vtables"
      36             : #endif
      37             : 
      38             : #include "generated/repo_generated.h"
      39             : 
      40             : #if defined(__clang__)
      41             : #pragma clang diagnostic pop
      42             : #endif
      43             : 
      44             : #if defined(__GNUC__)
      45             : #pragma GCC diagnostic pop
      46             : #endif
      47             : 
      48             : /* ------------------------------------------------------------------------- */
      49             : 
      50             : using namespace flatbuffers;
      51             : using namespace generated;
      52             : 
      53             : namespace gdal::icechunk
      54             : {
      55             : IcechunkFile::IcechunkFile() = default;
      56             : 
      57             : IcechunkFile::~IcechunkFile() = default;
      58             : 
      59             : IcechunkRepo::IcechunkRepo() = default;
      60             : 
      61             : IcechunkRepo::~IcechunkRepo() = default;
      62             : 
      63             : /************************************************************************/
      64             : /*                        IcechunkRepo::OpenV1()                        */
      65             : /************************************************************************/
      66             : 
      67             : /* static */
      68          13 : std::unique_ptr<IcechunkRepo> IcechunkRepo::OpenV1(const char *pszRootPath)
      69             : {
      70          13 :     auto repo = std::make_unique<IcechunkRepo>();
      71          13 :     repo->m_osRootPath = pszRootPath;
      72             : 
      73             :     const std::string osRefsDir =
      74          26 :         CPLFormFilenameSafe(pszRootPath, "refs", nullptr);
      75          26 :     const CPLStringList aosRefs(VSIReadDir(osRefsDir.c_str()));
      76          63 :     for (const char *pszRef : aosRefs)
      77             :     {
      78          50 :         if (strcmp(pszRef, ".") != 0 && strcmp(pszRef, "..") != 0)
      79             :         {
      80             :             const std::string osRefDir =
      81          48 :                 CPLFormFilenameSafe(osRefsDir.c_str(), pszRef, nullptr);
      82          24 :             if (STARTS_WITH(pszRef, "branch."))
      83             :             {
      84          26 :                 CPLJSONDocument oDoc;
      85          13 :                 if (oDoc.Load(CPLFormFilenameSafe(osRefDir.c_str(), "ref.json",
      86             :                                                   nullptr)))
      87             :                 {
      88             :                     const auto osSnapshotId =
      89          26 :                         oDoc.GetRoot().GetString("snapshot");
      90          13 :                     repo->m_oMapBranchNameToSnapshotId[pszRef +
      91          26 :                                                        strlen("branch.")] =
      92          13 :                         osSnapshotId;
      93             :                 }
      94             :             }
      95          11 :             else if (STARTS_WITH(pszRef, "tag."))
      96             :             {
      97          22 :                 CPLJSONDocument oDoc;
      98          11 :                 if (oDoc.Load(CPLFormFilenameSafe(osRefDir.c_str(), "ref.json",
      99             :                                                   nullptr)))
     100             :                 {
     101             :                     const auto osSnapshotId =
     102          22 :                         oDoc.GetRoot().GetString("snapshot");
     103          22 :                     repo->m_oMapTagNameToSnapshotId[pszRef + strlen("tag.")] =
     104          11 :                         osSnapshotId;
     105             :                 }
     106             :             }
     107             :         }
     108             :     }
     109             : 
     110          26 :     return repo;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                           ProcessConfig()                            */
     115             : /************************************************************************/
     116             : 
     117        2104 : static void ProcessConfig(const CPLJSONObject &oConfig)
     118             : {
     119        6312 :     const auto oVCC = oConfig["virtual_chunk_containers"];
     120        2104 :     if (oVCC.GetType() == CPLJSONObject::Type::Object)
     121             :     {
     122        4196 :         for (const auto &oVCCChild : oVCC.GetChildren())
     123             :         {
     124        6294 :             const auto osURLPrefix = oVCCChild.GetString("url_prefix");
     125        6294 :             const auto oStore = oVCCChild["store"];
     126        4196 :             if (cpl::starts_with(osURLPrefix, "s3://") &&
     127        2098 :                 oStore.GetType() == CPLJSONObject::Type::Object)
     128             :             {
     129        6294 :                 const auto oS3 = oStore["s3"];
     130        2098 :                 if (oS3.GetType() == CPLJSONObject::Type::Object)
     131             :                 {
     132        4196 :                     std::string osPath("/vsis3/");
     133        2098 :                     osPath += osURLPrefix.substr(strlen("s3://"));
     134             : 
     135        6294 :                     const std::string osRegion = oS3.GetString("region");
     136        2098 :                     if (!osRegion.empty())
     137             :                     {
     138        2098 :                         CPLDebug("Icechunk", "Set AWS_DEFAULT_REGION=%s for %s",
     139             :                                  osRegion.c_str(), osPath.c_str());
     140        2098 :                         VSISetPathSpecificOption(osPath.c_str(),
     141             :                                                  "AWS_DEFAULT_REGION",
     142             :                                                  osRegion.c_str());
     143             :                     }
     144             : 
     145        2098 :                     const bool bAnonymous = oS3.GetBool("anonymous");
     146        2098 :                     if (bAnonymous)
     147             :                     {
     148        2098 :                         CPLDebug("Icechunk",
     149             :                                  "Set AWS_NO_SIGN_REQUEST=YES for %s",
     150             :                                  osPath.c_str());
     151        2098 :                         VSISetPathSpecificOption(osPath.c_str(),
     152             :                                                  "AWS_NO_SIGN_REQUEST", "YES");
     153             :                     }
     154             : 
     155        2098 :                     const bool bRequesterPays = oS3.GetBool("requester_pays");
     156        2098 :                     if (bRequesterPays &&
     157           0 :                         !CPLTestBool(VSIGetPathSpecificOption(
     158             :                             osPath.c_str(), "AWS_REQUEST_PAYER", "NO")))
     159             :                     {
     160           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     161             :                                  "AWS_REQUEST_PAYER=YES must be set to "
     162             :                                  "access %s",
     163             :                                  osPath.c_str());
     164             :                     }
     165             :                 }
     166             :             }
     167           0 :             else if ((cpl::starts_with(osURLPrefix, "gs://") ||
     168           0 :                       cpl::starts_with(osURLPrefix, "gcs://")) &&
     169           0 :                      oStore.GetType() == CPLJSONObject::Type::Object)
     170             :             {
     171           0 :                 const auto oGCS = oStore["gcs"];
     172           0 :                 if (oGCS.GetType() == CPLJSONObject::Type::Object)
     173             :                 {
     174           0 :                     std::string osPath("/vsigs/");
     175           0 :                     osPath += osURLPrefix.substr(osURLPrefix.find("://") + 3);
     176             : 
     177           0 :                     const auto bAnonymous = oGCS.GetBool("anonymous");
     178           0 :                     if (bAnonymous)
     179             :                     {
     180           0 :                         CPLDebug("Icechunk",
     181             :                                  "Set GS_NO_SIGN_REQUEST=YES for %s",
     182             :                                  osPath.c_str());
     183           0 :                         VSISetPathSpecificOption(osPath.c_str(),
     184             :                                                  "GS_NO_SIGN_REQUEST", "YES");
     185             :                     }
     186             :                 }
     187             :             }
     188             :         }
     189             :     }
     190        2104 : }
     191             : 
     192             : /************************************************************************/
     193             : /*                         IcechunkRepo::Open()                         */
     194             : /************************************************************************/
     195             : 
     196             : /** Open a "repo" file */
     197             : /* static */
     198        4183 : std::unique_ptr<IcechunkRepo> IcechunkRepo::Open(const char *pszFilename,
     199             :                                                  VSIVirtualHandle *fp)
     200             : {
     201        4183 :     CPLDebugOnly("Icechunk", "Opening repo %s", pszFilename);
     202        8366 :     std::string osFilename(pszFilename);
     203        4183 :     VSIVirtualHandleUniquePtr tmpFp;
     204        4183 :     if (!fp)
     205             :     {
     206        4157 :         for (int iAttempt = 1; iAttempt <= 2; ++iAttempt)
     207             :         {
     208        8312 :             if (iAttempt == 2 ||
     209        4155 :                 strcmp(CPLGetFilename(pszFilename), "repo") != 0)
     210             :             {
     211             :                 osFilename =
     212        4155 :                     CPLFormFilenameSafe(osFilename.c_str(), "repo", nullptr);
     213             :                 tmpFp =
     214        4155 :                     VSIFilesystemHandler::OpenStatic(osFilename.c_str(), "rb");
     215             :                 // For network file systems, try to read one byte
     216        4155 :                 char chDummy = 1;
     217        4155 :                 if (tmpFp && tmpFp->Read(&chDummy, 1) != 1)
     218             :                 {
     219           0 :                     tmpFp.reset();
     220             :                 }
     221             : 
     222        4155 :                 if (tmpFp)
     223             :                 {
     224        4139 :                     CPL_IGNORE_RET_VAL(tmpFp->Seek(0, SEEK_SET));
     225        4139 :                     pszFilename = osFilename.c_str();
     226             :                 }
     227             :                 else
     228             :                 {
     229             :                     VSIStatBufL sStat;
     230          16 :                     if (VSIStatL(
     231          32 :                             CPLFormFilenameSafe(pszFilename, "refs", nullptr)
     232             :                                 .c_str(),
     233          16 :                             &sStat) == 0)
     234             :                     {
     235             :                         // Icechunk v1
     236          13 :                         return OpenV1(pszFilename);
     237             :                     }
     238             :                 }
     239        4142 :                 break;
     240             :             }
     241             :             else
     242             :             {
     243           2 :                 tmpFp = VSIFilesystemHandler::OpenStatic(pszFilename, "rb");
     244             :                 // For network file systems, try to read one byte
     245           2 :                 char chDummy = 1;
     246           2 :                 if (!tmpFp || tmpFp->Read(&chDummy, 1) != 1)
     247             :                 {
     248           2 :                     tmpFp.reset();
     249             :                     // This might be a repository named "repo". Retry with
     250             :                     // that assumption
     251             :                 }
     252             :                 else
     253             :                 {
     254           0 :                     if (tmpFp)
     255           0 :                         CPL_IGNORE_RET_VAL(tmpFp->Seek(0, SEEK_SET));
     256           0 :                     break;
     257             :                 }
     258             :             }
     259             :         }
     260             : 
     261        4142 :         fp = tmpFp.get();
     262        4142 :         if (!fp)
     263             :         {
     264           3 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", pszFilename);
     265           3 :             return nullptr;
     266             :         }
     267             :     }
     268             : 
     269        4167 :     int nFileVersion = 0;
     270        4167 :     auto [buffer, size] =
     271        8334 :         DecompressFile(pszFilename, fp, FILE_TYPE_REPO_INFO, &nFileVersion);
     272        4167 :     if (!buffer)
     273           2 :         return nullptr;
     274             : 
     275             :     {
     276        4165 :         Verifier verifier(buffer.get(), size);
     277        4165 :         if (!VerifyRepoBuffer(verifier))
     278             :         {
     279           3 :             CPLError(CE_Failure, CPLE_AppDefined, "%s: invalid Repo Flatbuffer",
     280             :                      pszFilename);
     281           3 :             return nullptr;
     282             :         }
     283             :     }
     284             : 
     285        8324 :     auto repo = std::make_unique<IcechunkRepo>();
     286        4162 :     repo->m_osRootPath = CPLGetPathSafe(pszFilename);
     287             : 
     288        4162 :     const Repo *repoPtr = GetRepo(buffer.get());
     289             : 
     290        4162 :     const int nSpecVersion = repoPtr->spec_version();
     291        4162 :     CPLDebugOnly("Icechunk", "Repo spec_version = %d", nSpecVersion);
     292        4162 :     if (nSpecVersion != 1 && nSpecVersion != 2)
     293             :     {
     294           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s: invalid spec_version %d",
     295             :                  pszFilename, nSpecVersion);
     296           1 :         return nullptr;
     297             :     }
     298        4161 :     if (nFileVersion != nSpecVersion)
     299             :     {
     300           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     301             :                  "%s: file version=%d != spec_version=%d", pszFilename,
     302             :                  nFileVersion, nSpecVersion);
     303           1 :         return nullptr;
     304             :     }
     305             : 
     306        4160 :     const auto status = repoPtr->status();
     307        4160 :     if (status)
     308             :     {
     309        4160 :         const auto availability = status->availability();
     310        4160 :         if (availability == RepoAvailability::Offline)
     311             :         {
     312           1 :             const auto reason = status->limited_availability_reason();
     313           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     314             :                      "%s: repository is offline: %s", pszFilename,
     315           0 :                      reason ? reason->c_str() : "unknown reason");
     316           1 :             return nullptr;
     317             :         }
     318             :     }
     319             : 
     320             :     {
     321        4159 :         const auto metadata = repoPtr->metadata();
     322        4159 :         if (metadata && nSpecVersion == 2)
     323             :         {
     324        4087 :             for (const auto &md : *metadata)
     325             :             {
     326           0 :                 if (const auto value = md->value())
     327             :                 {
     328           0 :                     if (!flexbuffers::VerifyBuffer(value->data(),
     329           0 :                                                    value->size()))
     330             :                     {
     331           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     332             :                                  "%s: flexbuffers::VerifyBuffer() failed",
     333             :                                  pszFilename);
     334           0 :                         return nullptr;
     335             :                     }
     336             :                     if constexpr (IS_DEBUG_BUILD)
     337             :                     {
     338           0 :                         std::string val;
     339           0 :                         flexbuffers::GetRoot(value->data(), value->size())
     340           0 :                             .ToString(true, true, val);
     341           0 :                         CPLDebugOnly("Icechunk", "metadata %s=%s",
     342             :                                      md->name() ? md->name()->c_str()
     343             :                                                 : "(null)",
     344             :                                      val.c_str());
     345             :                     }
     346             :                 }
     347             :             }
     348             :         }
     349             :     }
     350             : 
     351        4159 :     if (const auto config = repoPtr->config())
     352             :     {
     353        2104 :         if (!flexbuffers::VerifyBuffer(config->data(), config->size()))
     354             :         {
     355           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     356             :                      "%s: flexbuffers::VerifyBuffer() failed", pszFilename);
     357           0 :             return nullptr;
     358             :         }
     359        2104 :         std::string val;
     360        2104 :         flexbuffers::GetRoot(config->data(), config->size())
     361        2104 :             .ToString(true, true, val);
     362        2104 :         CPLDebugOnly("Icechunk", "config %s", val.c_str());
     363        2104 :         CPLJSONDocument oDoc;
     364        2104 :         if (!oDoc.LoadMemory(val))
     365             :         {
     366           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     367             :                      "%s: invalid configuration: %s", pszFilename, val.c_str());
     368           0 :             return nullptr;
     369             :         }
     370        2104 :         ProcessConfig(oDoc.GetRoot());
     371             :     }
     372             : 
     373        4159 :     const auto snapshots = repoPtr->snapshots();
     374        4159 :     CPLAssertAlways(snapshots);  // guaranteed by VerifyRepoBuffer()
     375       13228 :     for (uint32_t snapshotIdx = 0; snapshotIdx < snapshots->size();
     376             :          ++snapshotIdx)
     377             :     {
     378        9070 :         const auto *snapshot = (*snapshots)[snapshotIdx];
     379        9070 :         const auto *id = snapshot->id();
     380        9070 :         CPLAssertNotNull(id);           // guaranteed by VerifyRepoBuffer()
     381        9070 :         CPLAssertNotNull(id->bytes());  // guaranteed by VerifyRepoBuffer()
     382             : 
     383        9070 :         const auto *message = snapshot->message();
     384        9070 :         CPLAssertNotNull(message);  // guaranteed by VerifyRepoBuffer()
     385             : 
     386        9070 :         CPLDebugOnly("Icechunk",
     387             :                      "snapshot '%s', parent_offset %d, message '%s'",
     388             :                      CrockfordBase32Encode(*(id->bytes())).c_str(),
     389             :                      snapshot->parent_offset(), message->c_str());
     390             : 
     391        9070 :         const auto metadata = snapshot->metadata();
     392        9070 :         if (metadata && nSpecVersion == 2)
     393             :         {
     394       13917 :             for (const auto &md : *metadata)
     395             :             {
     396        4917 :                 if (const auto value = md->value())
     397             :                 {
     398        4917 :                     if (!flexbuffers::VerifyBuffer(value->data(),
     399        4917 :                                                    value->size()))
     400             :                     {
     401           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
     402             :                                  "%s: flexbuffers::VerifyBuffer() failed",
     403             :                                  pszFilename);
     404           1 :                         return nullptr;
     405             :                     }
     406             :                     if constexpr (IS_DEBUG_BUILD)
     407             :                     {
     408        9832 :                         std::string val;
     409        4916 :                         flexbuffers::GetRoot(value->data(), value->size())
     410        4916 :                             .ToString(true, true, val);
     411        4916 :                         CPLDebugOnly("Icechunk", "  metadata %s=%s",
     412             :                                      md->name() ? md->name()->c_str()
     413             :                                                 : "(null)",
     414             :                                      val.c_str());
     415             :                     }
     416             :                 }
     417             :             }
     418             :         }
     419             :     }
     420             : 
     421             :     // Parse tags
     422        4158 :     const auto tags = repoPtr->tags();
     423        4158 :     CPLAssertAlways(tags);  // guaranteed by VerifyRepoBuffer()
     424        4161 :     for (const auto &ref : *tags)
     425             :     {
     426           5 :         const auto *refName = ref->name();
     427           5 :         CPLAssertNotNull(refName);  // guaranteed by VerifyRepoBuffer()
     428           5 :         CPLDebugOnly("Icechunk", "tag '%s', snapshot_index %u",
     429             :                      refName->c_str(), ref->snapshot_index());
     430             : 
     431           5 :         if (ref->snapshot_index() >= snapshots->size())
     432             :         {
     433           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     434             :                      "%s: tag '%s', invalid snapshot_index %u", pszFilename,
     435             :                      refName->c_str(), ref->snapshot_index());
     436           2 :             return nullptr;
     437             :         }
     438             : 
     439           4 :         const auto *snapshot = (*snapshots)[ref->snapshot_index()];
     440           4 :         const auto *id = snapshot->id();
     441           4 :         CPLAssertNotNull(id);  // guaranteed by VerifyRepoBuffer()
     442           4 :         if (!repo->m_oMapTagNameToSnapshotId
     443           4 :                  .insert({GetString(refName),
     444           8 :                           CrockfordBase32Encode(*(id->bytes()))})
     445           4 :                  .second)
     446             :         {
     447           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s: more than one tag '%s'",
     448             :                      pszFilename, refName->c_str());
     449           1 :             return nullptr;
     450             :         }
     451             :     }
     452             : 
     453             :     // Parse branches
     454        4156 :     const auto branches = repoPtr->branches();
     455        4156 :     CPLAssertAlways(branches);  // guaranteed by VerifyRepoBuffer()
     456        8310 :     for (const auto &ref : *branches)
     457             :     {
     458        4156 :         const auto *refName = ref->name();
     459        4156 :         CPLAssertNotNull(refName);  // guaranteed by VerifyRepoBuffer()
     460        4156 :         CPLDebugOnly("Icechunk", "branch '%s', snapshot_index %u",
     461             :                      refName->c_str(), ref->snapshot_index());
     462             : 
     463        4156 :         if (ref->snapshot_index() >= snapshots->size())
     464             :         {
     465           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     466             :                      "%s: branch '%s', invalid snapshot_index %u", pszFilename,
     467             :                      refName->c_str(), ref->snapshot_index());
     468           2 :             return nullptr;
     469             :         }
     470             : 
     471        4155 :         const auto *snapshot = (*snapshots)[ref->snapshot_index()];
     472        4155 :         const auto *id = snapshot->id();
     473        4155 :         CPLAssertNotNull(id);  // guaranteed by VerifyRepoBuffer()
     474        4155 :         if (!repo->m_oMapBranchNameToSnapshotId
     475        4155 :                  .insert({GetString(refName),
     476        8310 :                           CrockfordBase32Encode(*(id->bytes()))})
     477        4155 :                  .second)
     478             :         {
     479           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     480             :                      "%s: more than one branch '%s'", pszFilename,
     481             :                      refName->c_str());
     482           1 :             return nullptr;
     483             :         }
     484             :     }
     485             : 
     486        4154 :     return repo;
     487             : }
     488             : 
     489             : /************************************************************************/
     490             : /*                 IcechunkRepo::OpenSnapshotOnBranch()                 */
     491             : /************************************************************************/
     492             : 
     493             : /** Open the snapshot corresponding to the passed branch name. */
     494             : std::unique_ptr<IcechunkSnapshot>
     495        4158 : IcechunkRepo::OpenSnapshotOnBranch(const std::string &name,
     496             :                                    bool emitErrorIfUnknownBranch) const
     497             : {
     498        4158 :     const auto oIter = m_oMapBranchNameToSnapshotId.find(name);
     499        4158 :     if (oIter == m_oMapBranchNameToSnapshotId.end())
     500             :     {
     501           1 :         if (emitErrorIfUnknownBranch)
     502           0 :             CPLError(CE_Failure, CPLE_AppDefined, "No branch '%s'",
     503             :                      name.c_str());
     504           1 :         return nullptr;
     505             :     }
     506        4157 :     const auto &snapshotId = oIter->second;
     507             : 
     508             :     std::string osSnapshotFilename = CPLFormFilenameSafe(
     509        4157 :         CPLFormFilenameSafe(m_osRootPath.c_str(), "snapshots", nullptr).c_str(),
     510       12471 :         snapshotId.c_str(), nullptr);
     511        4157 :     return IcechunkSnapshot::Open(osSnapshotFilename.c_str());
     512             : }
     513             : 
     514             : /************************************************************************/
     515             : /*                  IcechunkRepo::OpenSnapshotOnTag()                   */
     516             : /************************************************************************/
     517             : 
     518             : /** Open the snapshot corresponding to the passed tag name. */
     519             : std::unique_ptr<IcechunkSnapshot>
     520           6 : IcechunkRepo::OpenSnapshotOnTag(const std::string &name,
     521             :                                 bool emitErrorIfUnknownTag) const
     522             : {
     523           6 :     const auto oIter = m_oMapTagNameToSnapshotId.find(name);
     524           6 :     if (oIter == m_oMapTagNameToSnapshotId.end())
     525             :     {
     526           1 :         if (emitErrorIfUnknownTag)
     527           0 :             CPLError(CE_Failure, CPLE_AppDefined, "No tag '%s'", name.c_str());
     528           1 :         return nullptr;
     529             :     }
     530           5 :     const auto &snapshotId = oIter->second;
     531             : 
     532             :     std::string osSnapshotFilename = CPLFormFilenameSafe(
     533           5 :         CPLFormFilenameSafe(m_osRootPath.c_str(), "snapshots", nullptr).c_str(),
     534          15 :         snapshotId.c_str(), nullptr);
     535           5 :     return IcechunkSnapshot::Open(osSnapshotFilename.c_str());
     536             : }
     537             : 
     538             : /************************************************************************/
     539             : /*                            GetRepoCache()                            */
     540             : /************************************************************************/
     541             : 
     542             : using CacheType =
     543             :     lru11::Cache<std::string, std::shared_ptr<IcechunkManifest>, std::mutex>;
     544             : 
     545        9113 : static CacheType &GetRepoCache()
     546             : {
     547             :     static lru11::Cache<std::string, std::shared_ptr<IcechunkManifest>,
     548             :                         std::mutex>
     549        9113 :         goCache;
     550        9113 :     return goCache;
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                     IcechunkRepo::OpenManifest()                     */
     555             : /************************************************************************/
     556             : 
     557             : /** Open the manifest corresponding to the passed manifest id. */
     558             : std::shared_ptr<IcechunkManifest>
     559        8107 : IcechunkRepo::OpenManifest(const std::string &manifestId,
     560             :                            uint64_t nExpectedFileSize,
     561             :                            uint32_t nExpectedChunkRefs) const
     562             : {
     563        8107 :     CacheType &goCache = GetRepoCache();
     564        8107 :     std::shared_ptr<IcechunkManifest> manifest;
     565             :     const std::string cacheKey =
     566       16214 :         std::string(m_osRootPath).append("|").append(manifestId);
     567        8107 :     if (goCache.tryGet(cacheKey, manifest))
     568        4491 :         return manifest;
     569             : 
     570             :     std::string osFilename = CPLFormFilenameSafe(
     571        3616 :         CPLFormFilenameSafe(m_osRootPath.c_str(), "manifests", nullptr).c_str(),
     572       10848 :         manifestId.c_str(), nullptr);
     573        3616 :     manifest = IcechunkManifest::Open(osFilename.c_str());
     574        3616 :     if (manifest)
     575             :     {
     576             :         VSIStatBufL sStat;
     577        7204 :         if (VSIStatL(manifest->GetFilename().c_str(), &sStat) == 0 &&
     578        3602 :             static_cast<uint64_t>(sStat.st_size) != nExpectedFileSize)
     579             :         {
     580           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     581             :                      "Actual file size of manifest %s = %" PRIu64
     582             :                      " does not match expected one = %" PRIu64,
     583           0 :                      osFilename.c_str(), static_cast<uint64_t>(sStat.st_size),
     584             :                      nExpectedFileSize);
     585           0 :             manifest.reset();
     586             :         }
     587        3602 :         else if (manifest->GetChunkRefsCount() != nExpectedChunkRefs)
     588             :         {
     589           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     590             :                      "Actual count of chunk references in manifest %s = %u "
     591             :                      "does not match expected one = %u",
     592             :                      osFilename.c_str(), manifest->GetChunkRefsCount(),
     593             :                      nExpectedChunkRefs);
     594           1 :             manifest.reset();
     595             :         }
     596             :         else
     597             :         {
     598        3601 :             goCache.insert(cacheKey, manifest);
     599             :         }
     600             :     }
     601        3616 :     return manifest;
     602             : }
     603             : 
     604             : /************************************************************************/
     605             : /*                     IcechunkRepo::ClearCaches()                      */
     606             : /************************************************************************/
     607             : 
     608        1006 : /* static */ void IcechunkRepo::ClearCaches()
     609             : {
     610        1006 :     CacheType &goCache = GetRepoCache();
     611        1006 :     goCache.clear();
     612        1006 : }
     613             : 
     614             : }  // namespace gdal::icechunk

Generated by: LCOV version 1.14