LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_array_transposed.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 268 278 96.4 %
Date: 2026-04-15 22:10:00 Functions: 28 28 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_array_transposed.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDALMDArray::Transpose() implementation
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_multidim.h"
      15             : #include "gdal_pam_multidim.h"
      16             : #include "ogr_spatialref.h"
      17             : 
      18             : //! @cond Doxygen_Suppress
      19             : 
      20             : /************************************************************************/
      21             : /*                   CopyToFinalBufferSameDataType()                    */
      22             : /************************************************************************/
      23             : 
      24             : template <size_t N>
      25         116 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
      26             :                                    size_t nDims, const size_t *count,
      27             :                                    const GPtrDiff_t *bufferStride)
      28             : {
      29         232 :     std::vector<size_t> anStackCount(nDims);
      30         232 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
      31         116 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
      32             : #if defined(__GNUC__)
      33             : #pragma GCC diagnostic push
      34             : #pragma GCC diagnostic ignored "-Wnull-dereference"
      35             : #endif
      36         116 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
      37             : #if defined(__GNUC__)
      38             : #pragma GCC diagnostic pop
      39             : #endif
      40         116 :     size_t iDim = 0;
      41             : 
      42         659 : lbl_next_depth:
      43         659 :     if (iDim == nDims - 1)
      44             :     {
      45         543 :         size_t n = count[iDim];
      46         543 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
      47         543 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
      48       18270 :         while (n > 0)
      49             :         {
      50       17727 :             --n;
      51       17727 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
      52       17727 :             pabyDstBuffer += bufferStrideLastDim;
      53       17727 :             pabySrcBuffer += N;
      54             :         }
      55             :     }
      56             :     else
      57             :     {
      58         116 :         anStackCount[iDim] = count[iDim];
      59             :         while (true)
      60             :         {
      61         543 :             ++iDim;
      62         543 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
      63         543 :             goto lbl_next_depth;
      64         543 :         lbl_return_to_caller_in_loop:
      65         543 :             --iDim;
      66         543 :             --anStackCount[iDim];
      67         543 :             if (anStackCount[iDim] == 0)
      68         116 :                 break;
      69         427 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
      70             :         }
      71             :     }
      72         659 :     if (iDim > 0)
      73         543 :         goto lbl_return_to_caller_in_loop;
      74         116 : }
      75             : 
      76             : /************************************************************************/
      77             : /*                         CopyToFinalBuffer()                          */
      78             : /************************************************************************/
      79             : 
      80         364 : static void CopyToFinalBuffer(const void *pSrcBuffer,
      81             :                               const GDALExtendedDataType &eSrcDataType,
      82             :                               void *pDstBuffer,
      83             :                               const GDALExtendedDataType &eDstDataType,
      84             :                               size_t nDims, const size_t *count,
      85             :                               const GPtrDiff_t *bufferStride)
      86             : {
      87         364 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
      88             :     // Use specialized implementation for well-known data types when no
      89             :     // type conversion is needed
      90         364 :     if (eSrcDataType == eDstDataType)
      91             :     {
      92         169 :         if (nSrcDataTypeSize == 1)
      93             :         {
      94          77 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
      95             :                                              count, bufferStride);
      96         116 :             return;
      97             :         }
      98          92 :         else if (nSrcDataTypeSize == 2)
      99             :         {
     100           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
     101             :                                              count, bufferStride);
     102           1 :             return;
     103             :         }
     104          91 :         else if (nSrcDataTypeSize == 4)
     105             :         {
     106          18 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
     107             :                                              count, bufferStride);
     108          18 :             return;
     109             :         }
     110          73 :         else if (nSrcDataTypeSize == 8)
     111             :         {
     112          20 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
     113             :                                              count, bufferStride);
     114          20 :             return;
     115             :         }
     116             :     }
     117             : 
     118         248 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
     119         496 :     std::vector<size_t> anStackCount(nDims);
     120         496 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
     121         248 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
     122         248 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
     123         248 :     size_t iDim = 0;
     124             : 
     125         517 : lbl_next_depth:
     126         517 :     if (iDim == nDims - 1)
     127             :     {
     128         507 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
     129         507 :                                          pabyDstBufferStack[iDim], eDstDataType,
     130         507 :                                          bufferStride[iDim], count[iDim]);
     131         507 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
     132             :     }
     133             :     else
     134             :     {
     135          10 :         anStackCount[iDim] = count[iDim];
     136             :         while (true)
     137             :         {
     138         269 :             ++iDim;
     139         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
     140         269 :             goto lbl_next_depth;
     141         269 :         lbl_return_to_caller_in_loop:
     142         269 :             --iDim;
     143         269 :             --anStackCount[iDim];
     144         269 :             if (anStackCount[iDim] == 0)
     145          10 :                 break;
     146         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
     147             :         }
     148             :     }
     149         517 :     if (iDim > 0)
     150         269 :         goto lbl_return_to_caller_in_loop;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                        IsTransposedRequest()                         */
     155             : /************************************************************************/
     156             : 
     157        1575 : bool GDALMDArray::IsTransposedRequest(
     158             :     const size_t *count,
     159             :     const GPtrDiff_t *bufferStride) const  // stride in elements
     160             : {
     161             :     /*
     162             :     For example:
     163             :     count = [2,3,4]
     164             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
     165             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
     166             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
     167             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
     168             :     */
     169        1575 :     const size_t nDims(GetDimensionCount());
     170        1575 :     size_t nCurStrideForRowMajorStrides = 1;
     171        1575 :     bool bRowMajorStrides = true;
     172        1575 :     size_t nElts = 1;
     173        1575 :     size_t nLastIdx = 0;
     174        3955 :     for (size_t i = nDims; i > 0;)
     175             :     {
     176        2380 :         --i;
     177        2380 :         if (bufferStride[i] < 0)
     178           0 :             return false;
     179        2380 :         if (static_cast<size_t>(bufferStride[i]) !=
     180             :             nCurStrideForRowMajorStrides)
     181             :         {
     182         496 :             bRowMajorStrides = false;
     183             :         }
     184             :         // Integer overflows have already been checked in CheckReadWriteParams()
     185        2380 :         nCurStrideForRowMajorStrides *= count[i];
     186        2380 :         nElts *= count[i];
     187        2380 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
     188             :     }
     189        1575 :     if (bRowMajorStrides)
     190        1190 :         return false;
     191         385 :     return nLastIdx == nElts - 1;
     192             : }
     193             : 
     194             : /************************************************************************/
     195             : /*                         TransposeLast2Dims()                         */
     196             : /************************************************************************/
     197             : 
     198          20 : static bool TransposeLast2Dims(void *pDstBuffer,
     199             :                                const GDALExtendedDataType &eDT,
     200             :                                const size_t nDims, const size_t *count,
     201             :                                const size_t nEltsNonLast2Dims)
     202             : {
     203          20 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
     204          20 :     const auto nDTSize = eDT.GetSize();
     205             :     void *pTempBufferForLast2DimsTranspose =
     206          20 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
     207          20 :     if (pTempBufferForLast2DimsTranspose == nullptr)
     208           0 :         return false;
     209             : 
     210          20 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
     211          60 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
     212             :     {
     213          40 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
     214             :                         pTempBufferForLast2DimsTranspose,
     215          40 :                         eDT.GetNumericDataType(), count[nDims - 1],
     216          40 :                         count[nDims - 2]);
     217          40 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
     218             :                nDTSize * nEltsLast2Dims);
     219          40 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
     220             :     }
     221             : 
     222          20 :     VSIFree(pTempBufferForLast2DimsTranspose);
     223             : 
     224          20 :     return true;
     225             : }
     226             : 
     227             : /************************************************************************/
     228             : /*                      ReadForTransposedRequest()                      */
     229             : /************************************************************************/
     230             : 
     231             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
     232             : // transposed view yield to extremely poor/unusable performance. This fixes
     233             : // this by using temporary memory to read in a contiguous buffer in a
     234             : // row-major order, and then do the transposition to the final buffer.
     235             : 
     236         384 : bool GDALMDArray::ReadForTransposedRequest(
     237             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
     238             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
     239             :     void *pDstBuffer) const
     240             : {
     241         384 :     const size_t nDims(GetDimensionCount());
     242         384 :     if (nDims == 0)
     243             :     {
     244           0 :         CPLAssert(false);
     245             :         return false;  // shouldn't happen
     246             :     }
     247         384 :     size_t nElts = 1;
     248         919 :     for (size_t i = 0; i < nDims; ++i)
     249         535 :         nElts *= count[i];
     250             : 
     251         768 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
     252         384 :     tmpBufferStrides.back() = 1;
     253         535 :     for (size_t i = nDims - 1; i > 0;)
     254             :     {
     255         151 :         --i;
     256         151 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
     257             :     }
     258             : 
     259         384 :     const auto &eDT = GetDataType();
     260         384 :     const auto nDTSize = eDT.GetSize();
     261         573 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
     262         589 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
     263          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
     264             :     {
     265             :         // Optimization of the optimization if only the last 2 dims are
     266             :         // transposed that saves on temporary buffer allocation
     267          24 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
     268          24 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
     269          24 :         bool bRowMajorStridesForNonLast2Dims = true;
     270          24 :         size_t nEltsNonLast2Dims = 1;
     271          41 :         for (size_t i = nDims - 2; i > 0;)
     272             :         {
     273          17 :             --i;
     274          17 :             if (static_cast<size_t>(bufferStride[i]) !=
     275             :                 nCurStrideForRowMajorStrides)
     276             :             {
     277           4 :                 bRowMajorStridesForNonLast2Dims = false;
     278             :             }
     279             :             // Integer overflows have already been checked in
     280             :             // CheckReadWriteParams()
     281          17 :             nCurStrideForRowMajorStrides *= count[i];
     282          17 :             nEltsNonLast2Dims *= count[i];
     283             :         }
     284          24 :         if (bRowMajorStridesForNonLast2Dims)
     285             :         {
     286             :             // We read in the final buffer!
     287          20 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
     288          20 :                        eDT, pDstBuffer))
     289             :             {
     290           0 :                 return false;
     291             :             }
     292             : 
     293          20 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
     294          20 :                                       nEltsNonLast2Dims);
     295             :         }
     296             :     }
     297             : 
     298         364 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
     299         364 :     if (pTempBuffer == nullptr)
     300           0 :         return false;
     301             : 
     302         364 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
     303         364 :                pTempBuffer))
     304             :     {
     305           0 :         VSIFree(pTempBuffer);
     306           0 :         return false;
     307             :     }
     308         364 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
     309             :                       count, bufferStride);
     310             : 
     311         364 :     if (eDT.NeedsFreeDynamicMemory())
     312             :     {
     313         237 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
     314         474 :         for (size_t i = 0; i < nElts; ++i)
     315             :         {
     316         237 :             eDT.FreeDynamicMemory(pabyPtr);
     317         237 :             pabyPtr += nDTSize;
     318             :         }
     319             :     }
     320             : 
     321         364 :     VSIFree(pTempBuffer);
     322         364 :     return true;
     323             : }
     324             : 
     325             : /************************************************************************/
     326             : /*                        GDALMDArrayTransposed                         */
     327             : /************************************************************************/
     328             : 
     329             : class GDALMDArrayTransposed final : public GDALPamMDArray
     330             : {
     331             :   private:
     332             :     std::shared_ptr<GDALMDArray> m_poParent{};
     333             :     std::vector<int> m_anMapNewAxisToOldAxis{};
     334             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
     335             : 
     336             :     mutable std::vector<GUInt64> m_parentStart;
     337             :     mutable std::vector<size_t> m_parentCount;
     338             :     mutable std::vector<GInt64> m_parentStep;
     339             :     mutable std::vector<GPtrDiff_t> m_parentStride;
     340             : 
     341             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
     342             :                              const GInt64 *arrayStep,
     343             :                              const GPtrDiff_t *bufferStride) const;
     344             : 
     345             :     static std::string
     346          88 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
     347             :     {
     348          88 :         std::string ret;
     349          88 :         ret += '[';
     350         324 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
     351             :         {
     352         236 :             if (i > 0)
     353         148 :                 ret += ',';
     354         236 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
     355             :         }
     356          88 :         ret += ']';
     357          88 :         return ret;
     358             :     }
     359             : 
     360             :   protected:
     361          44 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
     362             :                           const std::vector<int> &anMapNewAxisToOldAxis,
     363             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
     364          88 :         : GDALAbstractMDArray(std::string(),
     365          88 :                               "Transposed view of " + poParent->GetFullName() +
     366          88 :                                   " along " +
     367          44 :                                   MappingToStr(anMapNewAxisToOldAxis)),
     368          88 :           GDALPamMDArray(std::string(),
     369          88 :                          "Transposed view of " + poParent->GetFullName() +
     370         176 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
     371          88 :                          GDALPamMultiDim::GetPAM(poParent),
     372             :                          poParent->GetContext()),
     373          44 :           m_poParent(std::move(poParent)),
     374             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
     375          44 :           m_dims(std::move(dims)),
     376          44 :           m_parentStart(m_poParent->GetDimensionCount()),
     377          44 :           m_parentCount(m_poParent->GetDimensionCount()),
     378          44 :           m_parentStep(m_poParent->GetDimensionCount()),
     379         352 :           m_parentStride(m_poParent->GetDimensionCount())
     380             :     {
     381          44 :     }
     382             : 
     383             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     384             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     385             :                const GDALExtendedDataType &bufferDataType,
     386             :                void *pDstBuffer) const override;
     387             : 
     388             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
     389             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     390             :                 const GDALExtendedDataType &bufferDataType,
     391             :                 const void *pSrcBuffer) override;
     392             : 
     393             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
     394             :                      CSLConstList papszOptions) const override;
     395             : 
     396             :   public:
     397             :     static std::shared_ptr<GDALMDArrayTransposed>
     398          44 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
     399             :            const std::vector<int> &anMapNewAxisToOldAxis)
     400             :     {
     401          44 :         const auto &parentDims(poParent->GetDimensions());
     402          88 :         std::vector<std::shared_ptr<GDALDimension>> dims;
     403         162 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
     404             :         {
     405         118 :             if (iOldAxis < 0)
     406             :             {
     407           1 :                 dims.push_back(std::make_shared<GDALDimension>(
     408           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
     409             :             }
     410             :             else
     411             :             {
     412         117 :                 dims.emplace_back(parentDims[iOldAxis]);
     413             :             }
     414             :         }
     415             : 
     416             :         auto newAr(
     417             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
     418          44 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
     419          44 :         newAr->SetSelf(newAr);
     420          88 :         return newAr;
     421             :     }
     422             : 
     423           3 :     bool IsWritable() const override
     424             :     {
     425           3 :         return m_poParent->IsWritable();
     426             :     }
     427             : 
     428          99 :     const std::string &GetFilename() const override
     429             :     {
     430          99 :         return m_poParent->GetFilename();
     431             :     }
     432             : 
     433             :     const std::vector<std::shared_ptr<GDALDimension>> &
     434         373 :     GetDimensions() const override
     435             :     {
     436         373 :         return m_dims;
     437             :     }
     438             : 
     439         151 :     const GDALExtendedDataType &GetDataType() const override
     440             :     {
     441         151 :         return m_poParent->GetDataType();
     442             :     }
     443             : 
     444           4 :     const std::string &GetUnit() const override
     445             :     {
     446           4 :         return m_poParent->GetUnit();
     447             :     }
     448             : 
     449           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     450             :     {
     451          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
     452           5 :         if (!poSrcSRS)
     453           2 :             return nullptr;
     454           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
     455           6 :         std::vector<int> dstMapping;
     456           9 :         for (int srcAxis : srcMapping)
     457             :         {
     458           6 :             bool bFound = false;
     459          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
     460             :             {
     461          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
     462             :                 {
     463           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
     464           6 :                     bFound = true;
     465           6 :                     break;
     466             :                 }
     467             :             }
     468           6 :             if (!bFound)
     469             :             {
     470           0 :                 dstMapping.push_back(0);
     471             :             }
     472             :         }
     473           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
     474           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
     475           3 :         return poClone;
     476             :     }
     477             : 
     478           7 :     const void *GetRawNoDataValue() const override
     479             :     {
     480           7 :         return m_poParent->GetRawNoDataValue();
     481             :     }
     482             : 
     483             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
     484             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
     485             : 
     486           4 :     double GetOffset(bool *pbHasOffset,
     487             :                      GDALDataType *peStorageType) const override
     488             :     {
     489           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
     490             :     }
     491             : 
     492           4 :     double GetScale(bool *pbHasScale,
     493             :                     GDALDataType *peStorageType) const override
     494             :     {
     495           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
     496             :     }
     497             : 
     498             :     // bool SetOffset(double dfOffset) override { return
     499             :     // m_poParent->SetOffset(dfOffset); }
     500             : 
     501             :     // bool SetScale(double dfScale) override { return
     502             :     // m_poParent->SetScale(dfScale); }
     503             : 
     504           5 :     std::vector<GUInt64> GetBlockSize() const override
     505             :     {
     506           5 :         std::vector<GUInt64> ret(GetDimensionCount());
     507          10 :         const auto parentBlockSize(m_poParent->GetBlockSize());
     508          17 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
     509             :         {
     510          12 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
     511          12 :             if (iOldAxis >= 0)
     512             :             {
     513          11 :                 ret[i] = parentBlockSize[iOldAxis];
     514             :             }
     515             :         }
     516          10 :         return ret;
     517             :     }
     518             : 
     519             :     std::shared_ptr<GDALAttribute>
     520           4 :     GetAttribute(const std::string &osName) const override
     521             :     {
     522           4 :         return m_poParent->GetAttribute(osName);
     523             :     }
     524             : 
     525             :     std::vector<std::shared_ptr<GDALAttribute>>
     526           8 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     527             :     {
     528           8 :         return m_poParent->GetAttributes(papszOptions);
     529             :     }
     530             : };
     531             : 
     532             : /************************************************************************/
     533             : /*                        PrepareParentArrays()                         */
     534             : /************************************************************************/
     535             : 
     536          48 : void GDALMDArrayTransposed::PrepareParentArrays(
     537             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
     538             :     const GPtrDiff_t *bufferStride) const
     539             : {
     540         179 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
     541             :     {
     542         131 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
     543         131 :         if (iOldAxis >= 0)
     544             :         {
     545         130 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
     546         130 :             m_parentCount[iOldAxis] = count[i];
     547         130 :             if (arrayStep)  // only null when called from IAdviseRead()
     548             :             {
     549         128 :                 m_parentStep[iOldAxis] = arrayStep[i];
     550             :             }
     551         130 :             if (bufferStride)  // only null when called from IAdviseRead()
     552             :             {
     553         128 :                 m_parentStride[iOldAxis] = bufferStride[i];
     554             :             }
     555             :         }
     556             :     }
     557          48 : }
     558             : 
     559             : /************************************************************************/
     560             : /*                               IRead()                                */
     561             : /************************************************************************/
     562             : 
     563          45 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
     564             :                                   const size_t *count, const GInt64 *arrayStep,
     565             :                                   const GPtrDiff_t *bufferStride,
     566             :                                   const GDALExtendedDataType &bufferDataType,
     567             :                                   void *pDstBuffer) const
     568             : {
     569          45 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
     570          90 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
     571          45 :                             m_parentStep.data(), m_parentStride.data(),
     572          45 :                             bufferDataType, pDstBuffer);
     573             : }
     574             : 
     575             : /************************************************************************/
     576             : /*                               IWrite()                               */
     577             : /************************************************************************/
     578             : 
     579           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
     580             :                                    const size_t *count, const GInt64 *arrayStep,
     581             :                                    const GPtrDiff_t *bufferStride,
     582             :                                    const GDALExtendedDataType &bufferDataType,
     583             :                                    const void *pSrcBuffer)
     584             : {
     585           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
     586           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
     587           2 :                              m_parentStep.data(), m_parentStride.data(),
     588           2 :                              bufferDataType, pSrcBuffer);
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*                            IAdviseRead()                             */
     593             : /************************************************************************/
     594             : 
     595           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
     596             :                                         const size_t *count,
     597             :                                         CSLConstList papszOptions) const
     598             : {
     599           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
     600           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
     601           1 :                                   papszOptions);
     602             : }
     603             : 
     604             : //! @endcond
     605             : 
     606             : /************************************************************************/
     607             : /*                             Transpose()                              */
     608             : /************************************************************************/
     609             : 
     610             : /** Return a view of the array whose axis have been reordered.
     611             :  *
     612             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
     613             :  * and GetDimensionCount() - 1, and each only once.
     614             :  * -1 can be used as a special index value to ask for the insertion of a new
     615             :  * axis of size 1.
     616             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
     617             :  * index of one of its dimension, it corresponds to the axis of index
     618             :  * anMapNewAxisToOldAxis[i] from the current array.
     619             :  *
     620             :  * This is similar to the numpy.transpose() method
     621             :  *
     622             :  * The returned array holds a reference to the original one, and thus is
     623             :  * a view of it (not a copy). If the content of the original array changes,
     624             :  * the content of the view array too. The view can be written if the underlying
     625             :  * array is writable.
     626             :  *
     627             :  * Note that I/O performance in such a transposed view might be poor.
     628             :  *
     629             :  * This is the same as the C function GDALMDArrayTranspose().
     630             :  *
     631             :  * @return a new array, that holds a reference to the original one, and thus is
     632             :  * a view of it (not a copy), or nullptr in case of error.
     633             :  */
     634             : std::shared_ptr<GDALMDArray>
     635          52 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
     636             : {
     637         104 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
     638          52 :     if (!self)
     639             :     {
     640           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     641             :                  "Driver implementation issue: m_pSelf not set !");
     642           0 :         return nullptr;
     643             :     }
     644          52 :     const int nDims = static_cast<int>(GetDimensionCount());
     645         104 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
     646          52 :     int nCountOldAxis = 0;
     647         185 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
     648             :     {
     649         137 :         if (iOldAxis < -1 || iOldAxis >= nDims)
     650             :         {
     651           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
     652           4 :             return nullptr;
     653             :         }
     654         134 :         if (iOldAxis >= 0)
     655             :         {
     656         132 :             if (alreadyUsedOldAxis[iOldAxis])
     657             :             {
     658           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
     659             :                          iOldAxis);
     660           1 :                 return nullptr;
     661             :             }
     662         131 :             alreadyUsedOldAxis[iOldAxis] = true;
     663         131 :             nCountOldAxis++;
     664             :         }
     665             :     }
     666          48 :     if (nCountOldAxis != nDims)
     667             :     {
     668           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     669             :                  "One or several original axis missing");
     670           4 :         return nullptr;
     671             :     }
     672          44 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
     673             : }

Generated by: LCOV version 1.14