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

Generated by: LCOV version 1.14