LCOV - code coverage report
Current view: top level - gcore/multidim - gdalmultidim_abstract_array.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 338 413 81.8 %
Date: 2026-04-15 22:10:00 Functions: 16 18 88.9 %

          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      172424 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
      35      172424 :                                          const std::string &osName)
      36             :     : m_osName(osName),
      37             :       m_osFullName(
      38      172424 :           !osParentName.empty()
      39      341863 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
      40      686711 :               : osName)
      41             : {
      42      172424 : }
      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       58548 : size_t GDALAbstractMDArray::GetDimensionCount() const
      82             : {
      83       58548 :     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      612201 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
     120             :                                      const GDALExtendedDataType &srcType,
     121             :                                      void *pDst,
     122             :                                      const GDALExtendedDataType &dstType)
     123             : {
     124     1215780 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
     125      603575 :         dstType.GetClass() == GEDTC_NUMERIC)
     126             :     {
     127      601324 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
     128             :                         dstType.GetNumericDataType(), 0, 1);
     129      601324 :         return true;
     130             :     }
     131       19087 :     if (srcType.GetClass() == GEDTC_STRING &&
     132        8210 :         dstType.GetClass() == GEDTC_STRING)
     133             :     {
     134             :         const char *srcStrPtr;
     135        6960 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
     136        6960 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
     137        6960 :         *reinterpret_cast<void **>(pDst) = pszDup;
     138        6960 :         return true;
     139             :     }
     140        6168 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
     141        2251 :         dstType.GetClass() == GEDTC_STRING)
     142             :     {
     143        2251 :         const char *str = nullptr;
     144        2251 :         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         100 :             case GDT_UInt16:
     155         100 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
     156         100 :                 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         892 :             case GDT_Float64:
     187         892 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
     188         892 :                 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        2251 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
     224        2251 :         *reinterpret_cast<void **>(pDst) = pszDup;
     225        2251 :         return true;
     226             :     }
     227        2916 :     if (srcType.GetClass() == GEDTC_STRING &&
     228        1250 :         dstType.GetClass() == GEDTC_NUMERIC)
     229             :     {
     230             :         const char *srcStrPtr;
     231        1250 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
     232        1250 :         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        1248 :         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        1246 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
     248        1246 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
     249             :                             dstType.GetNumericDataType(), 0, 1);
     250             :         }
     251        1250 :         return true;
     252             :     }
     253         832 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
     254         416 :         dstType.GetClass() == GEDTC_COMPOUND)
     255             :     {
     256         416 :         const auto &srcComponents = srcType.GetComponents();
     257         416 :         const auto &dstComponents = dstType.GetComponents();
     258         416 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
     259         416 :         GByte *pabyDst = static_cast<GByte *>(pDst);
     260             : 
     261             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
     262         832 :             srcComponentMap;
     263        2363 :         for (const auto &srcComp : srcComponents)
     264             :         {
     265        1947 :             srcComponentMap[srcComp->GetName()] = &srcComp;
     266             :         }
     267        1107 :         for (const auto &dstComp : dstComponents)
     268             :         {
     269         691 :             auto oIter = srcComponentMap.find(dstComp->GetName());
     270         691 :             if (oIter == srcComponentMap.end())
     271           0 :                 return false;
     272         691 :             const auto &srcComp = *(oIter->second);
     273        2073 :             if (!GDALExtendedDataType::CopyValue(
     274         691 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
     275         691 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
     276             :             {
     277           0 :                 return false;
     278             :             }
     279             :         }
     280         416 :         return true;
     281             :     }
     282             : 
     283           0 :     return false;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                        CheckReadWriteParams()                        */
     288             : /************************************************************************/
     289             : //! @cond Doxygen_Suppress
     290       22384 : 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           0 :     const auto lamda_error = []()
     298             :     {
     299           0 :         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           0 :     };
     304             : 
     305       22384 :     const auto &dims = GetDimensions();
     306       22384 :     if (dims.empty())
     307             :     {
     308       13772 :         if (buffer_alloc_start)
     309             :         {
     310       12900 :             const size_t elementSize = bufferDataType.GetSize();
     311       12900 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
     312       12900 :             const GByte *paby_buffer_alloc_start =
     313             :                 static_cast<const GByte *>(buffer_alloc_start);
     314       12900 :             const GByte *paby_buffer_alloc_end =
     315             :                 paby_buffer_alloc_start + buffer_alloc_size;
     316             : 
     317       12900 :             if (paby_buffer < paby_buffer_alloc_start ||
     318       12900 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
     319             :             {
     320           0 :                 lamda_error();
     321           0 :                 return false;
     322             :             }
     323             :         }
     324       13772 :         return true;
     325             :     }
     326             : 
     327        8612 :     if (arrayStep == nullptr)
     328             :     {
     329        2046 :         tmp_arrayStep.resize(dims.size(), 1);
     330        2046 :         arrayStep = tmp_arrayStep.data();
     331             :     }
     332       23324 :     for (size_t i = 0; i < dims.size(); i++)
     333             :     {
     334       14712 :         assert(count);
     335       14712 :         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        8612 :     bool bufferStride_all_positive = true;
     343        8612 :     if (bufferStride == nullptr)
     344             :     {
     345        1568 :         GPtrDiff_t stride = 1;
     346        1568 :         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        3548 :         for (size_t i = dims.size(); i != 0;)
     350             :         {
     351        1980 :             --i;
     352        1980 :             tmp_bufferStride.push_back(stride);
     353        1980 :             GUInt64 newStride = 0;
     354             :             bool bOK;
     355             :             try
     356             :             {
     357        1980 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
     358        3960 :                              CPLSM(static_cast<uint64_t>(count[i])))
     359        1980 :                                 .v();
     360        1980 :                 bOK = static_cast<size_t>(newStride) == newStride &&
     361        1980 :                       newStride < std::numeric_limits<size_t>::max() / 2;
     362             :             }
     363           0 :             catch (...)
     364             :             {
     365           0 :                 bOK = false;
     366             :             }
     367        1980 :             if (!bOK)
     368             :             {
     369           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
     370           0 :                 return false;
     371             :             }
     372        1980 :             stride = static_cast<GPtrDiff_t>(newStride);
     373             :         }
     374        1568 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
     375        1568 :         bufferStride = tmp_bufferStride.data();
     376             :     }
     377             :     else
     378             :     {
     379       19654 :         for (size_t i = 0; i < dims.size(); i++)
     380             :         {
     381       12671 :             if (bufferStride[i] < 0)
     382             :             {
     383          61 :                 bufferStride_all_positive = false;
     384          61 :                 break;
     385             :             }
     386             :         }
     387             :     }
     388       23293 :     for (size_t i = 0; i < dims.size(); i++)
     389             :     {
     390       14692 :         assert(arrayStartIdx);
     391       14692 :         assert(count);
     392       14692 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
     393             :         {
     394           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     395             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
     396             :                      static_cast<unsigned>(i),
     397           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
     398           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
     399           2 :             return false;
     400             :         }
     401             :         bool bOverflow;
     402       14690 :         if (arrayStep[i] >= 0)
     403             :         {
     404             :             try
     405             :             {
     406       13437 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
     407       13439 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     408       53751 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
     409       13437 :                                 .v() >= dims[i]->GetSize();
     410             :             }
     411           1 :             catch (...)
     412             :             {
     413           1 :                 bOverflow = true;
     414             :             }
     415       13438 :             if (bOverflow)
     416             :             {
     417           6 :                 CPLError(CE_Failure, CPLE_AppDefined,
     418             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
     419             :                          ">= " CPL_FRMT_GUIB,
     420             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
     421             :                          static_cast<unsigned>(i),
     422           6 :                          static_cast<GUInt64>(dims[i]->GetSize()));
     423           6 :                 return false;
     424             :             }
     425             :         }
     426             :         else
     427             :         {
     428             :             try
     429             :             {
     430        1252 :                 bOverflow =
     431        1252 :                     arrayStartIdx[i] <
     432        1252 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     433        2504 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
     434             :                                ? (static_cast<uint64_t>(1) << 63)
     435        2504 :                                : static_cast<uint64_t>(-arrayStep[i])))
     436        1252 :                         .v();
     437             :             }
     438           0 :             catch (...)
     439             :             {
     440           0 :                 bOverflow = true;
     441             :             }
     442        1252 :             if (bOverflow)
     443             :             {
     444           3 :                 CPLError(
     445             :                     CE_Failure, CPLE_AppDefined,
     446             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
     447             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
     448             :                     static_cast<unsigned>(i));
     449           3 :                 return false;
     450             :             }
     451             :         }
     452             :     }
     453             : 
     454        8601 :     if (buffer_alloc_start)
     455             :     {
     456        4188 :         const size_t elementSize = bufferDataType.GetSize();
     457        4188 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
     458        4188 :         const GByte *paby_buffer_alloc_start =
     459             :             static_cast<const GByte *>(buffer_alloc_start);
     460        4188 :         const GByte *paby_buffer_alloc_end =
     461             :             paby_buffer_alloc_start + buffer_alloc_size;
     462        4188 :         if (bufferStride_all_positive)
     463             :         {
     464        4188 :             if (paby_buffer < paby_buffer_alloc_start)
     465             :             {
     466           0 :                 lamda_error();
     467           0 :                 return false;
     468             :             }
     469        4188 :             GUInt64 nOffset = elementSize;
     470       12011 :             for (size_t i = 0; i < dims.size(); i++)
     471             :             {
     472             :                 try
     473             :                 {
     474        7823 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
     475        7823 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
     476       15646 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
     477       31292 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
     478        7823 :                                   .v();
     479             :                 }
     480           0 :                 catch (...)
     481             :                 {
     482           0 :                     lamda_error();
     483           0 :                     return false;
     484             :                 }
     485             :             }
     486             : #if SIZEOF_VOIDP == 4
     487             :             if (static_cast<size_t>(nOffset) != nOffset)
     488             :             {
     489             :                 lamda_error();
     490             :                 return false;
     491             :             }
     492             : #endif
     493        4188 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
     494             :             {
     495           0 :                 lamda_error();
     496           0 :                 return false;
     497             :             }
     498             :         }
     499           0 :         else if (dims.size() < 31)
     500             :         {
     501             :             // Check all corners of the hypercube
     502           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
     503           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
     504             :             {
     505           0 :                 const GByte *paby = paby_buffer;
     506           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
     507             :                      i++)
     508             :                 {
     509           0 :                     if (iCornerCode & (1U << i))
     510             :                     {
     511             :                         // We should check for integer overflows
     512           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
     513             :                     }
     514             :                 }
     515           0 :                 if (paby < paby_buffer_alloc_start ||
     516           0 :                     paby + elementSize > paby_buffer_alloc_end)
     517             :                 {
     518           0 :                     lamda_error();
     519           0 :                     return false;
     520             :                 }
     521             :             }
     522             :         }
     523             :     }
     524             : 
     525        8601 :     return true;
     526             : }
     527             : 
     528             : //! @endcond
     529             : 
     530             : /************************************************************************/
     531             : /*                                Read()                                */
     532             : /************************************************************************/
     533             : 
     534             : /** Read part or totality of a multidimensional array or attribute.
     535             :  *
     536             :  * This will extract the content of a hyper-rectangle from the array into
     537             :  * a user supplied buffer.
     538             :  *
     539             :  * If bufferDataType is of type string, the values written in pDstBuffer
     540             :  * will be char* pointers and the strings should be freed with CPLFree().
     541             :  *
     542             :  * This is the same as the C function GDALMDArrayRead().
     543             :  *
     544             :  * @param arrayStartIdx Values representing the starting index to read
     545             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
     546             :  *                      Array of GetDimensionCount() values. Must not be
     547             :  *                      nullptr, unless for a zero-dimensional array.
     548             :  *
     549             :  * @param count         Values representing the number of values to extract in
     550             :  *                      each dimension.
     551             :  *                      Array of GetDimensionCount() values. Must not be
     552             :  *                      nullptr, unless for a zero-dimensional array.
     553             :  *
     554             :  * @param arrayStep     Spacing between values to extract in each dimension.
     555             :  *                      The spacing is in number of array elements, not bytes.
     556             :  *                      If provided, must contain GetDimensionCount() values.
     557             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
     558             :  * default to indicate consecutive elements.
     559             :  *
     560             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
     561             :  *                      The spacing is in number of array elements, not bytes.
     562             :  *                      If provided, must contain GetDimensionCount() values.
     563             :  *                      Negative values are possible (for example to reorder
     564             :  *                      from bottom-to-top to top-to-bottom).
     565             :  *                      If set to nullptr, will be set so that pDstBuffer is
     566             :  *                      written in a compact way, with elements of the last /
     567             :  *                      fastest varying dimension being consecutive.
     568             :  *
     569             :  * @param bufferDataType Data type of values in pDstBuffer.
     570             :  *
     571             :  * @param pDstBuffer    User buffer to store the values read. Should be big
     572             :  *                      enough to store the number of values indicated by
     573             :  * count[] and with the spacing of bufferStride[].
     574             :  *
     575             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
     576             :  *                             validity of pDstBuffer. pDstBufferAllocStart
     577             :  * should be the pointer returned by the malloc() or equivalent call used to
     578             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
     579             :  * bufferStride[] values are all positive), but not necessarily. If specified,
     580             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
     581             :  * validation is needed, nullptr can be passed.
     582             :  *
     583             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
     584             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
     585             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
     586             :  *                             set to the appropriate value.
     587             :  *                             If no validation is needed, 0 can be passed.
     588             :  *
     589             :  * @return true in case of success.
     590             :  */
     591       12576 : bool GDALAbstractMDArray::Read(
     592             :     const GUInt64 *arrayStartIdx, const size_t *count,
     593             :     const GInt64 *arrayStep,         // step in elements
     594             :     const GPtrDiff_t *bufferStride,  // stride in elements
     595             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
     596             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
     597             : {
     598       12576 :     if (!GetDataType().CanConvertTo(bufferDataType))
     599             :     {
     600           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     601             :                  "Array data type is not convertible to buffer data type");
     602           0 :         return false;
     603             :     }
     604             : 
     605       25152 :     std::vector<GInt64> tmp_arrayStep;
     606       25152 :     std::vector<GPtrDiff_t> tmp_bufferStride;
     607       12576 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
     608             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
     609             :                               nDstBufferAllocSize, tmp_arrayStep,
     610             :                               tmp_bufferStride))
     611             :     {
     612           0 :         return false;
     613             :     }
     614             : 
     615       12576 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
     616       12576 :                  pDstBuffer);
     617             : }
     618             : 
     619             : /************************************************************************/
     620             : /*                               IWrite()                               */
     621             : /************************************************************************/
     622             : 
     623             : //! @cond Doxygen_Suppress
     624           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
     625             :                                  const GInt64 *, const GPtrDiff_t *,
     626             :                                  const GDALExtendedDataType &, const void *)
     627             : {
     628           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
     629           1 :     return false;
     630             : }
     631             : 
     632             : //! @endcond
     633             : 
     634             : /************************************************************************/
     635             : /*                               Write()                                */
     636             : /************************************************************************/
     637             : 
     638             : /** Write part or totality of a multidimensional array or attribute.
     639             :  *
     640             :  * This will set the content of a hyper-rectangle into the array from
     641             :  * a user supplied buffer.
     642             :  *
     643             :  * If bufferDataType is of type string, the values read from pSrcBuffer
     644             :  * will be char* pointers.
     645             :  *
     646             :  * This is the same as the C function GDALMDArrayWrite().
     647             :  *
     648             :  * @param arrayStartIdx Values representing the starting index to write
     649             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
     650             :  *                      Array of GetDimensionCount() values. Must not be
     651             :  *                      nullptr, unless for a zero-dimensional array.
     652             :  *
     653             :  * @param count         Values representing the number of values to write in
     654             :  *                      each dimension.
     655             :  *                      Array of GetDimensionCount() values. Must not be
     656             :  *                      nullptr, unless for a zero-dimensional array.
     657             :  *
     658             :  * @param arrayStep     Spacing between values to write in each dimension.
     659             :  *                      The spacing is in number of array elements, not bytes.
     660             :  *                      If provided, must contain GetDimensionCount() values.
     661             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
     662             :  * default to indicate consecutive elements.
     663             :  *
     664             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
     665             :  *                      The spacing is in number of array elements, not bytes.
     666             :  *                      If provided, must contain GetDimensionCount() values.
     667             :  *                      Negative values are possible (for example to reorder
     668             :  *                      from bottom-to-top to top-to-bottom).
     669             :  *                      If set to nullptr, will be set so that pSrcBuffer is
     670             :  *                      written in a compact way, with elements of the last /
     671             :  *                      fastest varying dimension being consecutive.
     672             :  *
     673             :  * @param bufferDataType Data type of values in pSrcBuffer.
     674             :  *
     675             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
     676             :  *                      enough to store the number of values indicated by
     677             :  * count[] and with the spacing of bufferStride[].
     678             :  *
     679             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
     680             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
     681             :  * should be the pointer returned by the malloc() or equivalent call used to
     682             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
     683             :  * bufferStride[] values are all positive), but not necessarily. If specified,
     684             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
     685             :  * validation is needed, nullptr can be passed.
     686             :  *
     687             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
     688             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
     689             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
     690             :  *                             set to the appropriate value.
     691             :  *                             If no validation is needed, 0 can be passed.
     692             :  *
     693             :  * @return true in case of success.
     694             :  */
     695        3285 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
     696             :                                 const size_t *count, const GInt64 *arrayStep,
     697             :                                 const GPtrDiff_t *bufferStride,
     698             :                                 const GDALExtendedDataType &bufferDataType,
     699             :                                 const void *pSrcBuffer,
     700             :                                 const void *pSrcBufferAllocStart,
     701             :                                 size_t nSrcBufferAllocSize)
     702             : {
     703        3285 :     if (!bufferDataType.CanConvertTo(GetDataType()))
     704             :     {
     705           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     706             :                  "Buffer data type is not convertible to array data type");
     707           0 :         return false;
     708             :     }
     709             : 
     710        6570 :     std::vector<GInt64> tmp_arrayStep;
     711        6570 :     std::vector<GPtrDiff_t> tmp_bufferStride;
     712        3285 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
     713             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
     714             :                               nSrcBufferAllocSize, tmp_arrayStep,
     715             :                               tmp_bufferStride))
     716             :     {
     717           0 :         return false;
     718             :     }
     719             : 
     720        3285 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
     721        3285 :                   pSrcBuffer);
     722             : }
     723             : 
     724             : /************************************************************************/
     725             : /*                       GetTotalElementsCount()                        */
     726             : /************************************************************************/
     727             : 
     728             : /** Return the total number of values in the array.
     729             :  *
     730             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
     731             :  * and GDALAttributeGetTotalElementsCount().
     732             :  *
     733             :  */
     734        1646 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
     735             : {
     736        1646 :     const auto &dims = GetDimensions();
     737        1646 :     if (dims.empty())
     738         861 :         return 1;
     739         785 :     GUInt64 nElts = 1;
     740        1708 :     for (const auto &dim : dims)
     741             :     {
     742             :         try
     743             :         {
     744         923 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
     745        2769 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
     746         923 :                         .v();
     747             :         }
     748           0 :         catch (...)
     749             :         {
     750           0 :             return 0;
     751             :         }
     752             :     }
     753         785 :     return nElts;
     754             : }
     755             : 
     756             : /************************************************************************/
     757             : /*                            GetBlockSize()                            */
     758             : /************************************************************************/
     759             : 
     760             : /** Return the "natural" block size of the array along all dimensions.
     761             :  *
     762             :  * Some drivers might organize the array in tiles/blocks and reading/writing
     763             :  * aligned on those tile/block boundaries will be more efficient.
     764             :  *
     765             :  * The returned number of elements in the vector is the same as
     766             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
     767             :  * the natural block size along the considered dimension.
     768             :  * "Flat" arrays will typically return a vector of values set to 0.
     769             :  *
     770             :  * The default implementation will return a vector of values set to 0.
     771             :  *
     772             :  * This method is used by GetProcessingChunkSize().
     773             :  *
     774             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikely
     775             :  * theoretical case of a 32-bit platform, this might exceed its size_t
     776             :  * allocation capabilities.
     777             :  *
     778             :  * This is the same as the C function GDALMDArrayGetBlockSize().
     779             :  *
     780             :  * @return the block size, in number of elements along each dimension.
     781             :  */
     782         316 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
     783             : {
     784         316 :     return std::vector<GUInt64>(GetDimensionCount());
     785             : }
     786             : 
     787             : /************************************************************************/
     788             : /*                       GetProcessingChunkSize()                       */
     789             : /************************************************************************/
     790             : 
     791             : /** \brief Return an optimal chunk size for read/write operations, given the
     792             :  * natural block size and memory constraints specified.
     793             :  *
     794             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
     795             :  * multiple of those returned by GetBlockSize() (unless the block define by
     796             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
     797             :  * returned by this method).
     798             :  *
     799             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
     800             :  *
     801             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
     802             :  * chunk.
     803             :  *
     804             :  * @return the chunk size, in number of elements along each dimension.
     805             :  */
     806             : std::vector<size_t>
     807          99 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
     808             : {
     809          99 :     const auto &dims = GetDimensions();
     810          99 :     const auto &nDTSize = GetDataType().GetSize();
     811          99 :     std::vector<size_t> anChunkSize;
     812         198 :     auto blockSize = GetBlockSize();
     813          99 :     CPLAssert(blockSize.size() == dims.size());
     814          99 :     size_t nChunkSize = nDTSize;
     815          99 :     bool bOverflow = false;
     816          99 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
     817             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
     818             :     // [1, min(sizet_max, dim_size[i])]
     819             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
     820         268 :     for (size_t i = 0; i < dims.size(); i++)
     821             :     {
     822             :         const auto sizeDimI =
     823         338 :             std::max(static_cast<size_t>(1),
     824         338 :                      static_cast<size_t>(
     825         338 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
     826         169 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
     827         169 :         anChunkSize.push_back(sizeDimI);
     828         169 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
     829             :         {
     830           4 :             bOverflow = true;
     831             :         }
     832             :         else
     833             :         {
     834         165 :             nChunkSize *= sizeDimI;
     835             :         }
     836             :     }
     837          99 :     if (nChunkSize == 0)
     838           0 :         return anChunkSize;
     839             : 
     840             :     // If the product of all anChunkSize[i] does not fit on size_t, then
     841             :     // set lowest anChunkSize[i] to 1.
     842          99 :     if (bOverflow)
     843             :     {
     844           2 :         nChunkSize = nDTSize;
     845           2 :         bOverflow = false;
     846           8 :         for (size_t i = dims.size(); i > 0;)
     847             :         {
     848           6 :             --i;
     849           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
     850             :             {
     851           4 :                 bOverflow = true;
     852           4 :                 anChunkSize[i] = 1;
     853             :             }
     854             :             else
     855             :             {
     856           2 :                 nChunkSize *= anChunkSize[i];
     857             :             }
     858             :         }
     859             :     }
     860             : 
     861          99 :     nChunkSize = nDTSize;
     862         198 :     std::vector<size_t> anAccBlockSizeFromStart;
     863         268 :     for (size_t i = 0; i < dims.size(); i++)
     864             :     {
     865         169 :         nChunkSize *= anChunkSize[i];
     866         169 :         anAccBlockSizeFromStart.push_back(nChunkSize);
     867             :     }
     868          99 :     if (nChunkSize <= nMaxChunkMemory / 2)
     869             :     {
     870          95 :         size_t nVoxelsFromEnd = 1;
     871         256 :         for (size_t i = dims.size(); i > 0;)
     872             :         {
     873         161 :             --i;
     874             :             const auto nCurBlockSize =
     875         161 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
     876         161 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
     877         161 :             if (nMul >= 2)
     878             :             {
     879         153 :                 const auto nSizeThisDim(dims[i]->GetSize());
     880             :                 const auto nBlocksThisDim =
     881         153 :                     cpl::div_round_up(nSizeThisDim, anChunkSize[i]);
     882         153 :                 anChunkSize[i] = static_cast<size_t>(std::min(
     883         153 :                     anChunkSize[i] *
     884         306 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
     885         153 :                     nSizeThisDim));
     886             :             }
     887         161 :             nVoxelsFromEnd *= anChunkSize[i];
     888             :         }
     889             :     }
     890          99 :     return anChunkSize;
     891             : }
     892             : 
     893             : /************************************************************************/
     894             : /*                             BaseRename()                             */
     895             : /************************************************************************/
     896             : 
     897             : //! @cond Doxygen_Suppress
     898          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
     899             : {
     900          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
     901          18 :     m_osFullName += osNewName;
     902          18 :     m_osName = osNewName;
     903             : 
     904          18 :     NotifyChildrenOfRenaming();
     905          18 : }
     906             : 
     907             : //! @endcond
     908             : 
     909             : //! @cond Doxygen_Suppress
     910             : /************************************************************************/
     911             : /*                           ParentRenamed()                            */
     912             : /************************************************************************/
     913             : 
     914          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
     915             : {
     916          50 :     m_osFullName = osNewParentFullName;
     917          50 :     m_osFullName += "/";
     918          50 :     m_osFullName += m_osName;
     919             : 
     920          50 :     NotifyChildrenOfRenaming();
     921          50 : }
     922             : 
     923             : //! @endcond
     924             : 
     925             : /************************************************************************/
     926             : /*                              Deleted()                               */
     927             : /************************************************************************/
     928             : 
     929             : //! @cond Doxygen_Suppress
     930          58 : void GDALAbstractMDArray::Deleted()
     931             : {
     932          58 :     m_bValid = false;
     933             : 
     934          58 :     NotifyChildrenOfDeletion();
     935          58 : }
     936             : 
     937             : //! @endcond
     938             : 
     939             : /************************************************************************/
     940             : /*                           ParentDeleted()                            */
     941             : /************************************************************************/
     942             : 
     943             : //! @cond Doxygen_Suppress
     944          30 : void GDALAbstractMDArray::ParentDeleted()
     945             : {
     946          30 :     Deleted();
     947          30 : }
     948             : 
     949             : //! @endcond
     950             : 
     951             : /************************************************************************/
     952             : /*                     CheckValidAndErrorOutIfNot()                     */
     953             : /************************************************************************/
     954             : 
     955             : //! @cond Doxygen_Suppress
     956       10206 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
     957             : {
     958       10206 :     if (!m_bValid)
     959             :     {
     960          26 :         CPLError(CE_Failure, CPLE_AppDefined,
     961             :                  "This object has been deleted. No action on it is possible");
     962             :     }
     963       10206 :     return m_bValid;
     964             : }
     965             : 
     966             : //! @endcond
     967             : 
     968             : /************************************************************************/
     969             : /*                          ProcessPerChunk()                           */
     970             : /************************************************************************/
     971             : 
     972             : namespace
     973             : {
     974             : enum class Caller
     975             : {
     976             :     CALLER_END_OF_LOOP,
     977             :     CALLER_IN_LOOP,
     978             : };
     979             : }
     980             : 
     981             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
     982             :  *
     983             :  * This method is to be used when doing operations on an array, or a subset of
     984             :  * it, in a chunk by chunk way.
     985             :  *
     986             :  * @param arrayStartIdx Values representing the starting index to use
     987             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
     988             :  *                      Array of GetDimensionCount() values. Must not be
     989             :  *                      nullptr, unless for a zero-dimensional array.
     990             :  *
     991             :  * @param count         Values representing the number of values to use in
     992             :  *                      each dimension.
     993             :  *                      Array of GetDimensionCount() values. Must not be
     994             :  *                      nullptr, unless for a zero-dimensional array.
     995             :  *
     996             :  * @param chunkSize     Values representing the chunk size in each dimension.
     997             :  *                      Might typically the output of GetProcessingChunkSize().
     998             :  *                      Array of GetDimensionCount() values. Must not be
     999             :  *                      nullptr, unless for a zero-dimensional array.
    1000             :  *
    1001             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    1002             :  *                      Must NOT be nullptr.
    1003             :  *
    1004             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    1005             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    1006             :  *
    1007             :  * @return true in case of success.
    1008             :  */
    1009          97 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    1010             :                                           const GUInt64 *count,
    1011             :                                           const size_t *chunkSize,
    1012             :                                           FuncProcessPerChunkType pfnFunc,
    1013             :                                           void *pUserData)
    1014             : {
    1015          97 :     const auto &dims = GetDimensions();
    1016          97 :     if (dims.empty())
    1017             :     {
    1018           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    1019             :     }
    1020             : 
    1021             :     // Sanity check
    1022          95 :     size_t nTotalChunkSize = 1;
    1023         241 :     for (size_t i = 0; i < dims.size(); i++)
    1024             :     {
    1025         153 :         const auto nSizeThisDim(dims[i]->GetSize());
    1026         153 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    1027         151 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    1028             :         {
    1029           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    1030             :                      "Inconsistent arrayStartIdx[] / count[] values "
    1031             :                      "regarding array size");
    1032           4 :             return false;
    1033             :         }
    1034         296 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    1035         147 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    1036             :         {
    1037           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    1038             :                      "Inconsistent chunkSize[] values");
    1039           3 :             return false;
    1040             :         }
    1041         146 :         nTotalChunkSize *= chunkSize[i];
    1042             :     }
    1043             : 
    1044          88 :     size_t dimIdx = 0;
    1045         176 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    1046         176 :     std::vector<size_t> chunkCount(dims.size());
    1047             : 
    1048             :     struct Stack
    1049             :     {
    1050             :         GUInt64 nBlockCounter = 0;
    1051             :         GUInt64 nBlocksMinusOne = 0;
    1052             :         size_t first_count = 0;  // only used if nBlocks > 1
    1053             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    1054             :     };
    1055             : 
    1056         176 :     std::vector<Stack> stack(dims.size());
    1057          88 :     GUInt64 iCurChunk = 0;
    1058          88 :     GUInt64 nChunkCount = 1;
    1059         233 :     for (size_t i = 0; i < dims.size(); i++)
    1060             :     {
    1061         145 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    1062         145 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    1063         145 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    1064         145 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    1065         145 :         if (stack[i].nBlocksMinusOne == 0)
    1066             :         {
    1067         140 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    1068         140 :             chunkCount[i] = static_cast<size_t>(count[i]);
    1069             :         }
    1070             :         else
    1071             :         {
    1072           5 :             stack[i].first_count = static_cast<size_t>(
    1073           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    1074             :         }
    1075             :     }
    1076             : 
    1077          88 : lbl_next_depth:
    1078         343 :     if (dimIdx == dims.size())
    1079             :     {
    1080         121 :         ++iCurChunk;
    1081         121 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    1082             :                      iCurChunk, nChunkCount, pUserData))
    1083             :         {
    1084           3 :             return false;
    1085             :         }
    1086             :     }
    1087             :     else
    1088             :     {
    1089         222 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    1090             :         {
    1091          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    1092          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    1093          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    1094          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    1095             :             while (true)
    1096             :             {
    1097          33 :                 dimIdx++;
    1098          33 :                 goto lbl_next_depth;
    1099          33 :             lbl_return_to_caller_in_loop:
    1100          33 :                 --stack[dimIdx].nBlockCounter;
    1101          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    1102          11 :                     break;
    1103          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    1104          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    1105             :             }
    1106             : 
    1107          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    1108          22 :             chunkCount[dimIdx] =
    1109          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    1110          11 :                                     chunkArrayStartIdx[dimIdx]);
    1111          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    1112             :         }
    1113         222 :         dimIdx++;
    1114         222 :         goto lbl_next_depth;
    1115         216 :     lbl_return_to_caller_end_of_loop:
    1116         216 :         if (dimIdx == 0)
    1117          85 :             goto end;
    1118             :     }
    1119             : 
    1120         249 :     assert(dimIdx > 0);
    1121         249 :     dimIdx--;
    1122             :     // cppcheck-suppress negativeContainerIndex
    1123         249 :     switch (stack[dimIdx].return_point)
    1124             :     {
    1125         216 :         case Caller::CALLER_END_OF_LOOP:
    1126         216 :             goto lbl_return_to_caller_end_of_loop;
    1127          33 :         case Caller::CALLER_IN_LOOP:
    1128          33 :             goto lbl_return_to_caller_in_loop;
    1129             :     }
    1130          85 : end:
    1131          85 :     return true;
    1132             : }

Generated by: LCOV version 1.14