LCOV - code coverage report
Current view: top level - frmts/zarr - zarr_v3_codec_transpose.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 125 157 79.6 %
Date: 2026-02-11 08:43:47 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Zarr driver, "transpose" codec
       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_v3_codec.h"
      14             : 
      15             : // Implements https://zarr-specs.readthedocs.io/en/latest/v3/codecs/transpose/index.html
      16             : 
      17             : /************************************************************************/
      18             : /*                        ZarrV3CodecTranspose()                        */
      19             : /************************************************************************/
      20             : 
      21          83 : ZarrV3CodecTranspose::ZarrV3CodecTranspose() : ZarrV3Codec(NAME)
      22             : {
      23          83 : }
      24             : 
      25             : /************************************************************************/
      26             : /*                               IsNoOp()                               */
      27             : /************************************************************************/
      28             : 
      29         184 : bool ZarrV3CodecTranspose::IsNoOp() const
      30             : {
      31         184 :     for (int i = 0; i < static_cast<int>(m_anOrder.size()); ++i)
      32             :     {
      33         184 :         if (m_anOrder[i] != i)
      34         184 :             return false;
      35             :     }
      36           0 :     return true;
      37             : }
      38             : 
      39             : /************************************************************************/
      40             : /*                          GetConfiguration()                          */
      41             : /************************************************************************/
      42             : 
      43             : /* static */ CPLJSONObject
      44          40 : ZarrV3CodecTranspose::GetConfiguration(const std::vector<int> &anOrder)
      45             : {
      46          40 :     CPLJSONObject oConfig;
      47          80 :     CPLJSONArray oOrder;
      48         130 :     for (const auto nVal : anOrder)
      49          90 :         oOrder.Add(nVal);
      50          40 :     oConfig.Add("order", oOrder);
      51          80 :     return oConfig;
      52             : }
      53             : 
      54             : /************************************************************************/
      55             : /*            ZarrV3CodecTranspose::InitFromConfiguration()             */
      56             : /************************************************************************/
      57             : 
      58          83 : bool ZarrV3CodecTranspose::InitFromConfiguration(
      59             :     const CPLJSONObject &configuration,
      60             :     const ZarrArrayMetadata &oInputArrayMetadata,
      61             :     ZarrArrayMetadata &oOutputArrayMetadata, bool /* bEmitWarnings */)
      62             : {
      63          83 :     m_oConfiguration = configuration.Clone();
      64          83 :     m_oInputArrayMetadata = oInputArrayMetadata;
      65          83 :     oOutputArrayMetadata = oInputArrayMetadata;
      66             : 
      67          83 :     if (!configuration.IsValid() &&
      68           0 :         configuration.GetType() != CPLJSONObject::Type::Object)
      69             :     {
      70           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      71             :                  "Codec transpose: configuration missing or not an object");
      72           0 :         return false;
      73             :     }
      74             : 
      75         166 :     for (const auto &oChild : configuration.GetChildren())
      76             :     {
      77          83 :         if (oChild.GetName() != "order")
      78             :         {
      79           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      80             :                      "Codec transpose: configuration contains a unhandled "
      81             :                      "member: %s",
      82           0 :                      oChild.GetName().c_str());
      83           0 :             return false;
      84             :         }
      85             :     }
      86             : 
      87         249 :     const auto oOrder = configuration.GetObj("order");
      88          83 :     const int nDims = static_cast<int>(oInputArrayMetadata.anBlockSizes.size());
      89          83 :     if (oOrder.GetType() == CPLJSONObject::Type::String)
      90             :     {
      91             :         // Deprecated
      92           0 :         const auto osOrder = oOrder.ToString();
      93           0 :         if (osOrder == "C")
      94             :         {
      95           0 :             for (int i = 0; i < nDims; ++i)
      96             :             {
      97           0 :                 m_anOrder.push_back(i);
      98             :             }
      99             :         }
     100           0 :         else if (osOrder == "F")
     101             :         {
     102           0 :             for (int i = 0; i < nDims; ++i)
     103             :             {
     104           0 :                 m_anOrder.push_back(nDims - 1 - i);
     105           0 :                 oOutputArrayMetadata.anBlockSizes[i] =
     106           0 :                     oInputArrayMetadata.anBlockSizes[nDims - 1 - i];
     107             :             }
     108             :         }
     109             :         else
     110             :         {
     111           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     112             :                      "Codec transpose: invalid value for order");
     113           0 :             return false;
     114             :         }
     115             :     }
     116          83 :     else if (oOrder.GetType() == CPLJSONObject::Type::Array)
     117             :     {
     118          83 :         const auto oOrderArray = oOrder.ToArray();
     119          83 :         if (oOrderArray.Size() != nDims)
     120             :         {
     121           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     122             :                      "Codec transpose: order[] does not have the expected "
     123             :                      "number of elements");
     124           0 :             return false;
     125             :         }
     126          83 :         std::vector<int> oSet(nDims);
     127          83 :         oOutputArrayMetadata.anBlockSizes.clear();
     128         270 :         for (const auto &oVal : oOrderArray)
     129             :         {
     130         187 :             const int nVal = oVal.ToInteger();
     131         187 :             if (nVal < 0 || nVal >= nDims || oSet[nVal])
     132             :             {
     133           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     134             :                          "Codec transpose: order[] does not define a valid "
     135             :                          "transposition");
     136           0 :                 return false;
     137             :             }
     138         187 :             oSet[nVal] = true;
     139         187 :             m_anOrder.push_back(nVal);
     140         187 :             oOutputArrayMetadata.anBlockSizes.push_back(
     141         187 :                 oInputArrayMetadata.anBlockSizes[nVal]);
     142             :         }
     143             :     }
     144             :     else
     145             :     {
     146           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     147             :                  "Codec transpose: invalid value for order");
     148           0 :         return false;
     149             :     }
     150             : 
     151          83 :     int i = 0;
     152          83 :     m_anReverseOrder.resize(m_anOrder.size());
     153         270 :     for (const auto nVal : m_anOrder)
     154             :     {
     155         187 :         m_anReverseOrder[nVal] = i;
     156         187 :         ++i;
     157             :     }
     158             : 
     159          83 :     return true;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*            ZarrV3CodecTranspose::GetInnerMostBlockSize()             */
     164             : /************************************************************************/
     165             : 
     166          82 : std::vector<size_t> ZarrV3CodecTranspose::GetInnerMostBlockSize(
     167             :     const std::vector<size_t> &anInnerBlockSize) const
     168             : {
     169          82 :     std::vector<size_t> ret;
     170         266 :     for (int idx : m_anReverseOrder)
     171         184 :         ret.push_back(anInnerBlockSize[idx]);
     172          82 :     return ret;
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                    ZarrV3CodecTranspose::Clone()                     */
     177             : /************************************************************************/
     178             : 
     179           0 : std::unique_ptr<ZarrV3Codec> ZarrV3CodecTranspose::Clone() const
     180             : {
     181           0 :     auto psClone = std::make_unique<ZarrV3CodecTranspose>();
     182           0 :     ZarrArrayMetadata oOutputArrayMetadata;
     183           0 :     psClone->InitFromConfiguration(m_oConfiguration, m_oInputArrayMetadata,
     184             :                                    oOutputArrayMetadata,
     185             :                                    /* bEmitWarnings = */ false);
     186           0 :     return psClone;
     187             : }
     188             : 
     189             : /************************************************************************/
     190             : /*                  ZarrV3CodecTranspose::Transpose()                   */
     191             : /************************************************************************/
     192             : 
     193         125 : bool ZarrV3CodecTranspose::Transpose(
     194             :     const ZarrByteVectorQuickResize &abySrc, ZarrByteVectorQuickResize &abyDst,
     195             :     bool bEncodeDirection, const std::vector<size_t> &anForwardBlockSizes) const
     196             : {
     197         125 :     CPLAssert(m_anOrder.size() == anForwardBlockSizes.size());
     198         125 :     CPLAssert(m_anReverseOrder.size() == anForwardBlockSizes.size());
     199         125 :     const size_t nDims = m_anOrder.size();
     200         125 :     const size_t nSourceSize = m_oInputArrayMetadata.oElt.nativeSize;
     201         125 :     CPLAssert(nDims > 0);
     202         125 :     if (abySrc.size() < MultiplyElements(anForwardBlockSizes) * nSourceSize)
     203             :     {
     204           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     205             :                  "ZarrV3CodecTranspose::Transpose(): input buffer too small");
     206           0 :         return false;
     207             :     }
     208         125 :     abyDst.resize(MultiplyElements(anForwardBlockSizes) * nSourceSize);
     209             : 
     210             :     struct Stack
     211             :     {
     212             :         size_t nIters = 0;
     213             :         const GByte *src_ptr = nullptr;
     214             :         GByte *dst_ptr = nullptr;
     215             :         size_t src_inc_offset = 0;
     216             :         size_t dst_inc_offset = 0;
     217             :     };
     218             : 
     219             : #if defined(__GNUC__)
     220             : #pragma GCC diagnostic push
     221             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     222             : #endif
     223         125 :     std::vector<Stack> stack(nDims);
     224             :     stack.emplace_back(
     225         125 :         Stack());  // to make gcc 9.3 -O2 -Wnull-dereference happy
     226             : #if defined(__GNUC__)
     227             : #pragma GCC diagnostic pop
     228             : #endif
     229             : 
     230         125 :     if (!bEncodeDirection)
     231             :     {
     232          75 :         stack[m_anReverseOrder[nDims - 1]].src_inc_offset = nSourceSize;
     233          75 :         size_t nStride = nSourceSize;
     234         170 :         for (size_t i = nDims - 1; i > 0;)
     235             :         {
     236          95 :             --i;
     237          95 :             nStride *= anForwardBlockSizes[m_anReverseOrder[i + 1]];
     238          95 :             stack[m_anReverseOrder[i]].src_inc_offset = nStride;
     239             :         }
     240             : 
     241          75 :         stack[nDims - 1].dst_inc_offset = nSourceSize;
     242          75 :         nStride = nSourceSize;
     243         170 :         for (size_t i = nDims - 1; i > 0;)
     244             :         {
     245          95 :             --i;
     246          95 :             nStride *= anForwardBlockSizes[i + 1];
     247          95 :             stack[i].dst_inc_offset = nStride;
     248             :         }
     249             :     }
     250             :     else
     251             :     {
     252          50 :         stack[m_anReverseOrder[nDims - 1]].dst_inc_offset = nSourceSize;
     253          50 :         size_t nStride = nSourceSize;
     254         120 :         for (size_t i = nDims - 1; i > 0;)
     255             :         {
     256          70 :             --i;
     257          70 :             nStride *= anForwardBlockSizes[m_anReverseOrder[i + 1]];
     258          70 :             stack[m_anReverseOrder[i]].dst_inc_offset = nStride;
     259             :         }
     260             : 
     261          50 :         stack[nDims - 1].src_inc_offset = nSourceSize;
     262          50 :         nStride = nSourceSize;
     263         120 :         for (size_t i = nDims - 1; i > 0;)
     264             :         {
     265          70 :             --i;
     266          70 :             nStride *= anForwardBlockSizes[i + 1];
     267          70 :             stack[i].src_inc_offset = nStride;
     268             :         }
     269             :     }
     270             : 
     271         125 :     stack[0].src_ptr = abySrc.data();
     272         125 :     stack[0].dst_ptr = &abyDst[0];
     273             : 
     274         125 :     size_t dimIdx = 0;
     275        1880 : lbl_next_depth:
     276        1880 :     if (dimIdx == nDims)
     277             :     {
     278        1350 :         void *dst_ptr = stack[nDims].dst_ptr;
     279        1350 :         const void *src_ptr = stack[nDims].src_ptr;
     280        1350 :         if (nSourceSize == 1)
     281         120 :             *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
     282        1230 :         else if (nSourceSize == 2)
     283         120 :             *static_cast<uint16_t *>(dst_ptr) =
     284         120 :                 *static_cast<const uint16_t *>(src_ptr);
     285        1110 :         else if (nSourceSize == 4)
     286         930 :             *static_cast<uint32_t *>(dst_ptr) =
     287         930 :                 *static_cast<const uint32_t *>(src_ptr);
     288         180 :         else if (nSourceSize == 8)
     289         180 :             *static_cast<uint64_t *>(dst_ptr) =
     290         180 :                 *static_cast<const uint64_t *>(src_ptr);
     291             :         else
     292           0 :             memcpy(dst_ptr, src_ptr, nSourceSize);
     293             :     }
     294             :     else
     295             :     {
     296         530 :         stack[dimIdx].nIters = anForwardBlockSizes[dimIdx];
     297             :         while (true)
     298             :         {
     299        1755 :             dimIdx++;
     300        1755 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
     301        1755 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
     302        1755 :             goto lbl_next_depth;
     303        1755 :         lbl_return_to_caller:
     304        1755 :             dimIdx--;
     305        1755 :             if ((--stack[dimIdx].nIters) == 0)
     306         530 :                 break;
     307        1225 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
     308        1225 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
     309             :         }
     310             :     }
     311        1880 :     if (dimIdx > 0)
     312        1755 :         goto lbl_return_to_caller;
     313             : 
     314         125 :     return true;
     315             : }
     316             : 
     317             : /************************************************************************/
     318             : /*                    ZarrV3CodecTranspose::Encode()                    */
     319             : /************************************************************************/
     320             : 
     321          50 : bool ZarrV3CodecTranspose::Encode(const ZarrByteVectorQuickResize &abySrc,
     322             :                                   ZarrByteVectorQuickResize &abyDst) const
     323             : {
     324          50 :     CPLAssert(!IsNoOp());
     325             : 
     326          50 :     return Transpose(abySrc, abyDst, true, m_oInputArrayMetadata.anBlockSizes);
     327             : }
     328             : 
     329             : /************************************************************************/
     330             : /*                    ZarrV3CodecTranspose::Decode()                    */
     331             : /************************************************************************/
     332             : 
     333          50 : bool ZarrV3CodecTranspose::Decode(const ZarrByteVectorQuickResize &abySrc,
     334             :                                   ZarrByteVectorQuickResize &abyDst) const
     335             : {
     336          50 :     CPLAssert(!IsNoOp());
     337             : 
     338          50 :     return Transpose(abySrc, abyDst, false, m_oInputArrayMetadata.anBlockSizes);
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                ZarrV3CodecTranspose::DecodePartial()                 */
     343             : /************************************************************************/
     344             : 
     345          25 : bool ZarrV3CodecTranspose::DecodePartial(
     346             :     VSIVirtualHandle * /* poFile */, const ZarrByteVectorQuickResize &abySrc,
     347             :     ZarrByteVectorQuickResize &abyDst, std::vector<size_t> &anStartIdx,
     348             :     std::vector<size_t> &anCount)
     349             : {
     350          25 :     CPLAssert(anStartIdx.size() == m_oInputArrayMetadata.anBlockSizes.size());
     351          25 :     CPLAssert(anStartIdx.size() == anCount.size());
     352             : 
     353          25 :     Reorder1DInverse(anStartIdx);
     354          25 :     Reorder1DInverse(anCount);
     355             : 
     356             :     // Note that we don't need to take anStartIdx into account for the
     357             :     // transpose operation, as abySrc corresponds to anStartIdx.
     358          25 :     return Transpose(abySrc, abyDst, false, anCount);
     359             : }

Generated by: LCOV version 1.14