LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_abstract_array.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 346 404 85.6 %
Date: 2026-09-18 08:43:21 Functions: 17 18 94.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim_abstract_array.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  Implementation of GDALAbstractMDArray class
       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 "cpl_float.h"
      15             : #include "cpl_safemaths.hpp"
      16             : #include "gdal_multidim.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <cassert>
      20             : #include <limits>
      21             : #include <map>
      22             : 
      23             : /************************************************************************/
      24             : /*                        ~GDALAbstractMDArray()                        */
      25             : /************************************************************************/
      26             : 
      27             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
      28             : 
      29             : /************************************************************************/
      30             : /*                        GDALAbstractMDArray()                         */
      31             : /************************************************************************/
      32             : 
      33             : //! @cond Doxygen_Suppress
      34      175210 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
      35      175210 :                                          const std::string &osName)
      36             :     : m_osName(osName),
      37             :       m_osFullName(
      38      175210 :           !osParentName.empty()
      39      347083 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
      40      697503 :               : osName)
      41             : {
      42      175210 : }
      43             : 
      44             : //! @endcond
      45             : 
      46             : /************************************************************************/
      47             : /*                           GetDimensions()                            */
      48             : /************************************************************************/
      49             : 
      50             : /** \fn GDALAbstractMDArray::GetDimensions() const
      51             :  * \brief Return the dimensions of an attribute/array.
      52             :  *
      53             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
      54             :  * similar to GDALAttributeGetDimensionsSize().
      55             :  */
      56             : 
      57             : /************************************************************************/
      58             : /*                            GetDataType()                             */
      59             : /************************************************************************/
      60             : 
      61             : /** \fn GDALAbstractMDArray::GetDataType() const
      62             :  * \brief Return the data type of an attribute/array.
      63             :  *
      64             :  * This is the same as the C functions GDALMDArrayGetDataType() and
      65             :  * GDALAttributeGetDataType()
      66             :  */
      67             : 
      68             : /************************************************************************/
      69             : /*                         GetDimensionCount()                          */
      70             : /************************************************************************/
      71             : 
      72             : /** Return the number of dimensions.
      73             :  *
      74             :  * Default implementation is GetDimensions().size(), and may be overridden by
      75             :  * drivers if they have a faster / less expensive implementations.
      76             :  *
      77             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
      78             :  * GDALAttributeGetDimensionCount().
      79             :  *
      80             :  */
      81       62720 : size_t GDALAbstractMDArray::GetDimensionCount() const
      82             : {
      83       62720 :     return GetDimensions().size();
      84             : }
      85             : 
      86             : /************************************************************************/
      87             : /*                               Rename()                               */
      88             : /************************************************************************/
      89             : 
      90             : /** Rename the attribute/array.
      91             :  *
      92             :  * This is not implemented by all drivers.
      93             :  *
      94             :  * Drivers known to implement it: MEM, netCDF, Zarr.
      95             :  *
      96             :  * This is the same as the C functions GDALMDArrayRename() or
      97             :  * GDALAttributeRename().
      98             :  *
      99             :  * @param osNewName New name.
     100             :  *
     101             :  * @return true in case of success
     102             :  * @since GDAL 3.8
     103             :  */
     104           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
     105             : {
     106           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
     107           0 :     return false;
     108             : }
     109             : 
     110             : /************************************************************************/
     111             : /*                             CopyValue()                              */
     112             : /************************************************************************/
     113             : 
     114             : /** Convert a value from a source type to a destination type.
     115             :  *
     116             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
     117             :  * that must be freed with CPLFree().
     118             :  */
     119      623917 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
     120             :                                      const GDALExtendedDataType &srcType,
     121             :                                      void *pDst,
     122             :                                      const GDALExtendedDataType &dstType)
     123             : {
     124     1238500 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
     125      614579 :         dstType.GetClass() == GEDTC_NUMERIC)
     126             :     {
     127      612316 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
     128             :                         dstType.GetNumericDataType(), 0, 1);
     129      612316 :         return true;
     130             :     }
     131       20521 :     if (srcType.GetClass() == GEDTC_STRING &&
     132        8920 :         dstType.GetClass() == GEDTC_STRING)
     133             :     {
     134             :         const char *srcStrPtr;
     135        7660 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
     136        7660 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
     137        7660 :         *reinterpret_cast<void **>(pDst) = pszDup;
     138        7660 :         return true;
     139             :     }
     140        6204 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
     141        2263 :         dstType.GetClass() == GEDTC_STRING)
     142             :     {
     143        2263 :         const char *str = nullptr;
     144        2263 :         switch (srcType.GetNumericDataType())
     145             :         {
     146           0 :             case GDT_Unknown:
     147           0 :                 break;
     148         531 :             case GDT_UInt8:
     149         531 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
     150         531 :                 break;
     151           3 :             case GDT_Int8:
     152           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
     153           3 :                 break;
     154         110 :             case GDT_UInt16:
     155         110 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
     156         110 :                 break;
     157           0 :             case GDT_Int16:
     158           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
     159           0 :                 break;
     160         153 :             case GDT_UInt32:
     161         153 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
     162         153 :                 break;
     163          68 :             case GDT_Int32:
     164          68 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
     165          68 :                 break;
     166           0 :             case GDT_UInt64:
     167             :                 str =
     168           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
     169             :                                static_cast<GUIntBig>(
     170             :                                    *static_cast<const std::uint64_t *>(pSrc)));
     171           0 :                 break;
     172          53 :             case GDT_Int64:
     173          53 :                 str = CPLSPrintf(CPL_FRMT_GIB,
     174             :                                  static_cast<GIntBig>(
     175             :                                      *static_cast<const std::int64_t *>(pSrc)));
     176          53 :                 break;
     177           0 :             case GDT_Float16:
     178           0 :                 str = CPLSPrintf("%.5g",
     179             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
     180           0 :                 break;
     181         449 :             case GDT_Float32:
     182         898 :                 str = CPLSPrintf(
     183             :                     "%.9g",
     184         449 :                     static_cast<double>(*static_cast<const float *>(pSrc)));
     185         449 :                 break;
     186         894 :             case GDT_Float64:
     187         894 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
     188         894 :                 break;
     189           2 :             case GDT_CInt16:
     190             :             {
     191           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
     192           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
     193           2 :                 break;
     194             :             }
     195           0 :             case GDT_CInt32:
     196             :             {
     197           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
     198           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
     199           0 :                 break;
     200             :             }
     201           0 :             case GDT_CFloat16:
     202             :             {
     203           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
     204           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
     205           0 :                 break;
     206             :             }
     207           0 :             case GDT_CFloat32:
     208             :             {
     209           0 :                 const float *src = static_cast<const float *>(pSrc);
     210           0 :                 str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
     211           0 :                 break;
     212             :             }
     213           0 :             case GDT_CFloat64:
     214             :             {
     215           0 :                 const double *src = static_cast<const double *>(pSrc);
     216           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
     217           0 :                 break;
     218             :             }
     219           0 :             case GDT_TypeCount:
     220           0 :                 CPLAssert(false);
     221             :                 break;
     222             :         }
     223        2263 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
     224        2263 :         *reinterpret_cast<void **>(pDst) = pszDup;
     225        2263 :         return true;
     226             :     }
     227        2938 :     if (srcType.GetClass() == GEDTC_STRING &&
     228        1260 :         dstType.GetClass() == GEDTC_NUMERIC)
     229             :     {
     230             :         const char *srcStrPtr;
     231        1260 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
     232        1260 :         if (dstType.GetNumericDataType() == GDT_Int64)
     233             :         {
     234           2 :             *(static_cast<int64_t *>(pDst)) =
     235           2 :                 srcStrPtr == nullptr ? 0
     236           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
     237             :         }
     238        1258 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
     239             :         {
     240           2 :             *(static_cast<uint64_t *>(pDst)) =
     241           2 :                 srcStrPtr == nullptr
     242           2 :                     ? 0
     243           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
     244             :         }
     245             :         else
     246             :         {
     247        1256 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
     248        1256 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
     249             :                             dstType.GetNumericDataType(), 0, 1);
     250             :         }
     251        1260 :         return true;
     252             :     }
     253         836 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
     254         418 :         dstType.GetClass() == GEDTC_COMPOUND)
     255             :     {
     256         418 :         const auto &srcComponents = srcType.GetComponents();
     257         418 :         const auto &dstComponents = dstType.GetComponents();
     258         418 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
     259         418 :         GByte *pabyDst = static_cast<GByte *>(pDst);
     260             : 
     261             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
     262         836 :             srcComponentMap;
     263        2375 :         for (const auto &srcComp : srcComponents)
     264             :         {
     265        1957 :             srcComponentMap[srcComp->GetName()] = &srcComp;
     266             :         }
     267        1119 :         for (const auto &dstComp : dstComponents)
     268             :         {
     269         701 :             auto oIter = srcComponentMap.find(dstComp->GetName());
     270         701 :             if (oIter == srcComponentMap.end())
     271           0 :                 return false;
     272         701 :             const auto &srcComp = *(oIter->second);
     273        2103 :             if (!GDALExtendedDataType::CopyValue(
     274         701 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
     275         701 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
     276             :             {
     277           0 :                 return false;
     278             :             }
     279             :         }
     280         418 :         return true;
     281             :     }
     282             : 
     283           0 :     return false;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                        CheckReadWriteParams()                        */
     288             : /************************************************************************/
     289             : //! @cond Doxygen_Suppress
     290       24181 : bool GDALAbstractMDArray::CheckReadWriteParams(
     291             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
     292             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
     293             :     const void *buffer, const void *buffer_alloc_start,
     294             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
     295             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
     296             : {
     297           2 :     const auto lambda_error = []()
     298             :     {
     299           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     300             :                  "Not all elements pointed by buffer will fit in "
     301             :                  "[buffer_alloc_start, "
     302             :                  "buffer_alloc_start + buffer_alloc_size]");
     303           2 :     };
     304             : 
     305       24181 :     const auto &dims = GetDimensions();
     306       24181 :     if (dims.empty())
     307             :     {
     308       14946 :         if (buffer_alloc_start)
     309             :         {
     310       13484 :             const size_t elementSize = bufferDataType.GetSize();
     311       13484 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
     312       13484 :             const GByte *paby_buffer_alloc_start =
     313             :                 static_cast<const GByte *>(buffer_alloc_start);
     314       13484 :             const GByte *paby_buffer_alloc_end =
     315             :                 paby_buffer_alloc_start + buffer_alloc_size;
     316             : 
     317       13484 :             if (paby_buffer < paby_buffer_alloc_start ||
     318       13484 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
     319             :             {
     320           0 :                 lambda_error();
     321           0 :                 return false;
     322             :             }
     323             :         }
     324       14946 :         return true;
     325             :     }
     326             : 
     327        9235 :     if (arrayStep == nullptr)
     328             :     {
     329        2257 :         tmp_arrayStep.resize(dims.size(), 1);
     330        2257 :         arrayStep = tmp_arrayStep.data();
     331             :     }
     332       24952 :     for (size_t i = 0; i < dims.size(); i++)
     333             :     {
     334       15717 :         assert(count);
     335       15717 :         if (count[i] == 0)
     336             :         {
     337           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
     338             :                      static_cast<unsigned>(i));
     339           0 :             return false;
     340             :         }
     341             :     }
     342             : 
     343        9235 :     if (bufferStride == nullptr)
     344             :     {
     345        1926 :         GPtrDiff_t stride = 1;
     346        1926 :         assert(dims.empty() || count != nullptr);
     347             :         // To compute strides we must proceed from the fastest varying dimension
     348             :         // (the last one), and then reverse the result
     349        4426 :         for (size_t i = dims.size(); i != 0;)
     350             :         {
     351        2500 :             --i;
     352        2500 :             tmp_bufferStride.push_back(stride);
     353        2500 :             GUInt64 newStride = 0;
     354             :             bool bOK;
     355             :             try
     356             :             {
     357        2500 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
     358        5000 :                              CPLSM(static_cast<uint64_t>(count[i])))
     359        2500 :                                 .v();
     360        2500 :                 bOK = static_cast<size_t>(newStride) == newStride &&
     361        2500 :                       newStride < std::numeric_limits<size_t>::max() / 2;
     362             :             }
     363           0 :             catch (...)
     364             :             {
     365           0 :                 bOK = false;
     366             :             }
     367        2500 :             if (!bOK)
     368             :             {
     369           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
     370           0 :                 return false;
     371             :             }
     372        2500 :             stride = static_cast<GPtrDiff_t>(newStride);
     373             :         }
     374        1926 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
     375        1926 :         bufferStride = tmp_bufferStride.data();
     376             :     }
     377             : 
     378       24923 :     for (size_t i = 0; i < dims.size(); i++)
     379             :     {
     380       15698 :         assert(arrayStartIdx);
     381       15698 :         assert(count);
     382       15698 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
     383             :         {
     384           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     385             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
     386             :                      static_cast<unsigned>(i),
     387           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
     388           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
     389           2 :             return false;
     390             :         }
     391             :         bool bOverflow;
     392       15696 :         if (arrayStep[i] >= 0)
     393             :         {
     394             :             try
     395             :             {
     396       14443 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
     397       14445 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     398       57775 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
     399       14443 :                                 .v() >= dims[i]->GetSize();
     400             :             }
     401           1 :             catch (...)
     402             :             {
     403           1 :                 bOverflow = true;
     404             :             }
     405       14444 :             if (bOverflow)
     406             :             {
     407           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
     408             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
     409             :                          ">= " CPL_FRMT_GUIB,
     410             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
     411             :                          static_cast<unsigned>(i),
     412           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
     413           5 :                 return false;
     414             :             }
     415             :         }
     416             :         else
     417             :         {
     418             :             try
     419             :             {
     420        1252 :                 bOverflow =
     421        1252 :                     arrayStartIdx[i] <
     422        1252 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     423        2504 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
     424             :                                ? (static_cast<uint64_t>(1) << 63)
     425        2504 :                                : static_cast<uint64_t>(-arrayStep[i])))
     426        1252 :                         .v();
     427             :             }
     428           0 :             catch (...)
     429             :             {
     430           0 :                 bOverflow = true;
     431             :             }
     432        1252 :             if (bOverflow)
     433             :             {
     434           3 :                 CPLError(
     435             :                     CE_Failure, CPLE_AppDefined,
     436             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
     437             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
     438             :                     static_cast<unsigned>(i));
     439           3 :                 return false;
     440             :             }
     441             :         }
     442             :     }
     443             : 
     444        9225 :     if (buffer_alloc_start)
     445             :     {
     446        4382 :         const size_t elementSize = bufferDataType.GetSize();
     447        4382 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
     448        4382 :         const GByte *paby_buffer_alloc_start =
     449             :             static_cast<const GByte *>(buffer_alloc_start);
     450        4382 :         const GByte *paby_buffer_alloc_end =
     451             :             paby_buffer_alloc_start + buffer_alloc_size;
     452             : 
     453        4382 :         GUInt64 nOffsetMaxNeg = 0;
     454        4382 :         GUInt64 nOffsetMax = elementSize;
     455       12556 :         for (size_t i = 0; i < dims.size(); i++)
     456             :         {
     457        8174 :             if (count[i] > 1)
     458             :             {
     459             :                 try
     460             :                 {
     461        7068 :                     if (bufferStride[i] >= 0)
     462             :                     {
     463             :                         nOffsetMax =
     464        7066 :                             (CPLSM(static_cast<uint64_t>(nOffsetMax)) +
     465        7066 :                              CPLSM(static_cast<uint64_t>(bufferStride[i])) *
     466       14132 :                                  CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     467       28264 :                                  CPLSM(static_cast<uint64_t>(elementSize)))
     468        7066 :                                 .v();
     469             :                     }
     470             :                     else
     471             :                     {
     472             :                         nOffsetMaxNeg =
     473           2 :                             (CPLSM(static_cast<uint64_t>(nOffsetMaxNeg)) +
     474           2 :                              CPLSM(static_cast<uint64_t>(-bufferStride[i])) *
     475           4 :                                  CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     476           8 :                                  CPLSM(static_cast<uint64_t>(elementSize)))
     477           2 :                                 .v();
     478             :                     }
     479             :                 }
     480           0 :                 catch (...)
     481             :                 {
     482           0 :                     lambda_error();
     483           0 :                     return false;
     484             :                 }
     485             :             }
     486             :         }
     487             : #if SIZEOF_VOIDP == 4
     488             :         if (static_cast<size_t>(nOffsetMax) != nOffsetMax ||
     489             :             static_cast<size_t>(nOffsetMaxNeg) != nOffsetMaxNeg)
     490             :         {
     491             :             lambda_error();
     492             :             return false;
     493             :         }
     494             : #endif
     495        4382 :         if (paby_buffer - nOffsetMaxNeg < paby_buffer_alloc_start)
     496             :         {
     497           2 :             lambda_error();
     498           2 :             return false;
     499             :         }
     500        4380 :         if (paby_buffer + nOffsetMax > paby_buffer_alloc_end)
     501             :         {
     502           0 :             lambda_error();
     503           0 :             return false;
     504             :         }
     505             :     }
     506             : 
     507        9223 :     return true;
     508             : }
     509             : 
     510             : //! @endcond
     511             : 
     512             : /************************************************************************/
     513             : /*                                Read()                                */
     514             : /************************************************************************/
     515             : 
     516             : /** Read part or totality of a multidimensional array or attribute.
     517             :  *
     518             :  * This will extract the content of a hyper-rectangle from the array into
     519             :  * a user supplied buffer.
     520             :  *
     521             :  * If bufferDataType is of type string, the values written in pDstBuffer
     522             :  * will be char* pointers and the strings should be freed with CPLFree().
     523             :  *
     524             :  * This is the same as the C function GDALMDArrayRead().
     525             :  *
     526             :  * @param arrayStartIdx Values representing the starting index to read
     527             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
     528             :  *                      Array of GetDimensionCount() values. Must not be
     529             :  *                      nullptr, unless for a zero-dimensional array.
     530             :  *
     531             :  * @param count         Values representing the number of values to extract in
     532             :  *                      each dimension.
     533             :  *                      Array of GetDimensionCount() values. Must not be
     534             :  *                      nullptr, unless for a zero-dimensional array.
     535             :  *
     536             :  * @param arrayStep     Spacing between values to extract in each dimension.
     537             :  *                      The spacing is in number of array elements, not bytes.
     538             :  *                      If provided, must contain GetDimensionCount() values.
     539             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
     540             :  * default to indicate consecutive elements.
     541             :  *
     542             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
     543             :  *                      The spacing is in number of array elements, not bytes.
     544             :  *                      If provided, must contain GetDimensionCount() values.
     545             :  *                      Negative values are possible (for example to reorder
     546             :  *                      from bottom-to-top to top-to-bottom).
     547             :  *                      If set to nullptr, will be set so that pDstBuffer is
     548             :  *                      written in a compact way, with elements of the last /
     549             :  *                      fastest varying dimension being consecutive.
     550             :  *
     551             :  * @param bufferDataType Data type of values in pDstBuffer.
     552             :  *
     553             :  * @param pDstBuffer    User buffer to store the values read. Should be big
     554             :  *                      enough to store the number of values indicated by
     555             :  * count[] and with the spacing of bufferStride[].
     556             :  *
     557             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
     558             :  *                             validity of pDstBuffer. pDstBufferAllocStart
     559             :  * should be the pointer returned by the malloc() or equivalent call used to
     560             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
     561             :  * bufferStride[] values are all positive), but not necessarily. If specified,
     562             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
     563             :  * validation is needed, nullptr can be passed.
     564             :  *
     565             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
     566             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
     567             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
     568             :  *                             set to the appropriate value.
     569             :  *                             If no validation is needed, 0 can be passed.
     570             :  *
     571             :  * @return true in case of success.
     572             :  */
     573       13108 : bool GDALAbstractMDArray::Read(
     574             :     const GUInt64 *arrayStartIdx, const size_t *count,
     575             :     const GInt64 *arrayStep,         // step in elements
     576             :     const GPtrDiff_t *bufferStride,  // stride in elements
     577             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
     578             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
     579             : {
     580       13108 :     if (!GetDataType().CanConvertTo(bufferDataType))
     581             :     {
     582           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     583             :                  "Array data type is not convertible to buffer data type");
     584           0 :         return false;
     585             :     }
     586             : 
     587       26216 :     std::vector<GInt64> tmp_arrayStep;
     588       26216 :     std::vector<GPtrDiff_t> tmp_bufferStride;
     589       13108 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
     590             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
     591             :                               nDstBufferAllocSize, tmp_arrayStep,
     592             :                               tmp_bufferStride))
     593             :     {
     594           0 :         return false;
     595             :     }
     596             : 
     597       13108 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
     598       13108 :                  pDstBuffer);
     599             : }
     600             : 
     601             : /************************************************************************/
     602             : /*                               IWrite()                               */
     603             : /************************************************************************/
     604             : 
     605             : //! @cond Doxygen_Suppress
     606           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
     607             :                                  const GInt64 *, const GPtrDiff_t *,
     608             :                                  const GDALExtendedDataType &, const void *)
     609             : {
     610           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
     611           1 :     return false;
     612             : }
     613             : 
     614             : //! @endcond
     615             : 
     616             : /************************************************************************/
     617             : /*                               Write()                                */
     618             : /************************************************************************/
     619             : 
     620             : /** Write part or totality of a multidimensional array or attribute.
     621             :  *
     622             :  * This will set the content of a hyper-rectangle into the array from
     623             :  * a user supplied buffer.
     624             :  *
     625             :  * If bufferDataType is of type string, the values read from pSrcBuffer
     626             :  * will be char* pointers.
     627             :  *
     628             :  * This is the same as the C function GDALMDArrayWrite().
     629             :  *
     630             :  * @param arrayStartIdx Values representing the starting index to write
     631             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
     632             :  *                      Array of GetDimensionCount() values. Must not be
     633             :  *                      nullptr, unless for a zero-dimensional array.
     634             :  *
     635             :  * @param count         Values representing the number of values to write in
     636             :  *                      each dimension.
     637             :  *                      Array of GetDimensionCount() values. Must not be
     638             :  *                      nullptr, unless for a zero-dimensional array.
     639             :  *
     640             :  * @param arrayStep     Spacing between values to write in each dimension.
     641             :  *                      The spacing is in number of array elements, not bytes.
     642             :  *                      If provided, must contain GetDimensionCount() values.
     643             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
     644             :  * default to indicate consecutive elements.
     645             :  *
     646             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
     647             :  *                      The spacing is in number of array elements, not bytes.
     648             :  *                      If provided, must contain GetDimensionCount() values.
     649             :  *                      Negative values are possible (for example to reorder
     650             :  *                      from bottom-to-top to top-to-bottom).
     651             :  *                      If set to nullptr, will be set so that pSrcBuffer is
     652             :  *                      written in a compact way, with elements of the last /
     653             :  *                      fastest varying dimension being consecutive.
     654             :  *
     655             :  * @param bufferDataType Data type of values in pSrcBuffer.
     656             :  *
     657             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
     658             :  *                      enough to store the number of values indicated by
     659             :  * count[] and with the spacing of bufferStride[].
     660             :  *
     661             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
     662             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
     663             :  * should be the pointer returned by the malloc() or equivalent call used to
     664             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
     665             :  * bufferStride[] values are all positive), but not necessarily. If specified,
     666             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
     667             :  * validation is needed, nullptr can be passed.
     668             :  *
     669             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
     670             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
     671             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
     672             :  *                             set to the appropriate value.
     673             :  *                             If no validation is needed, 0 can be passed.
     674             :  *
     675             :  * @return true in case of success.
     676             :  */
     677        4057 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
     678             :                                 const size_t *count, const GInt64 *arrayStep,
     679             :                                 const GPtrDiff_t *bufferStride,
     680             :                                 const GDALExtendedDataType &bufferDataType,
     681             :                                 const void *pSrcBuffer,
     682             :                                 const void *pSrcBufferAllocStart,
     683             :                                 size_t nSrcBufferAllocSize)
     684             : {
     685        4057 :     if (!bufferDataType.CanConvertTo(GetDataType()))
     686             :     {
     687           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     688             :                  "Buffer data type is not convertible to array data type");
     689           0 :         return false;
     690             :     }
     691             : 
     692        8114 :     std::vector<GInt64> tmp_arrayStep;
     693        8114 :     std::vector<GPtrDiff_t> tmp_bufferStride;
     694        4057 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
     695             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
     696             :                               nSrcBufferAllocSize, tmp_arrayStep,
     697             :                               tmp_bufferStride))
     698             :     {
     699           1 :         return false;
     700             :     }
     701             : 
     702        4056 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
     703        4056 :                   pSrcBuffer);
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                       GetTotalElementsCount()                        */
     708             : /************************************************************************/
     709             : 
     710             : /** Return the total number of values in the array.
     711             :  *
     712             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
     713             :  * and GDALAttributeGetTotalElementsCount().
     714             :  *
     715             :  */
     716        1914 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
     717             : {
     718        1914 :     const auto &dims = GetDimensions();
     719        1914 :     if (dims.empty())
     720        1074 :         return 1;
     721         840 :     GUInt64 nElts = 1;
     722        1826 :     for (const auto &dim : dims)
     723             :     {
     724             :         try
     725             :         {
     726         986 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
     727        2958 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
     728         986 :                         .v();
     729             :         }
     730           0 :         catch (...)
     731             :         {
     732           0 :             return 0;
     733             :         }
     734             :     }
     735         840 :     return nElts;
     736             : }
     737             : 
     738             : /************************************************************************/
     739             : /*                            GetBlockSize()                            */
     740             : /************************************************************************/
     741             : 
     742             : /** Return the "natural" block size of the array along all dimensions.
     743             :  *
     744             :  * Some drivers might organize the array in tiles/blocks and reading/writing
     745             :  * aligned on those tile/block boundaries will be more efficient.
     746             :  *
     747             :  * The returned number of elements in the vector is the same as
     748             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
     749             :  * the natural block size along the considered dimension.
     750             :  * "Flat" arrays will typically return a vector of values set to 0.
     751             :  *
     752             :  * The default implementation will return a vector of values set to 0.
     753             :  *
     754             :  * This method is used by GetProcessingChunkSize().
     755             :  *
     756             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikely
     757             :  * theoretical case of a 32-bit platform, this might exceed its size_t
     758             :  * allocation capabilities.
     759             :  *
     760             :  * This is the same as the C function GDALMDArrayGetBlockSize().
     761             :  *
     762             :  * @return the block size, in number of elements along each dimension.
     763             :  */
     764         358 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
     765             : {
     766         358 :     return std::vector<GUInt64>(GetDimensionCount());
     767             : }
     768             : 
     769             : /************************************************************************/
     770             : /*                       GetProcessingChunkSize()                       */
     771             : /************************************************************************/
     772             : 
     773             : /** \brief Return an optimal chunk size for read/write operations, given the
     774             :  * natural block size and memory constraints specified.
     775             :  *
     776             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
     777             :  * multiple of those returned by GetBlockSize() (unless the block define by
     778             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
     779             :  * returned by this method).
     780             :  *
     781             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
     782             :  *
     783             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
     784             :  * chunk.
     785             :  *
     786             :  * @return the chunk size, in number of elements along each dimension.
     787             :  */
     788             : std::vector<size_t>
     789         124 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
     790             : {
     791         124 :     const auto &dims = GetDimensions();
     792         124 :     const auto &nDTSize = GetDataType().GetSize();
     793         124 :     std::vector<size_t> anChunkSize;
     794         248 :     auto blockSize = GetBlockSize();
     795         124 :     CPLAssert(blockSize.size() == dims.size());
     796         124 :     size_t nChunkSize = nDTSize;
     797         124 :     bool bOverflow = false;
     798         124 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
     799             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
     800             :     // [1, min(sizet_max, dim_size[i])]
     801             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
     802         331 :     for (size_t i = 0; i < dims.size(); i++)
     803             :     {
     804             :         const auto sizeDimI =
     805         414 :             std::max(static_cast<size_t>(1),
     806         414 :                      static_cast<size_t>(
     807         414 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
     808         207 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
     809         207 :         anChunkSize.push_back(sizeDimI);
     810         207 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
     811             :         {
     812           4 :             bOverflow = true;
     813             :         }
     814             :         else
     815             :         {
     816         203 :             nChunkSize *= sizeDimI;
     817             :         }
     818             :     }
     819         124 :     if (nChunkSize == 0)
     820           0 :         return anChunkSize;
     821             : 
     822             :     // If the product of all anChunkSize[i] does not fit on size_t, then
     823             :     // set lowest anChunkSize[i] to 1.
     824         124 :     if (bOverflow)
     825             :     {
     826           2 :         nChunkSize = nDTSize;
     827           2 :         bOverflow = false;
     828           8 :         for (size_t i = dims.size(); i > 0;)
     829             :         {
     830           6 :             --i;
     831           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
     832             :             {
     833           4 :                 bOverflow = true;
     834           4 :                 anChunkSize[i] = 1;
     835             :             }
     836             :             else
     837             :             {
     838           2 :                 nChunkSize *= anChunkSize[i];
     839             :             }
     840             :         }
     841             :     }
     842             : 
     843         124 :     nChunkSize = nDTSize;
     844         248 :     std::vector<size_t> anAccBlockSizeFromStart;
     845         331 :     for (size_t i = 0; i < dims.size(); i++)
     846             :     {
     847         207 :         nChunkSize *= anChunkSize[i];
     848         207 :         anAccBlockSizeFromStart.push_back(nChunkSize);
     849             :     }
     850         124 :     if (nChunkSize <= nMaxChunkMemory / 2)
     851             :     {
     852         120 :         size_t nVoxelsFromEnd = 1;
     853         319 :         for (size_t i = dims.size(); i > 0;)
     854             :         {
     855         199 :             --i;
     856             :             const auto nCurBlockSize =
     857         199 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
     858         199 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
     859         199 :             if (nMul >= 2)
     860             :             {
     861         191 :                 const auto nSizeThisDim(dims[i]->GetSize());
     862             :                 const auto nBlocksThisDim =
     863         191 :                     cpl::div_round_up(nSizeThisDim, anChunkSize[i]);
     864         191 :                 anChunkSize[i] = static_cast<size_t>(std::min(
     865         191 :                     anChunkSize[i] *
     866         382 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
     867         191 :                     nSizeThisDim));
     868             :             }
     869         199 :             nVoxelsFromEnd *= anChunkSize[i];
     870             :         }
     871             :     }
     872         124 :     return anChunkSize;
     873             : }
     874             : 
     875             : /************************************************************************/
     876             : /*                             BaseRename()                             */
     877             : /************************************************************************/
     878             : 
     879             : //! @cond Doxygen_Suppress
     880          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
     881             : {
     882          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
     883          18 :     m_osFullName += osNewName;
     884          18 :     m_osName = osNewName;
     885             : 
     886          18 :     NotifyChildrenOfRenaming();
     887          18 : }
     888             : 
     889             : //! @endcond
     890             : 
     891             : //! @cond Doxygen_Suppress
     892             : /************************************************************************/
     893             : /*                           ParentRenamed()                            */
     894             : /************************************************************************/
     895             : 
     896          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
     897             : {
     898          50 :     m_osFullName = osNewParentFullName;
     899          50 :     m_osFullName += "/";
     900          50 :     m_osFullName += m_osName;
     901             : 
     902          50 :     NotifyChildrenOfRenaming();
     903          50 : }
     904             : 
     905             : //! @endcond
     906             : 
     907             : /************************************************************************/
     908             : /*                              Deleted()                               */
     909             : /************************************************************************/
     910             : 
     911             : //! @cond Doxygen_Suppress
     912          58 : void GDALAbstractMDArray::Deleted()
     913             : {
     914          58 :     m_bValid = false;
     915             : 
     916          58 :     NotifyChildrenOfDeletion();
     917          58 : }
     918             : 
     919             : //! @endcond
     920             : 
     921             : /************************************************************************/
     922             : /*                           ParentDeleted()                            */
     923             : /************************************************************************/
     924             : 
     925             : //! @cond Doxygen_Suppress
     926          30 : void GDALAbstractMDArray::ParentDeleted()
     927             : {
     928          30 :     Deleted();
     929          30 : }
     930             : 
     931             : //! @endcond
     932             : 
     933             : /************************************************************************/
     934             : /*                     CheckValidAndErrorOutIfNot()                     */
     935             : /************************************************************************/
     936             : 
     937             : //! @cond Doxygen_Suppress
     938       11786 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
     939             : {
     940       11786 :     if (!m_bValid)
     941             :     {
     942          26 :         CPLError(CE_Failure, CPLE_AppDefined,
     943             :                  "This object has been deleted. No action on it is possible");
     944             :     }
     945       11786 :     return m_bValid;
     946             : }
     947             : 
     948             : //! @endcond
     949             : 
     950             : /************************************************************************/
     951             : /*                          ProcessPerChunk()                           */
     952             : /************************************************************************/
     953             : 
     954             : namespace
     955             : {
     956             : enum class Caller
     957             : {
     958             :     CALLER_END_OF_LOOP,
     959             :     CALLER_IN_LOOP,
     960             : };
     961             : }
     962             : 
     963             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
     964             :  *
     965             :  * This method is to be used when doing operations on an array, or a subset of
     966             :  * it, in a chunk by chunk way.
     967             :  *
     968             :  * @param arrayStartIdx Values representing the starting index to use
     969             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
     970             :  *                      Array of GetDimensionCount() values. Must not be
     971             :  *                      nullptr, unless for a zero-dimensional array.
     972             :  *
     973             :  * @param count         Values representing the number of values to use in
     974             :  *                      each dimension.
     975             :  *                      Array of GetDimensionCount() values. Must not be
     976             :  *                      nullptr, unless for a zero-dimensional array.
     977             :  *
     978             :  * @param chunkSize     Values representing the chunk size in each dimension.
     979             :  *                      Might typically the output of GetProcessingChunkSize().
     980             :  *                      Array of GetDimensionCount() values. Must not be
     981             :  *                      nullptr, unless for a zero-dimensional array.
     982             :  *
     983             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
     984             :  *                      Must NOT be nullptr.
     985             :  *
     986             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
     987             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
     988             :  *
     989             :  * @return true in case of success.
     990             :  */
     991         122 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
     992             :                                           const GUInt64 *count,
     993             :                                           const size_t *chunkSize,
     994             :                                           FuncProcessPerChunkType pfnFunc,
     995             :                                           void *pUserData)
     996             : {
     997         122 :     const auto &dims = GetDimensions();
     998         122 :     if (dims.empty())
     999             :     {
    1000           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    1001             :     }
    1002             : 
    1003             :     // Sanity check
    1004         120 :     size_t nTotalChunkSize = 1;
    1005         304 :     for (size_t i = 0; i < dims.size(); i++)
    1006             :     {
    1007         191 :         const auto nSizeThisDim(dims[i]->GetSize());
    1008         191 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    1009         189 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    1010             :         {
    1011           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    1012             :                      "Inconsistent arrayStartIdx[] / count[] values "
    1013             :                      "regarding array size");
    1014           4 :             return false;
    1015             :         }
    1016         372 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    1017         185 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    1018             :         {
    1019           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    1020             :                      "Inconsistent chunkSize[] values");
    1021           3 :             return false;
    1022             :         }
    1023         184 :         nTotalChunkSize *= chunkSize[i];
    1024             :     }
    1025             : 
    1026         113 :     size_t dimIdx = 0;
    1027         226 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    1028         226 :     std::vector<size_t> chunkCount(dims.size());
    1029             : 
    1030             :     struct Stack
    1031             :     {
    1032             :         GUInt64 nBlockCounter = 0;
    1033             :         GUInt64 nBlocksMinusOne = 0;
    1034             :         size_t first_count = 0;  // only used if nBlocks > 1
    1035             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    1036             :     };
    1037             : 
    1038         226 :     std::vector<Stack> stack(dims.size());
    1039         113 :     GUInt64 iCurChunk = 0;
    1040         113 :     GUInt64 nChunkCount = 1;
    1041         296 :     for (size_t i = 0; i < dims.size(); i++)
    1042             :     {
    1043         183 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    1044         183 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    1045         183 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    1046         183 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    1047         183 :         if (stack[i].nBlocksMinusOne == 0)
    1048             :         {
    1049         178 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    1050         178 :             chunkCount[i] = static_cast<size_t>(count[i]);
    1051             :         }
    1052             :         else
    1053             :         {
    1054           5 :             stack[i].first_count = static_cast<size_t>(
    1055           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    1056             :         }
    1057             :     }
    1058             : 
    1059         113 : lbl_next_depth:
    1060         406 :     if (dimIdx == dims.size())
    1061             :     {
    1062         146 :         ++iCurChunk;
    1063         146 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    1064             :                      iCurChunk, nChunkCount, pUserData))
    1065             :         {
    1066           3 :             return false;
    1067             :         }
    1068             :     }
    1069             :     else
    1070             :     {
    1071         260 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    1072             :         {
    1073          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    1074          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    1075          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    1076          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    1077             :             while (true)
    1078             :             {
    1079          33 :                 dimIdx++;
    1080          33 :                 goto lbl_next_depth;
    1081          33 :             lbl_return_to_caller_in_loop:
    1082          33 :                 --stack[dimIdx].nBlockCounter;
    1083          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    1084          11 :                     break;
    1085          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    1086          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    1087             :             }
    1088             : 
    1089          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    1090          22 :             chunkCount[dimIdx] =
    1091          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    1092          11 :                                     chunkArrayStartIdx[dimIdx]);
    1093          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    1094             :         }
    1095         260 :         dimIdx++;
    1096         260 :         goto lbl_next_depth;
    1097         254 :     lbl_return_to_caller_end_of_loop:
    1098         254 :         if (dimIdx == 0)
    1099         110 :             goto end;
    1100             :     }
    1101             : 
    1102         287 :     assert(dimIdx > 0);
    1103         287 :     dimIdx--;
    1104             :     // cppcheck-suppress negativeContainerIndex
    1105         287 :     switch (stack[dimIdx].return_point)
    1106             :     {
    1107         254 :         case Caller::CALLER_END_OF_LOOP:
    1108         254 :             goto lbl_return_to_caller_end_of_loop;
    1109          33 :         case Caller::CALLER_IN_LOOP:
    1110          33 :             goto lbl_return_to_caller_in_loop;
    1111             :     }
    1112         110 : end:
    1113         110 :     return true;
    1114             : }

Generated by: LCOV version 1.14