LCOV - code coverage report
Current view: top level - frmts/icechunk - icechunkrepo.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 205 239 85.8 %
Date: 2026-06-19 21:24:00 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          12 : std::unique_ptr<IcechunkRepo> IcechunkRepo::OpenV1(const char *pszRootPath)
      69             : {
      70          12 :     auto repo = std::make_unique<IcechunkRepo>();
      71          12 :     repo->m_osRootPath = pszRootPath;
      72             : 
      73             :     const std::string osRefsDir =
      74          24 :         CPLFormFilenameSafe(pszRootPath, "refs", nullptr);
      75          24 :     const CPLStringList aosRefs(VSIReadDir(osRefsDir.c_str()));
      76          58 :     for (const char *pszRef : aosRefs)
      77             :     {
      78          46 :         if (strcmp(pszRef, ".") != 0 && strcmp(pszRef, "..") != 0)
      79             :         {
      80             :             const std::string osRefDir =
      81          44 :                 CPLFormFilenameSafe(osRefsDir.c_str(), pszRef, nullptr);
      82          22 :             if (STARTS_WITH(pszRef, "branch."))
      83             :             {
      84          24 :                 CPLJSONDocument oDoc;
      85          12 :                 if (oDoc.Load(CPLFormFilenameSafe(osRefDir.c_str(), "ref.json",
      86             :                                                   nullptr)))
      87             :                 {
      88             :                     const auto osSnapshotId =
      89          24 :                         oDoc.GetRoot().GetString("snapshot");
      90          12 :                     repo->m_oMapBranchNameToSnapshotId[pszRef +
      91          24 :                                                        strlen("branch.")] =
      92          12 :                         osSnapshotId;
      93             :                 }
      94             :             }
      95          10 :             else if (STARTS_WITH(pszRef, "tag."))
      96             :             {
      97          20 :                 CPLJSONDocument oDoc;
      98          10 :                 if (oDoc.Load(CPLFormFilenameSafe(osRefDir.c_str(), "ref.json",
      99             :                                                   nullptr)))
     100             :                 {
     101             :                     const auto osSnapshotId =
     102          20 :                         oDoc.GetRoot().GetString("snapshot");
     103          20 :                     repo->m_oMapTagNameToSnapshotId[pszRef + strlen("tag.")] =
     104          10 :                         osSnapshotId;
     105             :                 }
     106             :             }
     107             :         }
     108             :     }
     109             : 
     110          24 :     return repo;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                           ProcessConfig()                            */
     115             : /************************************************************************/
     116             : 
     117        1057 : static void ProcessConfig(const CPLJSONObject &oConfig)
     118             : {
     119        3171 :     const auto oVCC = oConfig["virtual_chunk_containers"];
     120        1057 :     if (oVCC.GetType() == CPLJSONObject::Type::Object)
     121             :     {
     122        2102 :         for (const auto &oVCCChild : oVCC.GetChildren())
     123             :         {
     124        3153 :             const auto osURLPrefix = oVCCChild.GetString("url_prefix");
     125        3153 :             const auto oStore = oVCCChild["store"];
     126        2102 :             if (cpl::starts_with(osURLPrefix, "s3://") &&
     127        1051 :                 oStore.GetType() == CPLJSONObject::Type::Object)
     128             :             {
     129        3153 :                 const auto oS3 = oStore["s3"];
     130        1051 :                 if (oS3.GetType() == CPLJSONObject::Type::Object)
     131             :                 {
     132        2102 :                     std::string osPath("/vsis3/");
     133        1051 :                     osPath += osURLPrefix.substr(strlen("s3://"));
     134             : 
     135        3153 :                     const std::string osRegion = oS3.GetString("region");
     136        1051 :                     if (!osRegion.empty())
     137             :                     {
     138        1051 :                         CPLDebug("Icechunk", "Set AWS_DEFAULT_REGION=%s for %s",
     139             :                                  osRegion.c_str(), osPath.c_str());
     140        1051 :                         VSISetPathSpecificOption(osPath.c_str(),
     141             :                                                  "AWS_DEFAULT_REGION",
     142             :                                                  osRegion.c_str());
     143             :                     }
     144             : 
     145        1051 :                     const bool bAnonymous = oS3.GetBool("anonymous");
     146        1051 :                     if (bAnonymous)
     147             :                     {
     148        1051 :                         CPLDebug("Icechunk",
     149             :                                  "Set AWS_NO_SIGN_REQUEST=YES for %s",
     150             :                                  osPath.c_str());
     151        1051 :                         VSISetPathSpecificOption(osPath.c_str(),
     152             :                                                  "AWS_NO_SIGN_REQUEST", "YES");
     153             :                     }
     154             : 
     155        1051 :                     const bool bRequesterPays = oS3.GetBool("requester_pays");
     156        1051 :                     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        1057 : }
     191             : 
     192             : /************************************************************************/
     193             : /*                         IcechunkRepo::Open()                         */
     194             : /************************************************************************/
     195             : 
     196             : /** Open a "repo" file */
     197             : /* static */
     198        2214 : std::unique_ptr<IcechunkRepo> IcechunkRepo::Open(const char *pszFilename,
     199             :                                                  VSIVirtualHandle *fp)
     200             : {
     201        2214 :     CPLDebugOnly("Icechunk", "Opening repo %s", pszFilename);
     202        4428 :     std::string osFilename(pszFilename);
     203        2214 :     VSIVirtualHandleUniquePtr tmpFp;
     204        2214 :     if (!fp)
     205             :     {
     206        2186 :         if (strcmp(CPLGetFilename(pszFilename), "repo") != 0)
     207             :         {
     208             :             osFilename =
     209        2186 :                 CPLFormFilenameSafe(osFilename.c_str(), "repo", nullptr);
     210        2186 :             tmpFp = VSIFilesystemHandler::OpenStatic(osFilename.c_str(), "rb");
     211             :             // For network file systems, try to read one byte
     212        2186 :             char chDummy = 1;
     213        2186 :             if (tmpFp && tmpFp->Read(&chDummy, 1) != 1)
     214             :             {
     215           0 :                 tmpFp.reset();
     216             :             }
     217             : 
     218        2186 :             if (tmpFp)
     219             :             {
     220        2171 :                 CPL_IGNORE_RET_VAL(tmpFp->Seek(0, SEEK_SET));
     221        2171 :                 pszFilename = osFilename.c_str();
     222             :             }
     223             :             else
     224             :             {
     225             :                 VSIStatBufL sStat;
     226          30 :                 if (VSIStatL(CPLFormFilenameSafe(pszFilename, "refs", nullptr)
     227             :                                  .c_str(),
     228          15 :                              &sStat) == 0)
     229             :                 {
     230             :                     // Icechunk v1
     231          12 :                     return OpenV1(pszFilename);
     232             :                 }
     233             :             }
     234             :         }
     235             :         else
     236             :         {
     237           0 :             tmpFp = VSIFilesystemHandler::OpenStatic(pszFilename, "rb");
     238             :         }
     239             : 
     240        2174 :         fp = tmpFp.get();
     241        2174 :         if (!fp)
     242             :         {
     243           3 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", pszFilename);
     244           3 :             return nullptr;
     245             :         }
     246             :     }
     247             : 
     248        2199 :     int nFileVersion = 0;
     249        2199 :     auto [buffer, size] =
     250        4398 :         DecompressFile(pszFilename, fp, FILE_TYPE_REPO_INFO, &nFileVersion);
     251        2199 :     if (!buffer)
     252           2 :         return nullptr;
     253             : 
     254             :     {
     255        2197 :         Verifier verifier(buffer.get(), size);
     256        2197 :         if (!VerifyRepoBuffer(verifier))
     257             :         {
     258           3 :             CPLError(CE_Failure, CPLE_AppDefined, "%s: invalid Repo Flatbuffer",
     259             :                      pszFilename);
     260           3 :             return nullptr;
     261             :         }
     262             :     }
     263             : 
     264        4388 :     auto repo = std::make_unique<IcechunkRepo>();
     265        2194 :     repo->m_osRootPath = CPLGetPathSafe(pszFilename);
     266             : 
     267        2194 :     const Repo *repoPtr = GetRepo(buffer.get());
     268             : 
     269        2194 :     const int nSpecVersion = repoPtr->spec_version();
     270        2194 :     CPLDebugOnly("Icechunk", "Repo spec_version = %d", nSpecVersion);
     271        2194 :     if (nSpecVersion != 1 && nSpecVersion != 2)
     272             :     {
     273           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s: invalid spec_version %d",
     274             :                  pszFilename, nSpecVersion);
     275           1 :         return nullptr;
     276             :     }
     277        2193 :     if (nFileVersion != nSpecVersion)
     278             :     {
     279           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     280             :                  "%s: file version=%d != spec_version=%d", pszFilename,
     281             :                  nFileVersion, nSpecVersion);
     282           1 :         return nullptr;
     283             :     }
     284             : 
     285        2192 :     const auto status = repoPtr->status();
     286        2192 :     if (status)
     287             :     {
     288        2192 :         const auto availability = status->availability();
     289        2192 :         if (availability == RepoAvailability::Offline)
     290             :         {
     291           1 :             const auto reason = status->limited_availability_reason();
     292           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     293             :                      "%s: repository is offline: %s", pszFilename,
     294           0 :                      reason ? reason->c_str() : "unknown reason");
     295           1 :             return nullptr;
     296             :         }
     297             :     }
     298             : 
     299             :     {
     300        2191 :         const auto metadata = repoPtr->metadata();
     301        2191 :         if (metadata && nSpecVersion == 2)
     302             :         {
     303        2123 :             for (const auto &md : *metadata)
     304             :             {
     305           0 :                 if (const auto value = md->value())
     306             :                 {
     307           0 :                     if (!flexbuffers::VerifyBuffer(value->data(),
     308           0 :                                                    value->size()))
     309             :                     {
     310           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     311             :                                  "%s: flexbuffers::VerifyBuffer() failed",
     312             :                                  pszFilename);
     313           0 :                         return nullptr;
     314             :                     }
     315             :                     if constexpr (IS_DEBUG_BUILD)
     316             :                     {
     317           0 :                         std::string val;
     318           0 :                         flexbuffers::GetRoot(value->data(), value->size())
     319           0 :                             .ToString(true, true, val);
     320           0 :                         CPLDebugOnly("Icechunk", "metadata %s=%s",
     321             :                                      md->name() ? md->name()->c_str()
     322             :                                                 : "(null)",
     323             :                                      val.c_str());
     324             :                     }
     325             :                 }
     326             :             }
     327             :         }
     328             :     }
     329             : 
     330        2191 :     if (const auto config = repoPtr->config())
     331             :     {
     332        1057 :         if (!flexbuffers::VerifyBuffer(config->data(), config->size()))
     333             :         {
     334           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     335             :                      "%s: flexbuffers::VerifyBuffer() failed", pszFilename);
     336           0 :             return nullptr;
     337             :         }
     338        1057 :         std::string val;
     339        1057 :         flexbuffers::GetRoot(config->data(), config->size())
     340        1057 :             .ToString(true, true, val);
     341        1057 :         CPLDebugOnly("Icechunk", "config %s", val.c_str());
     342        1057 :         CPLJSONDocument oDoc;
     343        1057 :         if (!oDoc.LoadMemory(val))
     344             :         {
     345           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     346             :                      "%s: invalid configuration: %s", pszFilename, val.c_str());
     347           0 :             return nullptr;
     348             :         }
     349        1057 :         ProcessConfig(oDoc.GetRoot());
     350             :     }
     351             : 
     352        2191 :     const auto snapshots = repoPtr->snapshots();
     353        2191 :     CPLAssertAlways(snapshots);  // guaranteed by VerifyRepoBuffer()
     354        8324 :     for (uint32_t snapshotIdx = 0; snapshotIdx < snapshots->size();
     355             :          ++snapshotIdx)
     356             :     {
     357        6134 :         const auto *snapshot = (*snapshots)[snapshotIdx];
     358        6134 :         const auto *id = snapshot->id();
     359        6134 :         CPLAssertNotNull(id);           // guaranteed by VerifyRepoBuffer()
     360        6134 :         CPLAssertNotNull(id->bytes());  // guaranteed by VerifyRepoBuffer()
     361             : 
     362        6134 :         const auto *message = snapshot->message();
     363        6134 :         CPLAssertNotNull(message);  // guaranteed by VerifyRepoBuffer()
     364             : 
     365        6134 :         CPLDebugOnly("Icechunk",
     366             :                      "snapshot '%s', parent_offset %d, message '%s'",
     367             :                      CrockfordBase32Encode(*(id->bytes())).c_str(),
     368             :                      snapshot->parent_offset(), message->c_str());
     369             : 
     370        6134 :         const auto metadata = snapshot->metadata();
     371        6134 :         if (metadata && nSpecVersion == 2)
     372             :         {
     373       10019 :             for (const auto &md : *metadata)
     374             :             {
     375        3951 :                 if (const auto value = md->value())
     376             :                 {
     377        3951 :                     if (!flexbuffers::VerifyBuffer(value->data(),
     378        3951 :                                                    value->size()))
     379             :                     {
     380           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
     381             :                                  "%s: flexbuffers::VerifyBuffer() failed",
     382             :                                  pszFilename);
     383           1 :                         return nullptr;
     384             :                     }
     385             :                     if constexpr (IS_DEBUG_BUILD)
     386             :                     {
     387        7900 :                         std::string val;
     388        3950 :                         flexbuffers::GetRoot(value->data(), value->size())
     389        3950 :                             .ToString(true, true, val);
     390        3950 :                         CPLDebugOnly("Icechunk", "  metadata %s=%s",
     391             :                                      md->name() ? md->name()->c_str()
     392             :                                                 : "(null)",
     393             :                                      val.c_str());
     394             :                     }
     395             :                 }
     396             :             }
     397             :         }
     398             :     }
     399             : 
     400             :     // Parse tags
     401        2190 :     const auto tags = repoPtr->tags();
     402        2190 :     CPLAssertAlways(tags);  // guaranteed by VerifyRepoBuffer()
     403        2191 :     for (const auto &ref : *tags)
     404             :     {
     405           3 :         const auto *refName = ref->name();
     406           3 :         CPLAssertNotNull(refName);  // guaranteed by VerifyRepoBuffer()
     407           3 :         CPLDebugOnly("Icechunk", "tag '%s', snapshot_index %u",
     408             :                      refName->c_str(), ref->snapshot_index());
     409             : 
     410           3 :         if (ref->snapshot_index() >= snapshots->size())
     411             :         {
     412           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     413             :                      "%s: tag '%s', invalid snapshot_index %u", pszFilename,
     414             :                      refName->c_str(), ref->snapshot_index());
     415           2 :             return nullptr;
     416             :         }
     417             : 
     418           2 :         const auto *snapshot = (*snapshots)[ref->snapshot_index()];
     419           2 :         const auto *id = snapshot->id();
     420           2 :         CPLAssertNotNull(id);  // guaranteed by VerifyRepoBuffer()
     421           2 :         if (!repo->m_oMapTagNameToSnapshotId
     422           2 :                  .insert({GetString(refName),
     423           4 :                           CrockfordBase32Encode(*(id->bytes()))})
     424           2 :                  .second)
     425             :         {
     426           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s: more than one tag '%s'",
     427             :                      pszFilename, refName->c_str());
     428           1 :             return nullptr;
     429             :         }
     430             :     }
     431             : 
     432             :     // Parse branches
     433        2188 :     const auto branches = repoPtr->branches();
     434        2188 :     CPLAssertAlways(branches);  // guaranteed by VerifyRepoBuffer()
     435        4374 :     for (const auto &ref : *branches)
     436             :     {
     437        2188 :         const auto *refName = ref->name();
     438        2188 :         CPLAssertNotNull(refName);  // guaranteed by VerifyRepoBuffer()
     439        2188 :         CPLDebugOnly("Icechunk", "branch '%s', snapshot_index %u",
     440             :                      refName->c_str(), ref->snapshot_index());
     441             : 
     442        2188 :         if (ref->snapshot_index() >= snapshots->size())
     443             :         {
     444           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     445             :                      "%s: branch '%s', invalid snapshot_index %u", pszFilename,
     446             :                      refName->c_str(), ref->snapshot_index());
     447           2 :             return nullptr;
     448             :         }
     449             : 
     450        2187 :         const auto *snapshot = (*snapshots)[ref->snapshot_index()];
     451        2187 :         const auto *id = snapshot->id();
     452        2187 :         CPLAssertNotNull(id);  // guaranteed by VerifyRepoBuffer()
     453        2187 :         if (!repo->m_oMapBranchNameToSnapshotId
     454        2187 :                  .insert({GetString(refName),
     455        4374 :                           CrockfordBase32Encode(*(id->bytes()))})
     456        2187 :                  .second)
     457             :         {
     458           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     459             :                      "%s: more than one branch '%s'", pszFilename,
     460             :                      refName->c_str());
     461           1 :             return nullptr;
     462             :         }
     463             :     }
     464             : 
     465        2186 :     return repo;
     466             : }
     467             : 
     468             : /************************************************************************/
     469             : /*                 IcechunkRepo::OpenSnapshotOnBranch()                 */
     470             : /************************************************************************/
     471             : 
     472             : /** Open the snapshot corresponding to the passed branch name. */
     473             : std::unique_ptr<IcechunkSnapshot>
     474        2192 : IcechunkRepo::OpenSnapshotOnBranch(const std::string &name,
     475             :                                    bool emitErrorIfUnknownBranch) const
     476             : {
     477        2192 :     const auto oIter = m_oMapBranchNameToSnapshotId.find(name);
     478        2192 :     if (oIter == m_oMapBranchNameToSnapshotId.end())
     479             :     {
     480           1 :         if (emitErrorIfUnknownBranch)
     481           0 :             CPLError(CE_Failure, CPLE_AppDefined, "No branch '%s'",
     482             :                      name.c_str());
     483           1 :         return nullptr;
     484             :     }
     485        2191 :     const auto &snapshotId = oIter->second;
     486             : 
     487             :     std::string osSnapshotFilename = CPLFormFilenameSafe(
     488        2191 :         CPLFormFilenameSafe(m_osRootPath.c_str(), "snapshots", nullptr).c_str(),
     489        6573 :         snapshotId.c_str(), nullptr);
     490        2191 :     return IcechunkSnapshot::Open(osSnapshotFilename.c_str());
     491             : }
     492             : 
     493             : /************************************************************************/
     494             : /*                  IcechunkRepo::OpenSnapshotOnTag()                   */
     495             : /************************************************************************/
     496             : 
     497             : /** Open the snapshot corresponding to the passed tag name. */
     498             : std::unique_ptr<IcechunkSnapshot>
     499           3 : IcechunkRepo::OpenSnapshotOnTag(const std::string &name,
     500             :                                 bool emitErrorIfUnknownTag) const
     501             : {
     502           3 :     const auto oIter = m_oMapTagNameToSnapshotId.find(name);
     503           3 :     if (oIter == m_oMapTagNameToSnapshotId.end())
     504             :     {
     505           1 :         if (emitErrorIfUnknownTag)
     506           0 :             CPLError(CE_Failure, CPLE_AppDefined, "No tag '%s'", name.c_str());
     507           1 :         return nullptr;
     508             :     }
     509           2 :     const auto &snapshotId = oIter->second;
     510             : 
     511             :     std::string osSnapshotFilename = CPLFormFilenameSafe(
     512           2 :         CPLFormFilenameSafe(m_osRootPath.c_str(), "snapshots", nullptr).c_str(),
     513           6 :         snapshotId.c_str(), nullptr);
     514           2 :     return IcechunkSnapshot::Open(osSnapshotFilename.c_str());
     515             : }
     516             : 
     517             : /************************************************************************/
     518             : /*                            GetRepoCache()                            */
     519             : /************************************************************************/
     520             : 
     521             : using CacheType =
     522             :     lru11::Cache<std::string, std::shared_ptr<IcechunkManifest>, std::mutex>;
     523             : 
     524        9107 : static CacheType &GetRepoCache()
     525             : {
     526             :     static lru11::Cache<std::string, std::shared_ptr<IcechunkManifest>,
     527             :                         std::mutex>
     528        9107 :         goCache;
     529        9107 :     return goCache;
     530             : }
     531             : 
     532             : /************************************************************************/
     533             : /*                     IcechunkRepo::OpenManifest()                     */
     534             : /************************************************************************/
     535             : 
     536             : /** Open the manifest corresponding to the passed manifest id. */
     537             : std::shared_ptr<IcechunkManifest>
     538        8101 : IcechunkRepo::OpenManifest(const std::string &manifestId,
     539             :                            uint64_t nExpectedFileSize,
     540             :                            uint32_t nExpectedChunkRefs) const
     541             : {
     542        8101 :     CacheType &goCache = GetRepoCache();
     543        8101 :     std::shared_ptr<IcechunkManifest> manifest;
     544             :     const std::string cacheKey =
     545       16202 :         std::string(m_osRootPath).append("|").append(manifestId);
     546        8101 :     if (goCache.tryGet(cacheKey, manifest))
     547        6004 :         return manifest;
     548             : 
     549             :     std::string osFilename = CPLFormFilenameSafe(
     550        2097 :         CPLFormFilenameSafe(m_osRootPath.c_str(), "manifests", nullptr).c_str(),
     551        6291 :         manifestId.c_str(), nullptr);
     552        2097 :     manifest = IcechunkManifest::Open(osFilename.c_str());
     553        2097 :     if (manifest)
     554             :     {
     555             :         VSIStatBufL sStat;
     556        4166 :         if (VSIStatL(manifest->GetFilename().c_str(), &sStat) == 0 &&
     557        2083 :             static_cast<uint64_t>(sStat.st_size) != nExpectedFileSize)
     558             :         {
     559           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     560             :                      "Actual file size of manifest %s = %" PRIu64
     561             :                      " does not match expected one = %" PRIu64,
     562           0 :                      osFilename.c_str(), static_cast<uint64_t>(sStat.st_size),
     563             :                      nExpectedFileSize);
     564           0 :             manifest.reset();
     565             :         }
     566        2083 :         else if (manifest->GetChunkRefsCount() != nExpectedChunkRefs)
     567             :         {
     568           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     569             :                      "Actual count of chunk references in manifest %s = %u "
     570             :                      "does not match expected one = %u",
     571             :                      osFilename.c_str(), manifest->GetChunkRefsCount(),
     572             :                      nExpectedChunkRefs);
     573           1 :             manifest.reset();
     574             :         }
     575             :         else
     576             :         {
     577        2082 :             goCache.insert(cacheKey, manifest);
     578             :         }
     579             :     }
     580        2097 :     return manifest;
     581             : }
     582             : 
     583             : /************************************************************************/
     584             : /*                     IcechunkRepo::ClearCaches()                      */
     585             : /************************************************************************/
     586             : 
     587        1006 : /* static */ void IcechunkRepo::ClearCaches()
     588             : {
     589        1006 :     CacheType &goCache = GetRepoCache();
     590        1006 :     goCache.clear();
     591        1006 : }
     592             : 
     593             : }  // namespace gdal::icechunk

Generated by: LCOV version 1.14