LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_v3_codec.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 366 518 70.7 %
Date: 2025-02-20 10:14:44 Functions: 32 37 86.5 %

          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         392 : ZarrV3Codec::ZarrV3Codec(const std::string &osName) : m_osName(osName)
      22             : {
      23         392 : }
      24             : 
      25             : /************************************************************************/
      26             : /*                         ~ZarrV3Codec()                               */
      27             : /************************************************************************/
      28             : 
      29             : ZarrV3Codec::~ZarrV3Codec() = default;
      30             : 
      31             : /************************************************************************/
      32             : /*                      ZarrV3CodecAbstractCompressor()                 */
      33             : /************************************************************************/
      34             : 
      35          62 : ZarrV3CodecAbstractCompressor::ZarrV3CodecAbstractCompressor(
      36          62 :     const std::string &osName)
      37          62 :     : ZarrV3Codec(osName)
      38             : {
      39          62 : }
      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        5295 : bool ZarrV3CodecAbstractCompressor::Decode(
      73             :     const ZarrByteVectorQuickResize &abySrc,
      74             :     ZarrByteVectorQuickResize &abyDst) const
      75             : {
      76        5295 :     abyDst.resize(abyDst.capacity());
      77        5147 :     void *pOutputData = abyDst.data();
      78        5041 :     size_t nOutputSize = abyDst.size();
      79        5117 :     bool bRet = m_pDecompressor->pfnFunc(abySrc.data(), abySrc.size(),
      80             :                                          &pOutputData, &nOutputSize, nullptr,
      81        5031 :                                          m_pDecompressor->user_data);
      82        5324 :     if (bRet)
      83             :     {
      84        5324 :         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        5120 :     return bRet;
      93             : }
      94             : 
      95             : /************************************************************************/
      96             : /*                        ZarrV3CodecGZip()                             */
      97             : /************************************************************************/
      98             : 
      99          54 : ZarrV3CodecGZip::ZarrV3CodecGZip() : ZarrV3CodecAbstractCompressor(NAME)
     100             : {
     101          54 : }
     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          54 : bool ZarrV3CodecGZip::InitFromConfiguration(
     119             :     const CPLJSONObject &configuration,
     120             :     const ZarrArrayMetadata &oInputArrayMetadata,
     121             :     ZarrArrayMetadata &oOutputArrayMetadata)
     122             : {
     123          54 :     m_pCompressor = CPLGetCompressor("gzip");
     124          54 :     m_pDecompressor = CPLGetDecompressor("gzip");
     125          54 :     if (!m_pCompressor || !m_pDecompressor)
     126             :     {
     127           0 :         CPLError(CE_Failure, CPLE_AppDefined, "gzip compressor not available");
     128           0 :         return false;
     129             :     }
     130             : 
     131          54 :     m_oConfiguration = configuration.Clone();
     132          54 :     m_oInputArrayMetadata = oInputArrayMetadata;
     133             :     // byte->byte codec
     134          54 :     oOutputArrayMetadata = oInputArrayMetadata;
     135             : 
     136          54 :     int nLevel = 6;
     137             : 
     138          54 :     if (configuration.IsValid())
     139             :     {
     140          54 :         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         108 :         for (const auto &oChild : configuration.GetChildren())
     148             :         {
     149          54 :             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         108 :         const auto oLevel = configuration.GetObj("level");
     160          54 :         if (oLevel.IsValid())
     161             :         {
     162          54 :             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          54 :             nLevel = oLevel.ToInteger();
     169          54 :             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          54 :     m_aosCompressorOptions.SetNameValue("LEVEL", CPLSPrintf("%d", nLevel));
     179             : 
     180          54 :     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("CKECKSUM", "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         250 : ZarrV3CodecBytes::ZarrV3CodecBytes() : ZarrV3Codec(NAME)
     482             : {
     483         250 : }
     484             : 
     485             : /************************************************************************/
     486             : /*                           GetConfiguration()                         */
     487             : /************************************************************************/
     488             : 
     489         122 : /* static */ CPLJSONObject ZarrV3CodecBytes::GetConfiguration(bool bLittle)
     490             : {
     491         122 :     CPLJSONObject oConfig;
     492         122 :     oConfig.Add("endian", bLittle ? "little" : "big");
     493         122 :     return oConfig;
     494             : }
     495             : 
     496             : /************************************************************************/
     497             : /*                 ZarrV3CodecBytes::InitFromConfiguration()            */
     498             : /************************************************************************/
     499             : 
     500         250 : bool ZarrV3CodecBytes::InitFromConfiguration(
     501             :     const CPLJSONObject &configuration,
     502             :     const ZarrArrayMetadata &oInputArrayMetadata,
     503             :     ZarrArrayMetadata &oOutputArrayMetadata)
     504             : {
     505         250 :     m_oConfiguration = configuration.Clone();
     506         250 :     m_bLittle = true;
     507         250 :     m_oInputArrayMetadata = oInputArrayMetadata;
     508         250 :     oOutputArrayMetadata = oInputArrayMetadata;
     509             : 
     510         250 :     if (configuration.IsValid())
     511             :     {
     512         243 :         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         486 :         for (const auto &oChild : configuration.GetChildren())
     520             :         {
     521         243 :             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         486 :         const auto oEndian = configuration.GetObj("endian");
     532         243 :         if (oEndian.IsValid())
     533             :         {
     534         243 :             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         243 :             if (oEndian.ToString() == "little")
     541         183 :                 m_bLittle = true;
     542          60 :             else if (oEndian.ToString() == "big")
     543          60 :                 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         250 :     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             :                  "ZarrV3CodecTranspose::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          80 : ZarrV3CodecTranspose::ZarrV3CodecTranspose() : ZarrV3Codec(NAME)
     647             : {
     648          80 : }
     649             : 
     650             : /************************************************************************/
     651             : /*                             IsNoOp()                                 */
     652             : /************************************************************************/
     653             : 
     654         120 : bool ZarrV3CodecTranspose::IsNoOp() const
     655             : {
     656         180 :     for (int i = 0; i < static_cast<int>(m_anOrder.size()); ++i)
     657             :     {
     658         120 :         if (m_anOrder[i] != i)
     659          60 :             return false;
     660             :     }
     661          60 :     return true;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                           GetConfiguration()                         */
     666             : /************************************************************************/
     667             : 
     668             : /* static */ CPLJSONObject
     669           0 : ZarrV3CodecTranspose::GetConfiguration(const std::vector<int> &anOrder)
     670             : {
     671           0 :     CPLJSONObject oConfig;
     672           0 :     CPLJSONArray oOrder;
     673           0 :     for (const auto nVal : anOrder)
     674           0 :         oOrder.Add(nVal);
     675           0 :     oConfig.Add("order", oOrder);
     676           0 :     return oConfig;
     677             : }
     678             : 
     679             : /************************************************************************/
     680             : /*                           GetConfiguration()                         */
     681             : /************************************************************************/
     682             : 
     683             : /* static */ CPLJSONObject
     684          40 : ZarrV3CodecTranspose::GetConfiguration(const std::string &osOrder)
     685             : {
     686          40 :     CPLJSONObject oConfig;
     687          80 :     CPLJSONArray oOrder;
     688          40 :     oConfig.Add("order", osOrder);
     689          80 :     return oConfig;
     690             : }
     691             : 
     692             : /************************************************************************/
     693             : /*                ZarrV3CodecTranspose::InitFromConfiguration()         */
     694             : /************************************************************************/
     695             : 
     696          80 : bool ZarrV3CodecTranspose::InitFromConfiguration(
     697             :     const CPLJSONObject &configuration,
     698             :     const ZarrArrayMetadata &oInputArrayMetadata,
     699             :     ZarrArrayMetadata &oOutputArrayMetadata)
     700             : {
     701          80 :     m_oConfiguration = configuration.Clone();
     702          80 :     m_oInputArrayMetadata = oInputArrayMetadata;
     703          80 :     oOutputArrayMetadata = oInputArrayMetadata;
     704             : 
     705          80 :     if (!configuration.IsValid() &&
     706           0 :         configuration.GetType() != CPLJSONObject::Type::Object)
     707             :     {
     708           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     709             :                  "Codec transpose: configuration missing or not an object");
     710           0 :         return false;
     711             :     }
     712             : 
     713         160 :     for (const auto &oChild : configuration.GetChildren())
     714             :     {
     715          80 :         if (oChild.GetName() != "order")
     716             :         {
     717           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     718             :                      "Codec transpose: configuration contains a unhandled "
     719             :                      "member: %s",
     720           0 :                      oChild.GetName().c_str());
     721           0 :             return false;
     722             :         }
     723             :     }
     724             : 
     725         240 :     const auto oOrder = configuration.GetObj("order");
     726          80 :     const int nDims = static_cast<int>(oInputArrayMetadata.anBlockSizes.size());
     727          80 :     if (oOrder.GetType() == CPLJSONObject::Type::String)
     728             :     {
     729         160 :         const auto osOrder = oOrder.ToString();
     730          80 :         if (osOrder == "C")
     731             :         {
     732           0 :             for (int i = 0; i < nDims; ++i)
     733             :             {
     734           0 :                 m_anOrder.push_back(i);
     735             :             }
     736             :         }
     737          80 :         else if (osOrder == "F")
     738             :         {
     739         200 :             for (int i = 0; i < nDims; ++i)
     740             :             {
     741         120 :                 m_anOrder.push_back(nDims - 1 - i);
     742         120 :                 oOutputArrayMetadata.anBlockSizes[i] =
     743         120 :                     oInputArrayMetadata.anBlockSizes[nDims - 1 - i];
     744             :             }
     745             :         }
     746             :         else
     747             :         {
     748           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     749             :                      "Codec transpose: invalid value for order");
     750           0 :             return false;
     751             :         }
     752             :     }
     753           0 :     else if (oOrder.GetType() == CPLJSONObject::Type::Array)
     754             :     {
     755           0 :         const auto oOrderArray = oOrder.ToArray();
     756           0 :         if (oOrderArray.Size() != nDims)
     757             :         {
     758           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     759             :                      "Codec transpose: order[] does not have the expected "
     760             :                      "number of elements");
     761           0 :             return false;
     762             :         }
     763           0 :         std::vector<int> oSet(nDims);
     764           0 :         oOutputArrayMetadata.anBlockSizes.clear();
     765           0 :         for (const auto &oVal : oOrderArray)
     766             :         {
     767           0 :             const int nVal = oVal.ToInteger();
     768           0 :             if (nVal < 0 || nVal >= nDims || oSet[nVal])
     769             :             {
     770           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     771             :                          "Codec transpose: order[] does not define a valid "
     772             :                          "transposition");
     773           0 :                 return false;
     774             :             }
     775           0 :             oSet[nVal] = true;
     776           0 :             m_anOrder.push_back(nVal);
     777           0 :             oOutputArrayMetadata.anBlockSizes.push_back(
     778           0 :                 oInputArrayMetadata.anBlockSizes[nVal]);
     779             :         }
     780             :     }
     781             :     else
     782             :     {
     783           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     784             :                  "Codec transpose: invalid value for order");
     785           0 :         return false;
     786             :     }
     787             : 
     788          80 :     int i = 0;
     789          80 :     m_anReverseOrder.resize(m_anOrder.size());
     790         200 :     for (const auto nVal : m_anOrder)
     791             :     {
     792         120 :         m_anReverseOrder[nVal] = i;
     793         120 :         ++i;
     794             :     }
     795             : 
     796          80 :     return true;
     797             : }
     798             : 
     799             : /************************************************************************/
     800             : /*                   ZarrV3CodecTranspose::Clone()                      */
     801             : /************************************************************************/
     802             : 
     803           0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecTranspose::Clone() const
     804             : {
     805           0 :     auto psClone = std::make_unique<ZarrV3CodecTranspose>();
     806           0 :     ZarrArrayMetadata oOutputArrayMetadata;
     807           0 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     808             :                                    oOutputArrayMetadata);
     809           0 :     return psClone;
     810             : }
     811             : 
     812             : /************************************************************************/
     813             : /*                  ZarrV3CodecTranspose::Transpose()                   */
     814             : /************************************************************************/
     815             : 
     816          40 : bool ZarrV3CodecTranspose::Transpose(const ZarrByteVectorQuickResize &abySrc,
     817             :                                      ZarrByteVectorQuickResize &abyDst,
     818             :                                      bool bEncodeDirection) const
     819             : {
     820          40 :     CPLAssert(m_anOrder.size() == m_oInputArrayMetadata.anBlockSizes.size());
     821          40 :     CPLAssert(m_anReverseOrder.size() ==
     822             :               m_oInputArrayMetadata.anBlockSizes.size());
     823          40 :     const size_t nDims = m_anOrder.size();
     824          40 :     const size_t nSourceSize = m_oInputArrayMetadata.oElt.nativeSize;
     825          40 :     const auto &anBlockSizes = m_oInputArrayMetadata.anBlockSizes;
     826          40 :     CPLAssert(nDims > 0);
     827          40 :     if (abySrc.size() < m_oInputArrayMetadata.GetEltCount() * nSourceSize)
     828             :     {
     829           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     830             :                  "ZarrV3CodecTranspose::Transpose(): input buffer too small");
     831           0 :         return false;
     832             :     }
     833          40 :     abyDst.resize(m_oInputArrayMetadata.GetEltCount() * nSourceSize);
     834             : 
     835             :     struct Stack
     836             :     {
     837             :         size_t nIters = 0;
     838             :         const GByte *src_ptr = nullptr;
     839             :         GByte *dst_ptr = nullptr;
     840             :         size_t src_inc_offset = 0;
     841             :         size_t dst_inc_offset = 0;
     842             :     };
     843             : 
     844          40 :     std::vector<Stack> stack(nDims);
     845             :     stack.emplace_back(
     846          40 :         Stack());  // to make gcc 9.3 -O2 -Wnull-dereference happy
     847             : 
     848          40 :     if (!bEncodeDirection)
     849             :     {
     850          20 :         stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
     851          20 :         size_t nStride = nSourceSize;
     852          60 :         for (size_t i = nDims - 1; i > 0;)
     853             :         {
     854          40 :             --i;
     855          40 :             nStride *=
     856          40 :                 static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
     857          40 :             stack[m_anReverseOrder[i]].src_inc_offset = nStride;
     858             :         }
     859             : 
     860          20 :         stack[nDims - 1].dst_inc_offset = nSourceSize;
     861          20 :         nStride = nSourceSize;
     862          60 :         for (size_t i = nDims - 1; i > 0;)
     863             :         {
     864          40 :             --i;
     865          40 :             nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
     866          40 :             stack[i].dst_inc_offset = nStride;
     867             :         }
     868             :     }
     869             :     else
     870             :     {
     871          20 :         stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
     872          20 :         size_t nStride = nSourceSize;
     873          60 :         for (size_t i = nDims - 1; i > 0;)
     874             :         {
     875          40 :             --i;
     876          40 :             nStride *=
     877          40 :                 static_cast<size_t>(anBlockSizes[m_anReverseOrder[i + 1]]);
     878          40 :             stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
     879             :         }
     880             : 
     881          20 :         stack[nDims - 1].src_inc_offset = nSourceSize;
     882          20 :         nStride = nSourceSize;
     883          60 :         for (size_t i = nDims - 1; i > 0;)
     884             :         {
     885          40 :             --i;
     886          40 :             nStride *= static_cast<size_t>(anBlockSizes[i + 1]);
     887          40 :             stack[i].src_inc_offset = nStride;
     888             :         }
     889             :     }
     890             : 
     891          40 :     stack[0].src_ptr = abySrc.data();
     892          40 :     stack[0].dst_ptr = &abyDst[0];
     893             : 
     894          40 :     size_t dimIdx = 0;
     895         680 : lbl_next_depth:
     896         680 :     if (dimIdx == nDims)
     897             :     {
     898         480 :         void *dst_ptr = stack[nDims].dst_ptr;
     899         480 :         const void *src_ptr = stack[nDims].src_ptr;
     900         480 :         if (nSourceSize == 1)
     901          96 :             *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
     902         384 :         else if (nSourceSize == 2)
     903          96 :             *static_cast<uint16_t *>(dst_ptr) =
     904          96 :                 *static_cast<const uint16_t *>(src_ptr);
     905         288 :         else if (nSourceSize == 4)
     906         144 :             *static_cast<uint32_t *>(dst_ptr) =
     907         144 :                 *static_cast<const uint32_t *>(src_ptr);
     908         144 :         else if (nSourceSize == 8)
     909         144 :             *static_cast<uint64_t *>(dst_ptr) =
     910         144 :                 *static_cast<const uint64_t *>(src_ptr);
     911             :         else
     912           0 :             memcpy(dst_ptr, src_ptr, nSourceSize);
     913             :     }
     914             :     else
     915             :     {
     916         200 :         stack[dimIdx].nIters = static_cast<size_t>(anBlockSizes[dimIdx]);
     917             :         while (true)
     918             :         {
     919         640 :             dimIdx++;
     920         640 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
     921         640 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     922         640 :             goto lbl_next_depth;
     923         640 :         lbl_return_to_caller:
     924         640 :             dimIdx--;
     925         640 :             if ((--stack[dimIdx].nIters) == 0)
     926         200 :                 break;
     927         440 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
     928         440 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     929             :         }
     930             :     }
     931         680 :     if (dimIdx > 0)
     932         640 :         goto lbl_return_to_caller;
     933             : 
     934          40 :     return true;
     935             : }
     936             : 
     937             : /************************************************************************/
     938             : /*                    ZarrV3CodecTranspose::Encode()                    */
     939             : /************************************************************************/
     940             : 
     941          20 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
     942             :                                   ZarrByteVectorQuickResize &abyDst) const
     943             : {
     944          20 :     CPLAssert(!IsNoOp());
     945             : 
     946          20 :     return Transpose(abySrc, abyDst, true);
     947             : }
     948             : 
     949             : /************************************************************************/
     950             : /*                    ZarrV3CodecTranspose::Decode()                    */
     951             : /************************************************************************/
     952             : 
     953          20 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
     954             :                                   ZarrByteVectorQuickResize &abyDst) const
     955             : {
     956          20 :     CPLAssert(!IsNoOp());
     957             : 
     958          20 :     return Transpose(abySrc, abyDst, false);
     959             : }
     960             : 
     961             : /************************************************************************/
     962             : /*                    ZarrV3CodecSequence::Clone()                      */
     963             : /************************************************************************/
     964             : 
     965          16 : std::unique_ptr<ZarrV3CodecSequence> ZarrV3CodecSequence::Clone() const
     966             : {
     967          16 :     auto poClone = std::make_unique<ZarrV3CodecSequence>(m_oInputArrayMetadata);
     968          24 :     for (const auto &poCodec : m_apoCodecs)
     969           8 :         poClone->m_apoCodecs.emplace_back(poCodec->Clone());
     970          16 :     poClone->m_oCodecArray = m_oCodecArray.Clone();
     971          16 :     return poClone;
     972             : }
     973             : 
     974             : /************************************************************************/
     975             : /*                    ZarrV3CodecSequence::InitFromJson()               */
     976             : /************************************************************************/
     977             : 
     978         250 : bool ZarrV3CodecSequence::InitFromJson(const CPLJSONObject &oCodecs)
     979             : {
     980         250 :     if (oCodecs.GetType() != CPLJSONObject::Type::Array)
     981             :     {
     982           0 :         CPLError(CE_Failure, CPLE_AppDefined, "codecs is not an array");
     983           0 :         return false;
     984             :     }
     985         500 :     auto oCodecsArray = oCodecs.ToArray();
     986             : 
     987         500 :     ZarrArrayMetadata oInputArrayMetadata = m_oInputArrayMetadata;
     988         250 :     ZarrV3Codec::IOType eLastType = ZarrV3Codec::IOType::ARRAY;
     989         500 :     std::string osLastCodec;
     990             : 
     991             :     const auto InsertImplicitEndianCodecIfNeeded =
     992         304 :         [
     993             : #if !CPL_IS_LSB
     994             :             this,
     995             : #endif
     996         304 :             &oInputArrayMetadata, &eLastType, &osLastCodec]()
     997             :     {
     998         304 :         if (eLastType == ZarrV3Codec::IOType::ARRAY &&
     999           0 :             oInputArrayMetadata.oElt.nativeSize > 1)
    1000             :         {
    1001           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1002             :                      "'bytes' codec missing. Assuming little-endian storage, "
    1003             :                      "but such tolerance may be removed in future versions");
    1004           0 :             auto poEndianCodec = std::make_unique<ZarrV3CodecBytes>();
    1005           0 :             ZarrArrayMetadata oOutputArrayMetadata;
    1006           0 :             poEndianCodec->InitFromConfiguration(
    1007           0 :                 ZarrV3CodecBytes::GetConfiguration(true), oInputArrayMetadata,
    1008             :                 oOutputArrayMetadata);
    1009           0 :             oInputArrayMetadata = std::move(oOutputArrayMetadata);
    1010           0 :             eLastType = poEndianCodec->GetOutputType();
    1011           0 :             osLastCodec = poEndianCodec->GetName();
    1012             : #if !CPL_IS_LSB
    1013             :             // Insert a little endian codec if we are on a big endian target
    1014             :             m_apoCodecs.emplace_back(std::move(poEndianCodec));
    1015             : #endif
    1016             :         }
    1017         304 :     };
    1018             : 
    1019         634 :     for (const auto &oCodec : oCodecsArray)
    1020             :     {
    1021         384 :         if (oCodec.GetType() != CPLJSONObject::Type::Object)
    1022             :         {
    1023           0 :             CPLError(CE_Failure, CPLE_AppDefined, "codecs[] is not an array");
    1024           0 :             return false;
    1025             :         }
    1026         768 :         const auto osName = oCodec["name"].ToString();
    1027           0 :         std::unique_ptr<ZarrV3Codec> poCodec;
    1028         384 :         if (osName == "gzip")
    1029          46 :             poCodec = std::make_unique<ZarrV3CodecGZip>();
    1030         338 :         else if (osName == "blosc")
    1031           4 :             poCodec = std::make_unique<ZarrV3CodecBlosc>();
    1032         334 :         else if (osName == "zstd")
    1033           4 :             poCodec = std::make_unique<ZarrV3CodecZstd>();
    1034         410 :         else if (osName == "bytes" ||
    1035          80 :                  osName == "endian" /* endian is the old name */)
    1036         250 :             poCodec = std::make_unique<ZarrV3CodecBytes>();
    1037          80 :         else if (osName == "transpose")
    1038          80 :             poCodec = std::make_unique<ZarrV3CodecTranspose>();
    1039             :         else
    1040             :         {
    1041           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Unsupported codec: %s",
    1042             :                      osName.c_str());
    1043           0 :             return false;
    1044             :         }
    1045             : 
    1046         384 :         if (poCodec->GetInputType() == ZarrV3Codec::IOType::ARRAY)
    1047             :         {
    1048         330 :             if (eLastType == ZarrV3Codec::IOType::BYTES)
    1049             :             {
    1050           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1051             :                          "Cannot chain codec %s with %s",
    1052           0 :                          poCodec->GetName().c_str(), osLastCodec.c_str());
    1053           0 :                 return false;
    1054             :             }
    1055             :         }
    1056             :         else
    1057             :         {
    1058          54 :             InsertImplicitEndianCodecIfNeeded();
    1059             :         }
    1060             : 
    1061         384 :         ZarrArrayMetadata oOutputArrayMetadata;
    1062         768 :         if (!poCodec->InitFromConfiguration(oCodec["configuration"],
    1063             :                                             oInputArrayMetadata,
    1064         384 :                                             oOutputArrayMetadata))
    1065             :         {
    1066           0 :             return false;
    1067             :         }
    1068         384 :         oInputArrayMetadata = std::move(oOutputArrayMetadata);
    1069         384 :         eLastType = poCodec->GetOutputType();
    1070         384 :         osLastCodec = poCodec->GetName();
    1071             : 
    1072         384 :         if (!poCodec->IsNoOp())
    1073         122 :             m_apoCodecs.emplace_back(std::move(poCodec));
    1074             :     }
    1075             : 
    1076         250 :     InsertImplicitEndianCodecIfNeeded();
    1077             : 
    1078         250 :     m_oCodecArray = oCodecs.Clone();
    1079         250 :     return true;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                  ZarrV3CodecBytes::AllocateBuffer()                 */
    1084             : /************************************************************************/
    1085             : 
    1086       21416 : bool ZarrV3CodecSequence::AllocateBuffer(ZarrByteVectorQuickResize &abyBuffer)
    1087             : {
    1088       21416 :     if (!m_apoCodecs.empty())
    1089             :     {
    1090       10595 :         const size_t nRawSize = m_oInputArrayMetadata.GetEltCount() *
    1091       10668 :                                 m_oInputArrayMetadata.oElt.nativeSize;
    1092             :         // Grow the temporary buffer a bit beyond the uncompressed size
    1093       10668 :         const size_t nMaxSize = nRawSize + nRawSize / 3 + 64;
    1094             :         try
    1095             :         {
    1096       10668 :             m_abyTmp.resize(nMaxSize);
    1097             :         }
    1098           0 :         catch (const std::exception &e)
    1099             :         {
    1100           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1101           0 :             return false;
    1102             :         }
    1103       10581 :         m_abyTmp.resize(nRawSize);
    1104             : 
    1105             :         // Grow the input/output buffer too if we have several steps
    1106       10580 :         if (m_apoCodecs.size() >= 2 && abyBuffer.capacity() < nMaxSize)
    1107             :         {
    1108          36 :             const size_t nSize = abyBuffer.size();
    1109             :             try
    1110             :             {
    1111          36 :                 abyBuffer.resize(nMaxSize);
    1112             :             }
    1113           0 :             catch (const std::exception &e)
    1114             :             {
    1115           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1116           0 :                 return false;
    1117             :             }
    1118          36 :             abyBuffer.resize(nSize);
    1119             :         }
    1120             :     }
    1121       21161 :     return true;
    1122             : }
    1123             : 
    1124             : /************************************************************************/
    1125             : /*                    ZarrV3CodecSequence::Encode()                     */
    1126             : /************************************************************************/
    1127             : 
    1128       10744 : bool ZarrV3CodecSequence::Encode(ZarrByteVectorQuickResize &abyBuffer)
    1129             : {
    1130       10744 :     if (!AllocateBuffer(abyBuffer))
    1131           0 :         return false;
    1132       16155 :     for (const auto &poCodec : m_apoCodecs)
    1133             :     {
    1134        5411 :         if (!poCodec->Encode(abyBuffer, m_abyTmp))
    1135           0 :             return false;
    1136        5411 :         std::swap(abyBuffer, m_abyTmp);
    1137             :     }
    1138       10744 :     return true;
    1139             : }
    1140             : 
    1141             : /************************************************************************/
    1142             : /*                    ZarrV3CodecSequence::Decode()                     */
    1143             : /************************************************************************/
    1144             : 
    1145       10689 : bool ZarrV3CodecSequence::Decode(ZarrByteVectorQuickResize &abyBuffer)
    1146             : {
    1147       10689 :     if (!AllocateBuffer(abyBuffer))
    1148           0 :         return false;
    1149       15812 :     for (auto iter = m_apoCodecs.rbegin(); iter != m_apoCodecs.rend(); ++iter)
    1150             :     {
    1151        5148 :         const auto &poCodec = *iter;
    1152        5141 :         if (!poCodec->Decode(abyBuffer, m_abyTmp))
    1153           0 :             return false;
    1154        5335 :         std::swap(abyBuffer, m_abyTmp);
    1155             :     }
    1156       10417 :     return true;
    1157             : }

Generated by: LCOV version 1.14