LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_v3_codec.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 372 513 72.5 %
Date: 2025-12-02 17:42:13 Functions: 32 36 88.9 %

          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) 2023, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "zarr.h"
      14             : 
      15             : #include "cpl_compressor.h"
      16             : 
      17             : /************************************************************************/
      18             : /*                          ZarrV3Codec()                               */
      19             : /************************************************************************/
      20             : 
      21         411 : ZarrV3Codec::ZarrV3Codec(const std::string &osName) : m_osName(osName)
      22             : {
      23         411 : }
      24             : 
      25             : /************************************************************************/
      26             : /*                         ~ZarrV3Codec()                               */
      27             : /************************************************************************/
      28             : 
      29             : ZarrV3Codec::~ZarrV3Codec() = default;
      30             : 
      31             : /************************************************************************/
      32             : /*                      ZarrV3CodecAbstractCompressor()                 */
      33             : /************************************************************************/
      34             : 
      35          64 : ZarrV3CodecAbstractCompressor::ZarrV3CodecAbstractCompressor(
      36          64 :     const std::string &osName)
      37          64 :     : ZarrV3Codec(osName)
      38             : {
      39          64 : }
      40             : 
      41             : /************************************************************************/
      42             : /*                 ZarrV3CodecAbstractCompressor::Encode()              */
      43             : /************************************************************************/
      44             : 
      45        5367 : bool ZarrV3CodecAbstractCompressor::Encode(
      46             :     const ZarrByteVectorQuickResize &abySrc,
      47             :     ZarrByteVectorQuickResize &abyDst) const
      48             : {
      49        5367 :     abyDst.resize(abyDst.capacity());
      50        5367 :     void *pOutputData = abyDst.data();
      51        5367 :     size_t nOutputSize = abyDst.size();
      52       10734 :     bool bRet = m_pCompressor->pfnFunc(
      53        5367 :         abySrc.data(), abySrc.size(), &pOutputData, &nOutputSize,
      54        5367 :         m_aosCompressorOptions.List(), m_pCompressor->user_data);
      55        5367 :     if (bRet)
      56             :     {
      57        5367 :         abyDst.resize(nOutputSize);
      58             :     }
      59           0 :     else if (nOutputSize > abyDst.size())
      60             :     {
      61           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      62             :                  "%s codec:Encode(): output buffer too small",
      63             :                  m_osName.c_str());
      64             :     }
      65        5367 :     return bRet;
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /*                 ZarrV3CodecAbstractCompressor::Decode()              */
      70             : /************************************************************************/
      71             : 
      72        5261 : bool ZarrV3CodecAbstractCompressor::Decode(
      73             :     const ZarrByteVectorQuickResize &abySrc,
      74             :     ZarrByteVectorQuickResize &abyDst) const
      75             : {
      76        5261 :     abyDst.resize(abyDst.capacity());
      77        5046 :     void *pOutputData = abyDst.data();
      78        5158 :     size_t nOutputSize = abyDst.size();
      79        5102 :     bool bRet = m_pDecompressor->pfnFunc(abySrc.data(), abySrc.size(),
      80             :                                          &pOutputData, &nOutputSize, nullptr,
      81        5120 :                                          m_pDecompressor->user_data);
      82        5276 :     if (bRet)
      83             :     {
      84        5276 :         abyDst.resize(nOutputSize);
      85             :     }
      86           0 :     else if (nOutputSize > abyDst.size())
      87             :     {
      88           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      89             :                  "%s codec:Decode(): output buffer too small",
      90             :                  m_osName.c_str());
      91             :     }
      92        5107 :     return bRet;
      93             : }
      94             : 
      95             : /************************************************************************/
      96             : /*                        ZarrV3CodecGZip()                             */
      97             : /************************************************************************/
      98             : 
      99          56 : ZarrV3CodecGZip::ZarrV3CodecGZip() : ZarrV3CodecAbstractCompressor(NAME)
     100             : {
     101          56 : }
     102             : 
     103             : /************************************************************************/
     104             : /*                           GetConfiguration()                         */
     105             : /************************************************************************/
     106             : 
     107          23 : /* static */ CPLJSONObject ZarrV3CodecGZip::GetConfiguration(int nLevel)
     108             : {
     109          23 :     CPLJSONObject oConfig;
     110          23 :     oConfig.Add("level", nLevel);
     111          23 :     return oConfig;
     112             : }
     113             : 
     114             : /************************************************************************/
     115             : /*                   ZarrV3CodecGZip::InitFromConfiguration()           */
     116             : /************************************************************************/
     117             : 
     118          56 : bool ZarrV3CodecGZip::InitFromConfiguration(
     119             :     const CPLJSONObject &configuration,
     120             :     const ZarrArrayMetadata &oInputArrayMetadata,
     121             :     ZarrArrayMetadata &oOutputArrayMetadata)
     122             : {
     123          56 :     m_pCompressor = CPLGetCompressor("gzip");
     124          56 :     m_pDecompressor = CPLGetDecompressor("gzip");
     125          56 :     if (!m_pCompressor || !m_pDecompressor)
     126             :     {
     127           0 :         CPLError(CE_Failure, CPLE_AppDefined, "gzip compressor not available");
     128           0 :         return false;
     129             :     }
     130             : 
     131          56 :     m_oConfiguration = configuration.Clone();
     132          56 :     m_oInputArrayMetadata = oInputArrayMetadata;
     133             :     // byte->byte codec
     134          56 :     oOutputArrayMetadata = oInputArrayMetadata;
     135             : 
     136          56 :     int nLevel = 6;
     137             : 
     138          56 :     if (configuration.IsValid())
     139             :     {
     140          56 :         if (configuration.GetType() != CPLJSONObject::Type::Object)
     141             :         {
     142           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     143             :                      "Codec gzip: configuration is not an object");
     144           0 :             return false;
     145             :         }
     146             : 
     147         112 :         for (const auto &oChild : configuration.GetChildren())
     148             :         {
     149          56 :             if (oChild.GetName() != "level")
     150             :             {
     151           0 :                 CPLError(
     152             :                     CE_Failure, CPLE_AppDefined,
     153             :                     "Codec gzip: configuration contains a unhandled member: %s",
     154           0 :                     oChild.GetName().c_str());
     155           0 :                 return false;
     156             :             }
     157             :         }
     158             : 
     159         112 :         const auto oLevel = configuration.GetObj("level");
     160          56 :         if (oLevel.IsValid())
     161             :         {
     162          56 :             if (oLevel.GetType() != CPLJSONObject::Type::Integer)
     163             :             {
     164           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     165             :                          "Codec gzip: level is not an integer");
     166           0 :                 return false;
     167             :             }
     168          56 :             nLevel = oLevel.ToInteger();
     169          56 :             if (nLevel < 0 || nLevel > 9)
     170             :             {
     171           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     172             :                          "Codec gzip: invalid value for level: %d", nLevel);
     173           0 :                 return false;
     174             :             }
     175             :         }
     176             :     }
     177             : 
     178          56 :     m_aosCompressorOptions.SetNameValue("LEVEL", CPLSPrintf("%d", nLevel));
     179             : 
     180          56 :     return true;
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /*                      ZarrV3CodecGZip::Clone()                        */
     185             : /************************************************************************/
     186             : 
     187           8 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecGZip::Clone() const
     188             : {
     189          16 :     auto psClone = std::make_unique<ZarrV3CodecGZip>();
     190          16 :     ZarrArrayMetadata oOutputArrayMetadata;
     191           8 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     192             :                                    oOutputArrayMetadata);
     193          16 :     return psClone;
     194             : }
     195             : 
     196             : /************************************************************************/
     197             : /*                        ZarrV3CodecZstd()                             */
     198             : /************************************************************************/
     199             : 
     200           4 : ZarrV3CodecZstd::ZarrV3CodecZstd() : ZarrV3CodecAbstractCompressor(NAME)
     201             : {
     202           4 : }
     203             : 
     204             : /************************************************************************/
     205             : /*                           GetConfiguration()                         */
     206             : /************************************************************************/
     207             : 
     208           2 : /* static */ CPLJSONObject ZarrV3CodecZstd::GetConfiguration(int nLevel,
     209             :                                                              bool checksum)
     210             : {
     211           2 :     CPLJSONObject oConfig;
     212           2 :     oConfig.Add("level", nLevel);
     213           2 :     oConfig.Add("checksum", checksum);
     214           2 :     return oConfig;
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                   ZarrV3CodecZstd::InitFromConfiguration()           */
     219             : /************************************************************************/
     220             : 
     221           4 : bool ZarrV3CodecZstd::InitFromConfiguration(
     222             :     const CPLJSONObject &configuration,
     223             :     const ZarrArrayMetadata &oInputArrayMetadata,
     224             :     ZarrArrayMetadata &oOutputArrayMetadata)
     225             : {
     226           4 :     m_pCompressor = CPLGetCompressor("zstd");
     227           4 :     m_pDecompressor = CPLGetDecompressor("zstd");
     228           4 :     if (!m_pCompressor || !m_pDecompressor)
     229             :     {
     230           0 :         CPLError(CE_Failure, CPLE_AppDefined, "zstd compressor not available");
     231           0 :         return false;
     232             :     }
     233             : 
     234           4 :     m_oConfiguration = configuration.Clone();
     235           4 :     m_oInputArrayMetadata = oInputArrayMetadata;
     236             :     // byte->byte codec
     237           4 :     oOutputArrayMetadata = oInputArrayMetadata;
     238             : 
     239           4 :     int nLevel = 13;
     240           4 :     bool bChecksum = false;
     241             : 
     242           4 :     if (configuration.IsValid())
     243             :     {
     244           4 :         if (configuration.GetType() != CPLJSONObject::Type::Object)
     245             :         {
     246           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     247             :                      "Codec zstd: configuration is not an object");
     248           0 :             return false;
     249             :         }
     250             : 
     251          12 :         for (const auto &oChild : configuration.GetChildren())
     252             :         {
     253           8 :             if (oChild.GetName() != "level" && oChild.GetName() != "checksum")
     254             :             {
     255           0 :                 CPLError(
     256             :                     CE_Failure, CPLE_AppDefined,
     257             :                     "Codec zstd: configuration contains a unhandled member: %s",
     258           0 :                     oChild.GetName().c_str());
     259           0 :                 return false;
     260             :             }
     261             :         }
     262             : 
     263           8 :         const auto oLevel = configuration.GetObj("level");
     264           4 :         if (oLevel.IsValid())
     265             :         {
     266           4 :             if (oLevel.GetType() != CPLJSONObject::Type::Integer)
     267             :             {
     268           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     269             :                          "Codec zstd: level is not an integer");
     270           0 :                 return false;
     271             :             }
     272           4 :             nLevel = oLevel.ToInteger();
     273           4 :             if (nLevel < 0 || nLevel > 22)
     274             :             {
     275           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     276             :                          "Codec zstd: invalid value for level: %d", nLevel);
     277           0 :                 return false;
     278             :             }
     279             :         }
     280             : 
     281           8 :         const auto oChecksum = configuration.GetObj("checksum");
     282           4 :         if (oChecksum.IsValid())
     283             :         {
     284           4 :             if (oChecksum.GetType() != CPLJSONObject::Type::Boolean)
     285             :             {
     286           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     287             :                          "Codec zstd: checksum is not a boolean");
     288           0 :                 return false;
     289             :             }
     290           4 :             bChecksum = oChecksum.ToBool();
     291             :         }
     292             :     }
     293             : 
     294           4 :     m_aosCompressorOptions.SetNameValue("LEVEL", CPLSPrintf("%d", nLevel));
     295           4 :     if (bChecksum)
     296           2 :         m_aosCompressorOptions.SetNameValue("CHECKSUM", "YES");
     297             : 
     298           4 :     return true;
     299             : }
     300             : 
     301             : /************************************************************************/
     302             : /*                      ZarrV3CodecZstd::Clone()                        */
     303             : /************************************************************************/
     304             : 
     305           0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecZstd::Clone() const
     306             : {
     307           0 :     auto psClone = std::make_unique<ZarrV3CodecZstd>();
     308           0 :     ZarrArrayMetadata oOutputArrayMetadata;
     309           0 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     310             :                                    oOutputArrayMetadata);
     311           0 :     return psClone;
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*                       ZarrV3CodecBlosc()                             */
     316             : /************************************************************************/
     317             : 
     318           4 : ZarrV3CodecBlosc::ZarrV3CodecBlosc() : ZarrV3CodecAbstractCompressor(NAME)
     319             : {
     320           4 : }
     321             : 
     322             : /************************************************************************/
     323             : /*                           GetConfiguration()                         */
     324             : /************************************************************************/
     325             : 
     326             : /* static */ CPLJSONObject
     327           2 : ZarrV3CodecBlosc::GetConfiguration(const char *cname, int clevel,
     328             :                                    const char *shuffle, int typesize,
     329             :                                    int blocksize)
     330             : {
     331           2 :     CPLJSONObject oConfig;
     332           2 :     oConfig.Add("cname", cname);
     333           2 :     oConfig.Add("clevel", clevel);
     334           2 :     oConfig.Add("shuffle", shuffle);
     335           2 :     if (strcmp(shuffle, "noshuffle") != 0)
     336           1 :         oConfig.Add("typesize", typesize);
     337           2 :     oConfig.Add("blocksize", blocksize);
     338           2 :     return oConfig;
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                   ZarrV3CodecBlosc::InitFromConfiguration()           */
     343             : /************************************************************************/
     344             : 
     345           4 : bool ZarrV3CodecBlosc::InitFromConfiguration(
     346             :     const CPLJSONObject &configuration,
     347             :     const ZarrArrayMetadata &oInputArrayMetadata,
     348             :     ZarrArrayMetadata &oOutputArrayMetadata)
     349             : {
     350           4 :     m_pCompressor = CPLGetCompressor("blosc");
     351           4 :     m_pDecompressor = CPLGetDecompressor("blosc");
     352           4 :     if (!m_pCompressor || !m_pDecompressor)
     353             :     {
     354           0 :         CPLError(CE_Failure, CPLE_AppDefined, "blosc compressor not available");
     355           0 :         return false;
     356             :     }
     357             : 
     358           4 :     m_oConfiguration = configuration.Clone();
     359           4 :     m_oInputArrayMetadata = oInputArrayMetadata;
     360             :     // byte->byte codec
     361           4 :     oOutputArrayMetadata = oInputArrayMetadata;
     362             : 
     363           8 :     if (!configuration.IsValid() ||
     364           4 :         configuration.GetType() != CPLJSONObject::Type::Object)
     365             :     {
     366           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     367             :                  "Codec blosc: configuration missing or not an object");
     368           0 :         return false;
     369             :     }
     370             : 
     371          22 :     for (const auto &oChild : configuration.GetChildren())
     372             :     {
     373          18 :         const auto osName = oChild.GetName();
     374          32 :         if (osName != "cname" && osName != "clevel" && osName != "shuffle" &&
     375          32 :             osName != "typesize" && osName != "blocksize")
     376             :         {
     377           0 :             CPLError(
     378             :                 CE_Failure, CPLE_AppDefined,
     379             :                 "Codec blosc: configuration contains a unhandled member: %s",
     380             :                 osName.c_str());
     381           0 :             return false;
     382             :         }
     383             :     }
     384             : 
     385          12 :     const auto oCname = configuration.GetObj("cname");
     386           4 :     if (oCname.GetType() != CPLJSONObject::Type::String)
     387             :     {
     388           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     389             :                  "Codec blosc: cname is missing or not a string");
     390           0 :         return false;
     391             :     }
     392           4 :     m_aosCompressorOptions.SetNameValue("CNAME", oCname.ToString().c_str());
     393             : 
     394          12 :     const auto oLevel = configuration.GetObj("clevel");
     395           4 :     if (oLevel.IsValid())
     396             :     {
     397           4 :         if (oLevel.GetType() != CPLJSONObject::Type::Integer)
     398             :         {
     399           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     400             :                      "Codec blosc: clevel is not an integer");
     401           0 :             return false;
     402             :         }
     403           4 :         const int nLevel = oLevel.ToInteger();
     404           4 :         if (nLevel < 0 || nLevel > 9)
     405             :         {
     406           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     407             :                      "Codec blosc: clevel value for level: %d", nLevel);
     408           0 :             return false;
     409             :         }
     410           4 :         m_aosCompressorOptions.SetNameValue("CLEVEL", CPLSPrintf("%d", nLevel));
     411             :     }
     412             : 
     413          12 :     const auto oShuffle = configuration.GetObj("shuffle");
     414           4 :     if (oShuffle.GetType() != CPLJSONObject::Type::String)
     415             :     {
     416           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     417             :                  "Codec blosc: shuffle is missing or not a string");
     418           0 :         return false;
     419             :     }
     420           4 :     if (oShuffle.ToString() == "noshuffle")
     421           2 :         m_aosCompressorOptions.SetNameValue("SHUFFLE", "NONE");
     422           2 :     else if (oShuffle.ToString() == "shuffle")
     423           2 :         m_aosCompressorOptions.SetNameValue("SHUFFLE", "BYTE");
     424           0 :     else if (oShuffle.ToString() == "bitshuffle")
     425           0 :         m_aosCompressorOptions.SetNameValue("SHUFFLE", "BIT");
     426             :     else
     427             :     {
     428           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     429             :                  "Codec blosc: Invalid value for shuffle");
     430           0 :         return false;
     431             :     }
     432             : 
     433          12 :     const auto oTypesize = configuration.GetObj("typesize");
     434           4 :     if (oTypesize.IsValid())
     435             :     {
     436           2 :         if (oTypesize.GetType() != CPLJSONObject::Type::Integer)
     437             :         {
     438           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     439             :                      "Codec blosc: typesize is not an integer");
     440           0 :             return false;
     441             :         }
     442           2 :         const int nTypeSize = oTypesize.ToInteger();
     443             :         m_aosCompressorOptions.SetNameValue("TYPESIZE",
     444           2 :                                             CPLSPrintf("%d", nTypeSize));
     445             :     }
     446             : 
     447          12 :     const auto oBlocksize = configuration.GetObj("blocksize");
     448           4 :     if (oBlocksize.IsValid())
     449             :     {
     450           4 :         if (oBlocksize.GetType() != CPLJSONObject::Type::Integer)
     451             :         {
     452           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     453             :                      "Codec blosc: blocksize is not an integer");
     454           0 :             return false;
     455             :         }
     456           4 :         const int nBlocksize = oBlocksize.ToInteger();
     457             :         m_aosCompressorOptions.SetNameValue("BLOCKSIZE",
     458           4 :                                             CPLSPrintf("%d", nBlocksize));
     459             :     }
     460             : 
     461           4 :     return true;
     462             : }
     463             : 
     464             : /************************************************************************/
     465             : /*                      ZarrV3CodecBlosc::Clone()                        */
     466             : /************************************************************************/
     467             : 
     468           0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecBlosc::Clone() const
     469             : {
     470           0 :     auto psClone = std::make_unique<ZarrV3CodecBlosc>();
     471           0 :     ZarrArrayMetadata oOutputArrayMetadata;
     472           0 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     473             :                                    oOutputArrayMetadata);
     474           0 :     return psClone;
     475             : }
     476             : 
     477             : /************************************************************************/
     478             : /*                       ZarrV3CodecBytes()                            */
     479             : /************************************************************************/
     480             : 
     481         266 : ZarrV3CodecBytes::ZarrV3CodecBytes() : ZarrV3Codec(NAME)
     482             : {
     483         266 : }
     484             : 
     485             : /************************************************************************/
     486             : /*                           GetConfiguration()                         */
     487             : /************************************************************************/
     488             : 
     489         126 : /* static */ CPLJSONObject ZarrV3CodecBytes::GetConfiguration(bool bLittle)
     490             : {
     491         126 :     CPLJSONObject oConfig;
     492         126 :     oConfig.Add("endian", bLittle ? "little" : "big");
     493         126 :     return oConfig;
     494             : }
     495             : 
     496             : /************************************************************************/
     497             : /*                 ZarrV3CodecBytes::InitFromConfiguration()            */
     498             : /************************************************************************/
     499             : 
     500         266 : bool ZarrV3CodecBytes::InitFromConfiguration(
     501             :     const CPLJSONObject &configuration,
     502             :     const ZarrArrayMetadata &oInputArrayMetadata,
     503             :     ZarrArrayMetadata &oOutputArrayMetadata)
     504             : {
     505         266 :     m_oConfiguration = configuration.Clone();
     506         266 :     m_bLittle = true;
     507         266 :     m_oInputArrayMetadata = oInputArrayMetadata;
     508         266 :     oOutputArrayMetadata = oInputArrayMetadata;
     509             : 
     510         266 :     if (configuration.IsValid())
     511             :     {
     512         255 :         if (configuration.GetType() != CPLJSONObject::Type::Object)
     513             :         {
     514           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     515             :                      "Codec endian: configuration is not an object");
     516           0 :             return false;
     517             :         }
     518             : 
     519         510 :         for (const auto &oChild : configuration.GetChildren())
     520             :         {
     521         255 :             if (oChild.GetName() != "endian")
     522             :             {
     523           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     524             :                          "Codec endian: configuration contains a unhandled "
     525             :                          "member: %s",
     526           0 :                          oChild.GetName().c_str());
     527           0 :                 return false;
     528             :             }
     529             :         }
     530             : 
     531         510 :         const auto oEndian = configuration.GetObj("endian");
     532         255 :         if (oEndian.IsValid())
     533             :         {
     534         255 :             if (oEndian.GetType() != CPLJSONObject::Type::String)
     535             :             {
     536           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     537             :                          "Codec gzip: endian is not a string");
     538           0 :                 return false;
     539             :             }
     540         255 :             if (oEndian.ToString() == "little")
     541         194 :                 m_bLittle = true;
     542          61 :             else if (oEndian.ToString() == "big")
     543          61 :                 m_bLittle = false;
     544             :             else
     545             :             {
     546           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     547             :                          "Codec gzip: invalid value for endian");
     548           0 :                 return false;
     549             :             }
     550             :         }
     551             :     }
     552             : 
     553         266 :     return true;
     554             : }
     555             : 
     556             : /************************************************************************/
     557             : /*                     ZarrV3CodecBytes::Clone()                        */
     558             : /************************************************************************/
     559             : 
     560           0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecBytes::Clone() const
     561             : {
     562           0 :     auto psClone = std::make_unique<ZarrV3CodecBytes>();
     563           0 :     ZarrArrayMetadata oOutputArrayMetadata;
     564           0 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     565             :                                    oOutputArrayMetadata);
     566           0 :     return psClone;
     567             : }
     568             : 
     569             : /************************************************************************/
     570             : /*                      ZarrV3CodecBytes::Encode()                      */
     571             : /************************************************************************/
     572             : 
     573          48 : bool ZarrV3CodecBytes::Encode(const ZarrByteVectorQuickResize &abySrc,
     574             :                               ZarrByteVectorQuickResize &abyDst) const
     575             : {
     576          48 :     CPLAssert(!IsNoOp());
     577             : 
     578          48 :     size_t nEltCount = m_oInputArrayMetadata.GetEltCount();
     579          48 :     size_t nNativeSize = m_oInputArrayMetadata.oElt.nativeSize;
     580          48 :     if (abySrc.size() < nEltCount * nNativeSize)
     581             :     {
     582           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     583             :                  "ZarrV3CodecBytes::Encode(): input buffer too small");
     584           0 :         return false;
     585             :     }
     586          48 :     CPLAssert(abySrc.size() >= nEltCount * nNativeSize);
     587          48 :     abyDst.resize(nEltCount * nNativeSize);
     588             : 
     589          48 :     const GByte *pabySrc = abySrc.data();
     590          48 :     GByte *pabyDst = abyDst.data();
     591             : 
     592          48 :     if (m_oInputArrayMetadata.oElt.nativeType ==
     593             :         DtypeElt::NativeType::COMPLEX_IEEEFP)
     594             :     {
     595           0 :         nEltCount *= 2;
     596           0 :         nNativeSize /= 2;
     597             :     }
     598          48 :     if (nNativeSize == 2)
     599             :     {
     600          36 :         for (size_t i = 0; i < nEltCount; ++i)
     601             :         {
     602          24 :             const uint16_t val = CPL_SWAP16(*reinterpret_cast<const uint16_t *>(
     603             :                 pabySrc + sizeof(uint16_t) * i));
     604          24 :             memcpy(pabyDst + sizeof(uint16_t) * i, &val, sizeof(val));
     605             :         }
     606             :     }
     607          36 :     else if (nNativeSize == 4)
     608             :     {
     609          54 :         for (size_t i = 0; i < nEltCount; ++i)
     610             :         {
     611          36 :             const uint32_t val = CPL_SWAP32(*reinterpret_cast<const uint32_t *>(
     612             :                 pabySrc + sizeof(uint32_t) * i));
     613          36 :             memcpy(pabyDst + sizeof(uint32_t) * i, &val, sizeof(val));
     614             :         }
     615             :     }
     616          18 :     else if (nNativeSize == 8)
     617             :     {
     618          54 :         for (size_t i = 0; i < nEltCount; ++i)
     619             :         {
     620          36 :             const uint64_t val = CPL_SWAP64(*reinterpret_cast<const uint64_t *>(
     621             :                 pabySrc + sizeof(uint64_t) * i));
     622          36 :             memcpy(pabyDst + sizeof(uint64_t) * i, &val, sizeof(val));
     623             :         }
     624             :     }
     625             :     else
     626             :     {
     627           0 :         CPLAssert(false);
     628             :     }
     629          48 :     return true;
     630             : }
     631             : 
     632             : /************************************************************************/
     633             : /*                      ZarrV3CodecBytes::Decode()                      */
     634             : /************************************************************************/
     635             : 
     636          24 : bool ZarrV3CodecBytes::Decode(const ZarrByteVectorQuickResize &abySrc,
     637             :                               ZarrByteVectorQuickResize &abyDst) const
     638             : {
     639          24 :     return Encode(abySrc, abyDst);
     640             : }
     641             : 
     642             : /************************************************************************/
     643             : /*                       ZarrV3CodecTranspose()                         */
     644             : /************************************************************************/
     645             : 
     646          81 : ZarrV3CodecTranspose::ZarrV3CodecTranspose() : ZarrV3Codec(NAME)
     647             : {
     648          81 : }
     649             : 
     650             : /************************************************************************/
     651             : /*                             IsNoOp()                                 */
     652             : /************************************************************************/
     653             : 
     654         182 : bool ZarrV3CodecTranspose::IsNoOp() const
     655             : {
     656         182 :     for (int i = 0; i < static_cast<int>(m_anOrder.size()); ++i)
     657             :     {
     658         182 :         if (m_anOrder[i] != i)
     659         182 :             return false;
     660             :     }
     661           0 :     return true;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                           GetConfiguration()                         */
     666             : /************************************************************************/
     667             : 
     668             : /* static */ CPLJSONObject
     669          40 : ZarrV3CodecTranspose::GetConfiguration(const std::vector<int> &anOrder)
     670             : {
     671          40 :     CPLJSONObject oConfig;
     672          80 :     CPLJSONArray oOrder;
     673         130 :     for (const auto nVal : anOrder)
     674          90 :         oOrder.Add(nVal);
     675          40 :     oConfig.Add("order", oOrder);
     676          80 :     return oConfig;
     677             : }
     678             : 
     679             : /************************************************************************/
     680             : /*                ZarrV3CodecTranspose::InitFromConfiguration()         */
     681             : /************************************************************************/
     682             : 
     683          81 : bool ZarrV3CodecTranspose::InitFromConfiguration(
     684             :     const CPLJSONObject &configuration,
     685             :     const ZarrArrayMetadata &oInputArrayMetadata,
     686             :     ZarrArrayMetadata &oOutputArrayMetadata)
     687             : {
     688          81 :     m_oConfiguration = configuration.Clone();
     689          81 :     m_oInputArrayMetadata = oInputArrayMetadata;
     690          81 :     oOutputArrayMetadata = oInputArrayMetadata;
     691             : 
     692          81 :     if (!configuration.IsValid() &&
     693           0 :         configuration.GetType() != CPLJSONObject::Type::Object)
     694             :     {
     695           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     696             :                  "Codec transpose: configuration missing or not an object");
     697           0 :         return false;
     698             :     }
     699             : 
     700         162 :     for (const auto &oChild : configuration.GetChildren())
     701             :     {
     702          81 :         if (oChild.GetName() != "order")
     703             :         {
     704           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     705             :                      "Codec transpose: configuration contains a unhandled "
     706             :                      "member: %s",
     707           0 :                      oChild.GetName().c_str());
     708           0 :             return false;
     709             :         }
     710             :     }
     711             : 
     712         243 :     const auto oOrder = configuration.GetObj("order");
     713          81 :     const int nDims = static_cast<int>(oInputArrayMetadata.anBlockSizes.size());
     714          81 :     if (oOrder.GetType() == CPLJSONObject::Type::String)
     715             :     {
     716             :         // Deprecated
     717           0 :         const auto osOrder = oOrder.ToString();
     718           0 :         if (osOrder == "C")
     719             :         {
     720           0 :             for (int i = 0; i < nDims; ++i)
     721             :             {
     722           0 :                 m_anOrder.push_back(i);
     723             :             }
     724             :         }
     725           0 :         else if (osOrder == "F")
     726             :         {
     727           0 :             for (int i = 0; i < nDims; ++i)
     728             :             {
     729           0 :                 m_anOrder.push_back(nDims - 1 - i);
     730           0 :                 oOutputArrayMetadata.anBlockSizes[i] =
     731           0 :                     oInputArrayMetadata.anBlockSizes[nDims - 1 - i];
     732             :             }
     733             :         }
     734             :         else
     735             :         {
     736           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     737             :                      "Codec transpose: invalid value for order");
     738           0 :             return false;
     739             :         }
     740             :     }
     741          81 :     else if (oOrder.GetType() == CPLJSONObject::Type::Array)
     742             :     {
     743          81 :         const auto oOrderArray = oOrder.ToArray();
     744          81 :         if (oOrderArray.Size() != nDims)
     745             :         {
     746           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     747             :                      "Codec transpose: order[] does not have the expected "
     748             :                      "number of elements");
     749           0 :             return false;
     750             :         }
     751          81 :         std::vector<int> oSet(nDims);
     752          81 :         oOutputArrayMetadata.anBlockSizes.clear();
     753         263 :         for (const auto &oVal : oOrderArray)
     754             :         {
     755         182 :             const int nVal = oVal.ToInteger();
     756         182 :             if (nVal < 0 || nVal >= nDims || oSet[nVal])
     757             :             {
     758           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     759             :                          "Codec transpose: order[] does not define a valid "
     760             :                          "transposition");
     761           0 :                 return false;
     762             :             }
     763         182 :             oSet[nVal] = true;
     764         182 :             m_anOrder.push_back(nVal);
     765         182 :             oOutputArrayMetadata.anBlockSizes.push_back(
     766         182 :                 oInputArrayMetadata.anBlockSizes[nVal]);
     767             :         }
     768             :     }
     769             :     else
     770             :     {
     771           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     772             :                  "Codec transpose: invalid value for order");
     773           0 :         return false;
     774             :     }
     775             : 
     776          81 :     int i = 0;
     777          81 :     m_anReverseOrder.resize(m_anOrder.size());
     778         263 :     for (const auto nVal : m_anOrder)
     779             :     {
     780         182 :         m_anReverseOrder[nVal] = i;
     781         182 :         ++i;
     782             :     }
     783             : 
     784          81 :     return true;
     785             : }
     786             : 
     787             : /************************************************************************/
     788             : /*                   ZarrV3CodecTranspose::Clone()                      */
     789             : /************************************************************************/
     790             : 
     791           0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecTranspose::Clone() const
     792             : {
     793           0 :     auto psClone = std::make_unique<ZarrV3CodecTranspose>();
     794           0 :     ZarrArrayMetadata oOutputArrayMetadata;
     795           0 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     796             :                                    oOutputArrayMetadata);
     797           0 :     return psClone;
     798             : }
     799             : 
     800             : /************************************************************************/
     801             : /*                  ZarrV3CodecTranspose::Transpose()                   */
     802             : /************************************************************************/
     803             : 
     804         100 : bool ZarrV3CodecTranspose::Transpose(const ZarrByteVectorQuickResize &abySrc,
     805             :                                      ZarrByteVectorQuickResize &abyDst,
     806             :                                      bool bEncodeDirection) const
     807             : {
     808         100 :     CPLAssert(m_anOrder.size() == m_oInputArrayMetadata.anBlockSizes.size());
     809         100 :     CPLAssert(m_anReverseOrder.size() ==
     810             :               m_oInputArrayMetadata.anBlockSizes.size());
     811         100 :     const size_t nDims = m_anOrder.size();
     812         100 :     const size_t nSourceSize = m_oInputArrayMetadata.oElt.nativeSize;
     813         100 :     const auto &anBlockSizes = m_oInputArrayMetadata.anBlockSizes;
     814         100 :     CPLAssert(nDims > 0);
     815         100 :     if (abySrc.size() < m_oInputArrayMetadata.GetEltCount() * nSourceSize)
     816             :     {
     817           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     818             :                  "ZarrV3CodecTranspose::Transpose(): input buffer too small");
     819           0 :         return false;
     820             :     }
     821         100 :     abyDst.resize(m_oInputArrayMetadata.GetEltCount() * nSourceSize);
     822             : 
     823             :     struct Stack
     824             :     {
     825             :         size_t nIters = 0;
     826             :         const GByte *src_ptr = nullptr;
     827             :         GByte *dst_ptr = nullptr;
     828             :         size_t src_inc_offset = 0;
     829             :         size_t dst_inc_offset = 0;
     830             :     };
     831             : 
     832             : #if defined(__GNUC__)
     833             : #pragma GCC diagnostic push
     834             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     835             : #endif
     836         100 :     std::vector<Stack> stack(nDims);
     837             :     stack.emplace_back(
     838         100 :         Stack());  // to make gcc 9.3 -O2 -Wnull-dereference happy
     839             : #if defined(__GNUC__)
     840             : #pragma GCC diagnostic pop
     841             : #endif
     842             : 
     843         100 :     if (!bEncodeDirection)
     844             :     {
     845          50 :         stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
     846          50 :         size_t nStride = nSourceSize;
     847         120 :         for (size_t i = nDims - 1; i > 0;)
     848             :         {
     849          70 :             --i;
     850          70 :             nStride *=
     851          70 :                 static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
     852          70 :             stack[m_anReverseOrder[i]].src_inc_offset = nStride;
     853             :         }
     854             : 
     855          50 :         stack[nDims - 1].dst_inc_offset = nSourceSize;
     856          50 :         nStride = nSourceSize;
     857         120 :         for (size_t i = nDims - 1; i > 0;)
     858             :         {
     859          70 :             --i;
     860          70 :             nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
     861          70 :             stack[i].dst_inc_offset = nStride;
     862             :         }
     863             :     }
     864             :     else
     865             :     {
     866          50 :         stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
     867          50 :         size_t nStride = nSourceSize;
     868         120 :         for (size_t i = nDims - 1; i > 0;)
     869             :         {
     870          70 :             --i;
     871          70 :             nStride *=
     872          70 :                 static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
     873          70 :             stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
     874             :         }
     875             : 
     876          50 :         stack[nDims - 1].src_inc_offset = nSourceSize;
     877          50 :         nStride = nSourceSize;
     878         120 :         for (size_t i = nDims - 1; i > 0;)
     879             :         {
     880          70 :             --i;
     881          70 :             nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
     882          70 :             stack[i].src_inc_offset = nStride;
     883             :         }
     884             :     }
     885             : 
     886         100 :     stack[0].src_ptr = abySrc.data();
     887         100 :     stack[0].dst_ptr = &abyDst[0];
     888             : 
     889         100 :     size_t dimIdx = 0;
     890         980 : lbl_next_depth:
     891         980 :     if (dimIdx == nDims)
     892             :     {
     893         600 :         void *dst_ptr = stack[nDims].dst_ptr;
     894         600 :         const void *src_ptr = stack[nDims].src_ptr;
     895         600 :         if (nSourceSize == 1)
     896         120 :             *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
     897         480 :         else if (nSourceSize == 2)
     898         120 :             *static_cast<uint16_t *>(dst_ptr) =
     899         120 :                 *static_cast<const uint16_t *>(src_ptr);
     900         360 :         else if (nSourceSize == 4)
     901         180 :             *static_cast<uint32_t *>(dst_ptr) =
     902         180 :                 *static_cast<const uint32_t *>(src_ptr);
     903         180 :         else if (nSourceSize == 8)
     904         180 :             *static_cast<uint64_t *>(dst_ptr) =
     905         180 :                 *static_cast<const uint64_t *>(src_ptr);
     906             :         else
     907           0 :             memcpy(dst_ptr, src_ptr, nSourceSize);
     908             :     }
     909             :     else
     910             :     {
     911         380 :         stack[dimIdx].nIters = static_cast<size_t>(anBlockSizes[dimIdx]);
     912             :         while (true)
     913             :         {
     914         880 :             dimIdx++;
     915         880 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
     916         880 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     917         880 :             goto lbl_next_depth;
     918         880 :         lbl_return_to_caller:
     919         880 :             dimIdx--;
     920         880 :             if ((--stack[dimIdx].nIters) == 0)
     921         380 :                 break;
     922         500 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
     923         500 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     924             :         }
     925             :     }
     926         980 :     if (dimIdx > 0)
     927         880 :         goto lbl_return_to_caller;
     928             : 
     929         100 :     return true;
     930             : }
     931             : 
     932             : /************************************************************************/
     933             : /*                    ZarrV3CodecTranspose::Encode()                    */
     934             : /************************************************************************/
     935             : 
     936          50 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
     937             :                                   ZarrByteVectorQuickResize &abyDst) const
     938             : {
     939          50 :     CPLAssert(!IsNoOp());
     940             : 
     941          50 :     return Transpose(abySrc, abyDst, true);
     942             : }
     943             : 
     944             : /************************************************************************/
     945             : /*                    ZarrV3CodecTranspose::Decode()                    */
     946             : /************************************************************************/
     947             : 
     948          50 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
     949             :                                   ZarrByteVectorQuickResize &abyDst) const
     950             : {
     951          50 :     CPLAssert(!IsNoOp());
     952             : 
     953          50 :     return Transpose(abySrc, abyDst, false);
     954             : }
     955             : 
     956             : /************************************************************************/
     957             : /*                    ZarrV3CodecSequence::Clone()                      */
     958             : /************************************************************************/
     959             : 
     960          16 : std::unique_ptr<ZarrV3CodecSequence> ZarrV3CodecSequence::Clone() const
     961             : {
     962          16 :     auto poClone = std::make_unique<ZarrV3CodecSequence>(m_oInputArrayMetadata);
     963          24 :     for (const auto &poCodec : m_apoCodecs)
     964           8 :         poClone->m_apoCodecs.emplace_back(poCodec->Clone());
     965          16 :     poClone->m_oCodecArray = m_oCodecArray.Clone();
     966          16 :     return poClone;
     967             : }
     968             : 
     969             : /************************************************************************/
     970             : /*                    ZarrV3CodecSequence::InitFromJson()               */
     971             : /************************************************************************/
     972             : 
     973         266 : bool ZarrV3CodecSequence::InitFromJson(const CPLJSONObject &oCodecs)
     974             : {
     975         266 :     if (oCodecs.GetType() != CPLJSONObject::Type::Array)
     976             :     {
     977           0 :         CPLError(CE_Failure, CPLE_AppDefined, "codecs is not an array");
     978           0 :         return false;
     979             :     }
     980         532 :     auto oCodecsArray = oCodecs.ToArray();
     981             : 
     982         532 :     ZarrArrayMetadata oInputArrayMetadata = m_oInputArrayMetadata;
     983         266 :     ZarrV3Codec::IOType eLastType = ZarrV3Codec::IOType::ARRAY;
     984         532 :     std::string osLastCodec;
     985             : 
     986             :     const auto InsertImplicitEndianCodecIfNeeded =
     987         644 :         [this, &oInputArrayMetadata, &eLastType, &osLastCodec]()
     988             :     {
     989         322 :         CPL_IGNORE_RET_VAL(this);
     990         322 :         if (eLastType == ZarrV3Codec::IOType::ARRAY &&
     991           0 :             oInputArrayMetadata.oElt.nativeSize > 1)
     992             :         {
     993           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     994             :                      "'bytes' codec missing. Assuming little-endian storage, "
     995             :                      "but such tolerance may be removed in future versions");
     996           0 :             auto poEndianCodec = std::make_unique<ZarrV3CodecBytes>();
     997           0 :             ZarrArrayMetadata oOutputArrayMetadata;
     998           0 :             poEndianCodec->InitFromConfiguration(
     999           0 :                 ZarrV3CodecBytes::GetConfiguration(true), oInputArrayMetadata,
    1000             :                 oOutputArrayMetadata);
    1001           0 :             oInputArrayMetadata = std::move(oOutputArrayMetadata);
    1002           0 :             eLastType = poEndianCodec->GetOutputType();
    1003           0 :             osLastCodec = poEndianCodec->GetName();
    1004             :             if constexpr (!CPL_IS_LSB)
    1005             :             {
    1006             :                 // Insert a little endian codec if we are on a big endian target
    1007             :                 m_apoCodecs.emplace_back(std::move(poEndianCodec));
    1008             :             }
    1009             :         }
    1010         322 :     };
    1011             : 
    1012         669 :     for (const auto &oCodec : oCodecsArray)
    1013             :     {
    1014         403 :         if (oCodec.GetType() != CPLJSONObject::Type::Object)
    1015             :         {
    1016           0 :             CPLError(CE_Failure, CPLE_AppDefined, "codecs[] is not an array");
    1017           0 :             return false;
    1018             :         }
    1019         806 :         const auto osName = oCodec["name"].ToString();
    1020           0 :         std::unique_ptr<ZarrV3Codec> poCodec;
    1021         403 :         if (osName == "gzip")
    1022          48 :             poCodec = std::make_unique<ZarrV3CodecGZip>();
    1023         355 :         else if (osName == "blosc")
    1024           4 :             poCodec = std::make_unique<ZarrV3CodecBlosc>();
    1025         351 :         else if (osName == "zstd")
    1026           4 :             poCodec = std::make_unique<ZarrV3CodecZstd>();
    1027         428 :         else if (osName == "bytes" ||
    1028          81 :                  osName == "endian" /* endian is the old name */)
    1029         266 :             poCodec = std::make_unique<ZarrV3CodecBytes>();
    1030          81 :         else if (osName == "transpose")
    1031          81 :             poCodec = std::make_unique<ZarrV3CodecTranspose>();
    1032             :         else
    1033             :         {
    1034           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unsupported codec: %s",
    1035             :                      osName.c_str());
    1036           0 :             return false;
    1037             :         }
    1038             : 
    1039         403 :         if (poCodec->GetInputType() == ZarrV3Codec::IOType::ARRAY)
    1040             :         {
    1041         347 :             if (eLastType == ZarrV3Codec::IOType::BYTES)
    1042             :             {
    1043           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1044             :                          "Cannot chain codec %s with %s",
    1045           0 :                          poCodec->GetName().c_str(), osLastCodec.c_str());
    1046           0 :                 return false;
    1047             :             }
    1048             :         }
    1049             :         else
    1050             :         {
    1051          56 :             InsertImplicitEndianCodecIfNeeded();
    1052             :         }
    1053             : 
    1054         403 :         ZarrArrayMetadata oOutputArrayMetadata;
    1055         806 :         if (!poCodec->InitFromConfiguration(oCodec["configuration"],
    1056             :                                             oInputArrayMetadata,
    1057         403 :                                             oOutputArrayMetadata))
    1058             :         {
    1059           0 :             return false;
    1060             :         }
    1061         403 :         oInputArrayMetadata = std::move(oOutputArrayMetadata);
    1062         403 :         eLastType = poCodec->GetOutputType();
    1063         403 :         osLastCodec = poCodec->GetName();
    1064             : 
    1065         403 :         if (!poCodec->IsNoOp())
    1066         186 :             m_apoCodecs.emplace_back(std::move(poCodec));
    1067             :     }
    1068             : 
    1069         266 :     InsertImplicitEndianCodecIfNeeded();
    1070             : 
    1071         266 :     m_oCodecArray = oCodecs.Clone();
    1072         266 :     return true;
    1073             : }
    1074             : 
    1075             : /************************************************************************/
    1076             : /*                  ZarrV3CodecBytes::AllocateBuffer()                 */
    1077             : /************************************************************************/
    1078             : 
    1079       21345 : bool ZarrV3CodecSequence::AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer)
    1080             : {
    1081       21345 :     if (!m_apoCodecs.empty())
    1082             :     {
    1083       10436 :         const size_t nRawSize = m_oInputArrayMetadata.GetEltCount() *
    1084       10539 :                                 m_oInputArrayMetadata.oElt.nativeSize;
    1085             :         // Grow the temporary buffer a bit beyond the uncompressed size
    1086       10539 :         const size_t nMaxSize = nRawSize + nRawSize / 3 + 64;
    1087             :         try
    1088             :         {
    1089       10539 :             m_abyTmp.resize(nMaxSize);
    1090             :         }
    1091           0 :         catch (const std::exception &e)
    1092             :         {
    1093           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1094           0 :             return false;
    1095             :         }
    1096       10602 :         m_abyTmp.resize(nRawSize);
    1097             : 
    1098             :         // Grow the input/output buffer too if we have several steps
    1099       10499 :         if (m_apoCodecs.size() >= 2 && abyBuffer.capacity() < nMaxSize)
    1100             :         {
    1101          56 :             const size_t nSize = abyBuffer.size();
    1102             :             try
    1103             :             {
    1104          56 :                 abyBuffer.resize(nMaxSize);
    1105             :             }
    1106           0 :             catch (const std::exception &e)
    1107             :             {
    1108           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1109           0 :                 return false;
    1110             :             }
    1111          56 :             abyBuffer.resize(nSize);
    1112             :         }
    1113             :     }
    1114       20828 :     return true;
    1115             : }
    1116             : 
    1117             : /************************************************************************/
    1118             : /*                    ZarrV3CodecSequence::Encode()                     */
    1119             : /************************************************************************/
    1120             : 
    1121       10747 : bool ZarrV3CodecSequence::Encode(ZarrByteVectorQuickResize &abyBuffer)
    1122             : {
    1123       10747 :     if (!AllocateBuffer(abyBuffer))
    1124           0 :         return false;
    1125       16188 :     for (const auto &poCodec : m_apoCodecs)
    1126             :     {
    1127        5441 :         if (!poCodec->Encode(abyBuffer, m_abyTmp))
    1128           0 :             return false;
    1129        5441 :         std::swap(abyBuffer, m_abyTmp);
    1130             :     }
    1131       10747 :     return true;
    1132             : }
    1133             : 
    1134             : /************************************************************************/
    1135             : /*                    ZarrV3CodecSequence::Decode()                     */
    1136             : /************************************************************************/
    1137             : 
    1138       10631 : bool ZarrV3CodecSequence::Decode(ZarrByteVectorQuickResize &abyBuffer)
    1139             : {
    1140       10631 :     if (!AllocateBuffer(abyBuffer))
    1141           0 :         return false;
    1142       15593 :     for (auto iter = m_apoCodecs.rbegin(); iter != m_apoCodecs.rend(); ++iter)
    1143             :     {
    1144        5044 :         const auto &poCodec = *iter;
    1145        5008 :         if (!poCodec->Decode(abyBuffer, m_abyTmp))
    1146           0 :             return false;
    1147        5335 :         std::swap(abyBuffer, m_abyTmp);
    1148             :     }
    1149       10301 :     return true;
    1150             : }

Generated by: LCOV version 1.14