1 /******************************************************************************
2  * $Id: gdalmultidim.cpp 5f249867c4bbe5a64ddcbc483c76b612119d85d4 2021-04-29 11:57:20 +0200 Even Rouault $
3  *
4  * Name:     gdalmultidim.cpp
5  * Project:  GDAL Core
6  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
7  * Author:   Even Rouault <even.rouault at spatialys.com>
8  *
9  ******************************************************************************
10  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include <assert.h>
32 #include <algorithm>
33 #include <queue>
34 #include <set>
35 
36 #include "gdal_priv.h"
37 #include "gdal_pam.h"
38 #include "cpl_safemaths.hpp"
39 
40 #if defined(__clang__) || defined(_MSC_VER)
41 #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
42 #endif
43 
44 /************************************************************************/
45 /*                       GDALMDArrayUnscaled                            */
46 /************************************************************************/
47 
48 class GDALMDArrayUnscaled final: public GDALMDArray
49 {
50 private:
51     std::shared_ptr<GDALMDArray> m_poParent{};
52     GDALExtendedDataType m_dt;
53     bool m_bHasNoData;
54     double m_adfNoData[2]{ std::numeric_limits<double>::quiet_NaN(),
55                            std::numeric_limits<double>::quiet_NaN() };
56 
57 protected:
GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> & poParent)58     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray>& poParent):
59         GDALAbstractMDArray(std::string(), "Unscaled view of " + poParent->GetFullName()),
60         GDALMDArray(std::string(), "Unscaled view of " + poParent->GetFullName()),
61         m_poParent(std::move(poParent)),
62         m_dt(GDALExtendedDataType::Create(GDALDataTypeIsComplex(
63             m_poParent->GetDataType().GetNumericDataType()) ?
64                 GDT_CFloat64 : GDT_Float64)),
65         m_bHasNoData( m_poParent->GetRawNoDataValue() != nullptr )
66     {
67     }
68 
69     bool IRead(const GUInt64* arrayStartIdx,
70                       const size_t* count,
71                       const GInt64* arrayStep,
72                       const GPtrDiff_t* bufferStride,
73                       const GDALExtendedDataType& bufferDataType,
74                       void* pDstBuffer) const override;
75 
76     bool IWrite(const GUInt64* arrayStartIdx,
77                       const size_t* count,
78                       const GInt64* arrayStep,
79                       const GPtrDiff_t* bufferStride,
80                       const GDALExtendedDataType& bufferDataType,
81                       const void* pSrcBuffer) override;
82 
IAdviseRead(const GUInt64 * arrayStartIdx,const size_t * count) const83     bool IAdviseRead(const GUInt64* arrayStartIdx,
84                      const size_t* count) const override
85         { return m_poParent->AdviseRead(arrayStartIdx, count); }
86 
87 public:
Create(const std::shared_ptr<GDALMDArray> & poParent)88     static std::shared_ptr<GDALMDArrayUnscaled> Create(
89                     const std::shared_ptr<GDALMDArray>& poParent)
90     {
91         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
92             poParent)));
93         newAr->SetSelf(newAr);
94         return newAr;
95     }
96 
IsWritable() const97     bool IsWritable() const override { return m_poParent->IsWritable(); }
98 
GetDimensions() const99     const std::vector<std::shared_ptr<GDALDimension>>& GetDimensions() const override { return m_poParent->GetDimensions(); }
100 
GetDataType() const101     const GDALExtendedDataType &GetDataType() const override { return m_dt; }
102 
GetUnit() const103     const std::string& GetUnit() const override { return m_poParent->GetUnit(); }
104 
GetSpatialRef() const105     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override { return m_poParent->GetSpatialRef(); }
106 
GetRawNoDataValue() const107     const void* GetRawNoDataValue() const override { return m_bHasNoData ? &m_adfNoData[0] : nullptr; }
108 
SetRawNoDataValue(const void * pRawNoData)109     bool SetRawNoDataValue(const void* pRawNoData) override {
110         m_bHasNoData = true; memcpy(m_adfNoData, pRawNoData, m_dt.GetSize()); return true; }
111 
GetBlockSize() const112     std::vector<GUInt64> GetBlockSize() const override { return m_poParent->GetBlockSize(); }
113 
GetAttribute(const std::string & osName) const114     std::shared_ptr<GDALAttribute> GetAttribute(const std::string& osName) const override
115         { return m_poParent->GetAttribute(osName); }
116 
GetAttributes(CSLConstList papszOptions=nullptr) const117     std::vector<std::shared_ptr<GDALAttribute>> GetAttributes(CSLConstList papszOptions = nullptr) const override
118         { return m_poParent->GetAttributes(papszOptions); }
119 
SetUnit(const std::string & osUnit)120     bool SetUnit(const std::string& osUnit) override { return m_poParent->SetUnit(osUnit); }
121 
SetSpatialRef(const OGRSpatialReference * poSRS)122     bool SetSpatialRef(const OGRSpatialReference* poSRS) override { return m_poParent->SetSpatialRef(poSRS); }
123 
CreateAttribute(const std::string & osName,const std::vector<GUInt64> & anDimensions,const GDALExtendedDataType & oDataType,CSLConstList papszOptions=nullptr)124     std::shared_ptr<GDALAttribute> CreateAttribute(
125         const std::string& osName,
126         const std::vector<GUInt64>& anDimensions,
127         const GDALExtendedDataType& oDataType,
128         CSLConstList papszOptions = nullptr) override {return m_poParent->CreateAttribute(osName, anDimensions, oDataType, papszOptions); }
129 };
130 
131 /************************************************************************/
132 /*                         ~GDALIHasAttribute()                         */
133 /************************************************************************/
134 
135 GDALIHasAttribute::~GDALIHasAttribute() = default;
136 
137 /************************************************************************/
138 /*                            GetAttribute()                            */
139 /************************************************************************/
140 
141 /** Return an attribute by its name.
142  *
143  * If the attribute does not exist, nullptr should be silently returned.
144  *
145  * @note Driver implementation: this method will fallback to
146  * GetAttributeFromAttributes() is not explicitly implemented
147  *
148  * Drivers known to implement it for groups and arrays: MEM, netCDF.
149  *
150  * This is the same as the C function GDALGroupGetAttribute() or
151  * GDALMDArrayGetAttribute().
152  *
153  * @param osName Attribute name
154  * @return the attribute, or nullptr if it does not exist or an error occurred.
155  */
GetAttribute(const std::string & osName) const156 std::shared_ptr<GDALAttribute> GDALIHasAttribute::GetAttribute(
157                                 const std::string& osName) const
158 {
159     return GetAttributeFromAttributes(osName);
160 }
161 
162 /************************************************************************/
163 /*                       GetAttributeFromAttributes()                   */
164 /************************************************************************/
165 
166 /** Possible fallback implementation for GetAttribute() using GetAttributes().
167  */
GetAttributeFromAttributes(const std::string & osName) const168 std::shared_ptr<GDALAttribute> GDALIHasAttribute::GetAttributeFromAttributes(
169     const std::string& osName) const
170 {
171     auto attrs(GetAttributes());
172     for( const auto& attr: attrs )
173     {
174         if( attr->GetName() == osName )
175             return attr;
176     }
177     return nullptr;
178 }
179 
180 
181 /************************************************************************/
182 /*                           GetAttributes()                            */
183 /************************************************************************/
184 
185 /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
186  *
187  * If the attribute does not exist, nullptr should be silently returned.
188  *
189  * @note Driver implementation: optionally implemented. If implemented,
190  * GetAttribute() should also be implemented.
191  *
192  * Drivers known to implement it for groups and arrays: MEM, netCDF.
193  *
194  * This is the same as the C function GDALGroupGetAttributes() or
195  * GDALMDArrayGetAttributes().
196 
197  * @param papszOptions Driver specific options determining how attributes
198  * should be retrieved. Pass nullptr for default behavior.
199  *
200  * @return the attributes.
201  */
202 std::vector<std::shared_ptr<GDALAttribute>>
GetAttributes(CPL_UNUSED CSLConstList papszOptions) const203 GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
204 {
205     return {};
206 }
207 
208 /************************************************************************/
209 /*                             CreateAttribute()                         */
210 /************************************************************************/
211 
212 /** Create an attribute within a GDALMDArray or GDALGroup.
213  *
214  * The attribute might not be "physically" created until a value is written
215  * into it.
216  *
217  * Optionally implemented.
218  *
219  * Drivers known to implement it: MEM, netCDF
220  *
221  * This is the same as the C function GDALGroupCreateAttribute() or
222  * GDALMDArrayCreateAttribute()
223  *
224  * @param osName Attribute name.
225  * @param anDimensions List of dimension sizes, ordered from the slowest varying
226  *                     dimension first to the fastest varying dimension last.
227  *                     Empty for a scalar attribute (common case)
228  * @param oDataType  Attribute data type.
229  * @param papszOptions Driver specific options determining how the attribute.
230  * should be created.
231  *
232  * @return the new attribute, or nullptr if case of error
233  */
CreateAttribute(CPL_UNUSED const std::string & osName,CPL_UNUSED const std::vector<GUInt64> & anDimensions,CPL_UNUSED const GDALExtendedDataType & oDataType,CPL_UNUSED CSLConstList papszOptions)234 std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
235                                 CPL_UNUSED const std::string& osName,
236                                 CPL_UNUSED const std::vector<GUInt64>& anDimensions,
237                                 CPL_UNUSED const GDALExtendedDataType& oDataType,
238                                 CPL_UNUSED CSLConstList papszOptions)
239 {
240     CPLError(CE_Failure, CPLE_NotSupported, "CreateAttribute() not implemented");
241     return nullptr;
242 }
243 
244 /************************************************************************/
245 /*                            GDALGroup()                               */
246 /************************************************************************/
247 
248 //! @cond Doxygen_Suppress
GDALGroup(const std::string & osParentName,const std::string & osName)249 GDALGroup::GDALGroup(const std::string& osParentName, const std::string& osName):
250     m_osName(osParentName.empty() ? "/" : osName),
251     m_osFullName(!osParentName.empty() ? ((osParentName == "/" ? "/" : osParentName + "/") + osName) : "/")
252 {}
253 //! @endcond
254 
255 /************************************************************************/
256 /*                            ~GDALGroup()                              */
257 /************************************************************************/
258 
259 GDALGroup::~GDALGroup() = default;
260 
261 /************************************************************************/
262 /*                          GetMDArrayNames()                           */
263 /************************************************************************/
264 
265 /** Return the list of multidimensional array names contained in this group.
266  *
267  * @note Driver implementation: optionally implemented. If implemented,
268  * OpenMDArray() should also be implemented.
269  *
270  * Drivers known to implement it: MEM, netCDF.
271  *
272  * This is the same as the C function GDALGroupGetMDArrayNames().
273  *
274  * @param papszOptions Driver specific options determining how arrays
275  * should be retrieved. Pass nullptr for default behavior.
276  *
277  * @return the array names.
278  */
279 std::vector<std::string>
GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const280 GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
281 {
282     return {};
283 }
284 
285 /************************************************************************/
286 /*                            OpenMDArray()                             */
287 /************************************************************************/
288 
289 /** Open and return a multidimensional array.
290  *
291  * @note Driver implementation: optionally implemented. If implemented,
292  * GetMDArrayNames() should also be implemented.
293  *
294  * Drivers known to implement it: MEM, netCDF.
295  *
296  * This is the same as the C function GDALGroupOpenMDArray().
297  *
298  * @param osName Array name.
299  * @param papszOptions Driver specific options determining how the array should
300  * be opened.  Pass nullptr for default behavior.
301  *
302  * @return the array, or nullptr.
303  */
OpenMDArray(CPL_UNUSED const std::string & osName,CPL_UNUSED CSLConstList papszOptions) const304 std::shared_ptr<GDALMDArray> GDALGroup::OpenMDArray(CPL_UNUSED const std::string& osName,
305                                                     CPL_UNUSED CSLConstList papszOptions) const
306 {
307     return nullptr;
308 }
309 
310 /************************************************************************/
311 /*                           GetGroupNames()                            */
312 /************************************************************************/
313 
314 /** Return the list of sub-groups contained in this group.
315  *
316  * @note Driver implementation: optionally implemented. If implemented,
317  * OpenGroup() should also be implemented.
318  *
319  * Drivers known to implement it: MEM, netCDF.
320  *
321  * This is the same as the C function GDALGroupGetGroupNames().
322  *
323  * @param papszOptions Driver specific options determining how groups
324  * should be retrieved. Pass nullptr for default behavior.
325  *
326  * @return the group names.
327  */
GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const328 std::vector<std::string> GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
329 {
330     return {};
331 }
332 
333 /************************************************************************/
334 /*                             OpenGroup()                              */
335 /************************************************************************/
336 
337 /** Open and return a sub-group.
338  *
339  * @note Driver implementation: optionally implemented. If implemented,
340  * GetGroupNames() should also be implemented.
341  *
342  * Drivers known to implement it: MEM, netCDF.
343  *
344  * This is the same as the C function GDALGroupOpenGroup().
345  *
346  * @param osName Sub-group name.
347  * @param papszOptions Driver specific options determining how the sub-group should
348  * be opened.  Pass nullptr for default behavior.
349  *
350  * @return the group, or nullptr.
351  */
OpenGroup(CPL_UNUSED const std::string & osName,CPL_UNUSED CSLConstList papszOptions) const352 std::shared_ptr<GDALGroup> GDALGroup::OpenGroup(CPL_UNUSED const std::string& osName,
353                                                 CPL_UNUSED CSLConstList papszOptions) const
354 {
355     return nullptr;
356 }
357 
358 /************************************************************************/
359 /*                             GetDimensions()                          */
360 /************************************************************************/
361 
362 /** Return the list of dimensions contained in this group and used by its
363  * arrays.
364  *
365  * This is for dimensions that can potentially be used by several arrays.
366  * Not all drivers might implement this. To retrieve the dimensions used by
367  * a specific array, use GDALMDArray::GetDimensions().
368  *
369  * Drivers known to implement it: MEM, netCDF
370  *
371  * This is the same as the C function GDALGroupGetDimensions().
372  *
373  * @param papszOptions Driver specific options determining how groups
374  * should be retrieved. Pass nullptr for default behavior.
375  *
376  * @return the dimensions.
377  */
GetDimensions(CPL_UNUSED CSLConstList papszOptions) const378 std::vector<std::shared_ptr<GDALDimension>> GDALGroup::GetDimensions(
379                                     CPL_UNUSED CSLConstList papszOptions) const
380 {
381     return {};
382 }
383 
384 /************************************************************************/
385 /*                         GetStructuralInfo()                          */
386 /************************************************************************/
387 
388 /** Return structural information on the group.
389  *
390  * This may be the compression, etc..
391  *
392  * The return value should not be freed and is valid until GDALGroup is
393  * released or this function called again.
394  *
395  * This is the same as the C function GDALGroupGetStruturalInfo().
396  */
GetStructuralInfo() const397 CSLConstList GDALGroup::GetStructuralInfo() const
398 {
399     return nullptr;
400 }
401 
402 /************************************************************************/
403 /*                              CreateGroup()                           */
404 /************************************************************************/
405 
406 /** Create a sub-group within a group.
407  *
408  * Optionally implemented by drivers.
409  *
410  * Drivers known to implement it: MEM, netCDF
411  *
412  * This is the same as the C function GDALGroupCreateGroup().
413  *
414  * @param osName Sub-group name.
415  * @param papszOptions Driver specific options determining how the sub-group
416  * should be created.
417  *
418  * @return the new sub-group, or nullptr in case of error.
419  */
CreateGroup(CPL_UNUSED const std::string & osName,CPL_UNUSED CSLConstList papszOptions)420 std::shared_ptr<GDALGroup> GDALGroup::CreateGroup(CPL_UNUSED const std::string& osName,
421                                                   CPL_UNUSED CSLConstList papszOptions)
422 {
423     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
424     return nullptr;
425 }
426 
427 /************************************************************************/
428 /*                            CreateDimension()                         */
429 /************************************************************************/
430 
431 /** Create a dimension within a group.
432  *
433  * @note Driver implementation: drivers supporting CreateDimension() should implement
434  * this method, but do not have necessarily to implement GDALGroup::GetDimensions().
435  *
436  * Drivers known to implement it: MEM, netCDF
437  *
438  * This is the same as the C function GDALGroupCreateDimension().
439  *
440  * @param osName Dimension name.
441  * @param osType Dimension type (might be empty, and ignored by drivers)
442  * @param osDirection Dimension direction (might be empty, and ignored by drivers)
443  * @param nSize  Number of values indexed by this dimension. Should be > 0.
444  * @param papszOptions Driver specific options determining how the dimension
445  * should be created.
446  *
447  * @return the new dimension, or nullptr if case of error
448  */
CreateDimension(CPL_UNUSED const std::string & osName,CPL_UNUSED const std::string & osType,CPL_UNUSED const std::string & osDirection,CPL_UNUSED GUInt64 nSize,CPL_UNUSED CSLConstList papszOptions)449 std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(CPL_UNUSED const std::string& osName,
450                                                           CPL_UNUSED const std::string& osType,
451                                                           CPL_UNUSED const std::string& osDirection,
452                                                           CPL_UNUSED GUInt64 nSize,
453                                                           CPL_UNUSED CSLConstList papszOptions)
454 {
455     CPLError(CE_Failure, CPLE_NotSupported, "CreateDimension() not implemented");
456     return nullptr;
457 }
458 
459 /************************************************************************/
460 /*                             CreateMDArray()                          */
461 /************************************************************************/
462 
463 /** Create a multidimensional array within a group.
464  *
465  * It is recommended that the GDALDimension objects passed in aoDimensions
466  * belong to this group, either by retrieving them with GetDimensions()
467  * or creating a new one with CreateDimension().
468  *
469  * Optionally implemented.
470  *
471  * Drivers known to implement it: MEM, netCDF
472  *
473  * This is the same as the C function GDALGroupCreateMDArray().
474  *
475  * @note Driver implementation: drivers should take into account the possibility
476  * that GDALDimension object passed in aoDimensions might belong to a different
477  * group / dataset / driver and act accordingly.
478  *
479  * @param osName Array name.
480  * @param aoDimensions List of dimensions, ordered from the slowest varying
481  *                     dimension first to the fastest varying dimension last.
482  *                     Might be empty for a scalar array (if supported by driver)
483  * @param oDataType  Array data type.
484  * @param papszOptions Driver specific options determining how the array
485  * should be created.
486  *
487  * @return the new array, or nullptr if case of error
488  */
CreateMDArray(CPL_UNUSED const std::string & osName,CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> & aoDimensions,CPL_UNUSED const GDALExtendedDataType & oDataType,CPL_UNUSED CSLConstList papszOptions)489 std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
490     CPL_UNUSED const std::string& osName,
491     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>>& aoDimensions,
492     CPL_UNUSED const GDALExtendedDataType& oDataType,
493     CPL_UNUSED CSLConstList papszOptions)
494 {
495     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
496     return nullptr;
497 }
498 
499 /************************************************************************/
500 /*                           GetTotalCopyCost()                         */
501 /************************************************************************/
502 
503 /** Return a total "cost" to copy the group.
504  *
505  * Used as a parameter for CopFrom()
506  */
GetTotalCopyCost() const507 GUInt64 GDALGroup::GetTotalCopyCost() const
508 {
509     GUInt64 nCost = COPY_COST;
510     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
511 
512     auto groupNames = GetGroupNames();
513     for( const auto& name: groupNames )
514     {
515         auto subGroup = OpenGroup(name);
516         if( subGroup )
517         {
518             nCost += subGroup->GetTotalCopyCost();
519         }
520     }
521 
522     auto arrayNames = GetMDArrayNames();
523     for( const auto& name: arrayNames )
524     {
525         auto array = OpenMDArray(name);
526         if( array )
527         {
528             nCost += array->GetTotalCopyCost();
529         }
530     }
531     return nCost;
532 }
533 
534 /************************************************************************/
535 /*                               CopyFrom()                             */
536 /************************************************************************/
537 
538 /** Copy the content of a group into a new (generally empty) group.
539  *
540  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
541  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
542  *                   of some output drivers this is not recommended)
543  * @param poSrcGroup Source group. Must NOT be nullptr.
544  * @param bStrict Whether to enable stict mode. In strict mode, any error will
545  *                stop the copy. In relaxed mode, the copy will be attempted to
546  *                be pursued.
547  * @param nCurCost  Should be provided as a variable initially set to 0.
548  * @param nTotalCost Total cost from GetTotalCopyCost().
549  * @param pfnProgress Progress callback, or nullptr.
550  * @param pProgressData Progress user data, or nulptr.
551  * @param papszOptions Creation options. Currently, only array creation
552  *                     options are supported. They must be prefixed with "ARRAY:" .
553  *                     The scope may be further restricted to arrays of a certain
554  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
555  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
556  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
557  *                     Restriction to arrays of a given name is done with adding
558  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
559  *                     a full qualified name.
560  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can be
561  *                     used to ask (non indexing) variables of type Float32 or Float64
562  *                     to be scaled to UInt16 with scale and offset values
563  *                     being computed from the minimum and maximum of the source array.
564  *                     The integer data type used can be set with
565  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
566  *
567  * @return true in case of success (or partial success if bStrict == false).
568  */
CopyFrom(const std::shared_ptr<GDALGroup> & poDstRootGroup,GDALDataset * poSrcDS,const std::shared_ptr<GDALGroup> & poSrcGroup,bool bStrict,GUInt64 & nCurCost,const GUInt64 nTotalCost,GDALProgressFunc pfnProgress,void * pProgressData,CSLConstList papszOptions)569 bool GDALGroup::CopyFrom( const std::shared_ptr<GDALGroup>& poDstRootGroup,
570                           GDALDataset* poSrcDS,
571                           const std::shared_ptr<GDALGroup>& poSrcGroup,
572                           bool bStrict,
573                           GUInt64& nCurCost,
574                           const GUInt64 nTotalCost,
575                           GDALProgressFunc pfnProgress,
576                           void * pProgressData,
577                           CSLConstList papszOptions )
578 {
579     if( pfnProgress == nullptr )
580         pfnProgress = GDALDummyProgress;
581 
582 #define EXIT_OR_CONTINUE_IF_NULL(x) \
583             if( !(x) ) \
584             { \
585                 if( bStrict ) \
586                     return false; \
587                 continue; \
588             } \
589             (void)0
590 
591     try
592     {
593         nCurCost += GDALGroup::COPY_COST;
594 
595         const auto srcDims = poSrcGroup->GetDimensions();
596         std::map<std::string, std::shared_ptr<GDALDimension>> mapExistingDstDims;
597         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
598         for( const auto& dim: srcDims )
599         {
600             auto dstDim = CreateDimension(dim->GetName(),
601                                                       dim->GetType(),
602                                                       dim->GetDirection(),
603                                                       dim->GetSize());
604             EXIT_OR_CONTINUE_IF_NULL(dstDim);
605             mapExistingDstDims[dim->GetName()] = dstDim;
606             auto poIndexingVarSrc(dim->GetIndexingVariable());
607             if( poIndexingVarSrc )
608             {
609                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc->GetName()] = dim->GetName();
610             }
611         }
612 
613         auto attrs = poSrcGroup->GetAttributes();
614         for( const auto& attr: attrs )
615         {
616             auto dstAttr = CreateAttribute(attr->GetName(),
617                                         attr->GetDimensionsSize(),
618                                         attr->GetDataType());
619             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
620             auto raw(attr->ReadAsRaw());
621             if( !dstAttr->Write(raw.data(), raw.size()) && bStrict )
622                 return false;
623         }
624         if( !attrs.empty() )
625         {
626             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
627             if( !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData) )
628                 return false;
629         }
630 
631         auto arrayNames = poSrcGroup->GetMDArrayNames();
632         for( const auto& name: arrayNames )
633         {
634             auto srcArray = poSrcGroup->OpenMDArray(name);
635             EXIT_OR_CONTINUE_IF_NULL(srcArray);
636 
637             // Map source dimensions to target dimensions
638             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
639             const auto& srcArrayDims(srcArray->GetDimensions());
640             for( const auto& dim : srcArrayDims )
641             {
642                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
643                                                         dim->GetFullName());
644                 if( dstDim && dstDim->GetSize() == dim->GetSize() )
645                 {
646                     dstArrayDims.emplace_back(dstDim);
647                 }
648                 else
649                 {
650                     auto oIter = mapExistingDstDims.find(dim->GetName());
651                     if( oIter != mapExistingDstDims.end() &&
652                         oIter->second->GetSize() == dim->GetSize() )
653                     {
654                         dstArrayDims.emplace_back(oIter->second);
655                     }
656                     else
657                     {
658                         std::string newDimName;
659                         if( oIter == mapExistingDstDims.end() )
660                         {
661                             newDimName = dim->GetName();
662                         }
663                         else
664                         {
665                             std::string newDimNamePrefix(name + '_' + dim->GetName());
666                             newDimName = newDimNamePrefix;
667                             int nIterCount = 2;
668                             while( mapExistingDstDims.find(newDimName) != mapExistingDstDims.end() )
669                             {
670                                 newDimName = newDimNamePrefix + CPLSPrintf("_%d", nIterCount);
671                                 nIterCount ++;
672                             }
673                         }
674                         dstDim = CreateDimension(newDimName,
675                                                         dim->GetType(),
676                                                         dim->GetDirection(),
677                                                         dim->GetSize());
678                         if( !dstDim )
679                             return false;
680                         mapExistingDstDims[newDimName] = dstDim;
681                         dstArrayDims.emplace_back(dstDim);
682                     }
683                 }
684             }
685 
686             CPLStringList aosArrayCO;
687             bool bAutoScale = false;
688             GDALDataType eAutoScaleType = GDT_UInt16;
689             for( CSLConstList papszIter = papszOptions;
690                                         papszIter && *papszIter; ++papszIter )
691             {
692                 if( STARTS_WITH_CI(*papszIter, "ARRAY:") )
693                 {
694                     const char* pszOption = *papszIter + strlen("ARRAY:");
695                     if( STARTS_WITH_CI(pszOption, "IF(DIM=") )
696                     {
697                         const char* pszNext = strchr(pszOption, ':');
698                         if( pszNext != nullptr )
699                         {
700                             int nDim = atoi(pszOption + strlen("IF(DIM="));
701                             if( static_cast<size_t>(nDim) == dstArrayDims.size() )
702                             {
703                                 pszOption = pszNext + 1;
704                             }
705                             else
706                             {
707                                 pszOption = nullptr;
708                             }
709                         }
710                     }
711                     else if( STARTS_WITH_CI(pszOption, "IF(NAME=") )
712                     {
713                         const char* pszName = pszOption + strlen("IF(NAME=");
714                         const char* pszNext = strchr(pszName, ':');
715                         if( pszNext != nullptr && pszNext > pszName &&
716                             pszNext[-1] == ')' )
717                         {
718                             CPLString osName;
719                             osName.assign(pszName, pszNext - pszName - 1);
720                             if( osName == srcArray->GetName() ||
721                                 osName == srcArray->GetFullName() )
722                             {
723                                 pszOption = pszNext + 1;
724                             }
725                             else
726                             {
727                                 pszOption = nullptr;
728                             }
729                         }
730                     }
731                     if( pszOption )
732                     {
733                         if( STARTS_WITH_CI(pszOption, "AUTOSCALE=") )
734                         {
735                             bAutoScale = CPLTestBool(pszOption + strlen("AUTOSCALE="));
736                         }
737                         else if( STARTS_WITH_CI(pszOption, "AUTOSCALE_DATA_TYPE=") )
738                         {
739                             const char* pszDataType = pszOption + strlen("AUTOSCALE_DATA_TYPE=");
740                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
741                             if( eAutoScaleType != GDT_Byte &&
742                                 eAutoScaleType != GDT_UInt16 &&
743                                 eAutoScaleType != GDT_Int16 &&
744                                 eAutoScaleType != GDT_UInt32 &&
745                                 eAutoScaleType != GDT_Int32 )
746                             {
747                                 CPLError(CE_Failure, CPLE_NotSupported,
748                                          "Unsupported value for AUTOSCALE_DATA_TYPE");
749                                 return false;
750                             }
751                         }
752                         else
753                         {
754                             aosArrayCO.AddString(pszOption);
755                         }
756                     }
757                 }
758             }
759 
760             auto oIterDimName = mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
761             const auto& srcArrayType = srcArray->GetDataType();
762 
763             std::shared_ptr<GDALMDArray> dstArray;
764 
765             // Only autoscale non-indexing variables
766             bool bHasOffset = false;
767             bool bHasScale = false;
768             if( bAutoScale &&
769                 srcArrayType.GetClass() == GEDTC_NUMERIC &&
770                 (srcArrayType.GetNumericDataType() == GDT_Float32 ||
771                  srcArrayType.GetNumericDataType() == GDT_Float64 ) &&
772                 srcArray->GetOffset(&bHasOffset) == 0.0 &&
773                 !bHasOffset &&
774                 srcArray->GetScale(&bHasScale) == 1.0 &&
775                 !bHasScale &&
776                 oIterDimName == mapSrcVariableNameToIndexedDimName.end() )
777             {
778                 constexpr bool bApproxOK = false;
779                 constexpr bool bForce = true;
780                 double dfMin = 0.0;
781                 double dfMax = 0.0;
782                 if( srcArray->GetStatistics(poSrcDS, bApproxOK, bForce,
783                                         &dfMin, &dfMax, nullptr, nullptr,
784                                         nullptr,
785                                         nullptr, nullptr) != CE_None )
786                 {
787                     CPLError(CE_Failure, CPLE_AppDefined,
788                              "Could not retrieve statistics for array %s",
789                              srcArray->GetName().c_str());
790                     return false;
791                 }
792                 double dfDTMin = 0;
793                 double dfDTMax = 0;
794 #define setDTMinMax(ctype) do \
795     { dfDTMin = std::numeric_limits<ctype>::min(); dfDTMax = std::numeric_limits<ctype>::max(); } while(0)
796 
797                 switch( eAutoScaleType )
798                 {
799                     case GDT_Byte:   setDTMinMax(GByte); break;
800                     case GDT_UInt16: setDTMinMax(GUInt16); break;
801                     case GDT_Int16:  setDTMinMax(GInt16); break;
802                     case GDT_UInt32: setDTMinMax(GUInt32); break;
803                     case GDT_Int32:  setDTMinMax(GInt32); break;
804                     default:
805                         CPLAssert(false);
806                 }
807 
808                 dstArray = CreateMDArray(srcArray->GetName(),
809                                          dstArrayDims,
810                                          GDALExtendedDataType::Create(eAutoScaleType),
811                                          aosArrayCO.List());
812                 EXIT_OR_CONTINUE_IF_NULL(dstArray);
813 
814                 if( srcArray->GetRawNoDataValue() != nullptr )
815                 {
816                     // If there's a nodata value in the source array, reserve
817                     // DTMax for that purpose in the target scaled array
818                     if( !dstArray->SetNoDataValue(dfDTMax) )
819                     {
820                         CPLError(CE_Failure, CPLE_AppDefined,
821                                  "Cannot set nodata value");
822                         return false;
823                     }
824                     dfDTMax --;
825                 }
826                 const double dfScale = dfMax > dfMin ?
827                     (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
828                 const double dfOffset = dfMin - dfDTMin * dfScale;
829 
830                 if( !dstArray->SetOffset(dfOffset) ||
831                     !dstArray->SetScale(dfScale) )
832                 {
833                     CPLError(CE_Failure, CPLE_AppDefined,
834                              "Cannot set scale/offset");
835                     return false;
836                 }
837 
838                 auto poUnscaled = dstArray->GetUnscaled();
839                 if( srcArray->GetRawNoDataValue() != nullptr )
840                 {
841                     poUnscaled->SetNoDataValue(
842                         srcArray->GetNoDataValueAsDouble() );
843                 }
844 
845                 // Copy source array into unscaled array
846                 if( !poUnscaled->CopyFrom(poSrcDS,
847                                           srcArray.get(), bStrict,
848                                           nCurCost, nTotalCost,
849                                           pfnProgress, pProgressData) )
850                 {
851                     return false;
852                 }
853             }
854             else
855             {
856                 dstArray = CreateMDArray(srcArray->GetName(),
857                                           dstArrayDims,
858                                           srcArrayType,
859                                           aosArrayCO.List());
860                 EXIT_OR_CONTINUE_IF_NULL(dstArray);
861 
862                 if( !dstArray->CopyFrom(poSrcDS,
863                                         srcArray.get(), bStrict,
864                                         nCurCost, nTotalCost,
865                                         pfnProgress, pProgressData) )
866                 {
867                     return false;
868                 }
869             }
870 
871             // If this array is the indexing variable of a dimension, link them
872             // together.
873             if( oIterDimName != mapSrcVariableNameToIndexedDimName.end() )
874             {
875                 auto oCorrespondingDimIter = mapExistingDstDims.find(oIterDimName->second);
876                 if( oCorrespondingDimIter != mapExistingDstDims.end() )
877                 {
878                     CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
879                     CPLErrorStateBackuper oErrorStateBackuper;
880                     oCorrespondingDimIter->second->SetIndexingVariable(dstArray);
881                 }
882             }
883         }
884 
885         auto groupNames = poSrcGroup->GetGroupNames();
886         for( const auto& name: groupNames )
887         {
888             auto srcSubGroup = poSrcGroup->OpenGroup(name);
889             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
890             auto dstSubGroup = CreateGroup(name);
891             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
892             if( !dstSubGroup->CopyFrom(poDstRootGroup, poSrcDS,
893                                        srcSubGroup, bStrict,
894                                        nCurCost, nTotalCost,
895                                        pfnProgress, pProgressData,
896                                        papszOptions) )
897                 return false;
898         }
899 
900         if( !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData) )
901                 return false;
902 
903         return true;
904     }
905     catch( const std::exception& e )
906     {
907         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
908         return false;
909     }
910 }
911 
912 /************************************************************************/
913 /*                         GetInnerMostGroup()                          */
914 /************************************************************************/
915 
916 //! @cond Doxygen_Suppress
GetInnerMostGroup(const std::string & osPathOrArrayOrDim,std::shared_ptr<GDALGroup> & curGroupHolder,std::string & osLastPart) const917 const GDALGroup* GDALGroup::GetInnerMostGroup(
918                                         const std::string& osPathOrArrayOrDim,
919                                         std::shared_ptr<GDALGroup>& curGroupHolder,
920                                         std::string& osLastPart) const
921 {
922     if( osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/' )
923         return nullptr;
924     const GDALGroup* poCurGroup = this;
925     CPLStringList aosTokens(CSLTokenizeString2(
926         osPathOrArrayOrDim.c_str(), "/", 0));
927     if( aosTokens.size() == 0 )
928     {
929         return nullptr;
930     }
931 
932     for( int i = 0; i < aosTokens.size() - 1; i++ )
933     {
934         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
935         if( !curGroupHolder )
936         {
937             CPLError(CE_Failure, CPLE_AppDefined,
938                     "Cannot find group %s", aosTokens[i]);
939             return nullptr;
940         }
941         poCurGroup = curGroupHolder.get();
942     }
943     osLastPart = aosTokens[aosTokens.size()-1];
944     return poCurGroup;
945 }
946 //! @endcond
947 
948 /************************************************************************/
949 /*                      OpenMDArrayFromFullname()                       */
950 /************************************************************************/
951 
952 /** Get an array from its fully qualified name */
OpenMDArrayFromFullname(const std::string & osFullName,CSLConstList papszOptions) const953 std::shared_ptr<GDALMDArray> GDALGroup::OpenMDArrayFromFullname(
954                                                 const std::string& osFullName,
955                                                 CSLConstList papszOptions) const
956 {
957     std::string osName;
958     std::shared_ptr<GDALGroup> curGroupHolder;
959     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
960     if( poGroup == nullptr )
961         return nullptr;
962     return poGroup->OpenMDArray(osName, papszOptions);
963 }
964 
965 /************************************************************************/
966 /*                          ResolveMDArray()                            */
967 /************************************************************************/
968 
969 /** Locate an array in a group and its subgroups by name.
970  *
971  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
972  * used
973  * Otherwise the search will start from the group identified by osStartingPath,
974  * and an array whose name is osName will be looked for in this group (if
975  * osStartingPath is empty or "/", then the current group is used). If there
976  * is no match, then a recursive descendent search will be made in its subgroups.
977  * If there is no match in the subgroups, then the parent (if existing) of the
978  * group pointed by osStartingPath will be used as the new starting point for the
979  * search.
980  *
981  * @param osName name, qualified or not
982  * @param osStartingPath fully qualified name of the (sub-)group from which
983  *                       the search should be started. If this is a non-empty
984  *                       string, the group on which this method is called should
985  *                       nominally be the root group (otherwise the path will
986  *                       be interpreted as from the current group)
987  * @param papszOptions options to pass to OpenMDArray()
988  * @since GDAL 3.2
989  */
ResolveMDArray(const std::string & osName,const std::string & osStartingPath,CSLConstList papszOptions) const990 std::shared_ptr<GDALMDArray> GDALGroup::ResolveMDArray(
991                                                 const std::string& osName,
992                                                 const std::string& osStartingPath,
993                                                 CSLConstList papszOptions) const
994 {
995     if( !osName.empty() && osName[0] == '/' )
996     {
997         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
998         if( poArray )
999             return poArray;
1000     }
1001     std::string osPath(osStartingPath);
1002     std::set<std::string> oSetAlreadyVisited;
1003 
1004     while(true)
1005     {
1006         std::shared_ptr<GDALGroup> curGroupHolder;
1007         std::shared_ptr<GDALGroup> poGroup;
1008 
1009         std::queue<std::shared_ptr<GDALGroup>> oQueue;
1010         bool goOn = false;
1011         if( osPath.empty() || osPath == "/" )
1012         {
1013             goOn = true;
1014         }
1015         else
1016         {
1017             std::string osLastPart;
1018             const GDALGroup* poGroupPtr = GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
1019             if( poGroupPtr )
1020                 poGroup = poGroupPtr->OpenGroup(osLastPart);
1021             if( poGroup &&
1022                 oSetAlreadyVisited.find(poGroup->GetFullName()) ==
1023                     oSetAlreadyVisited.end() )
1024             {
1025                 oQueue.push(poGroup);
1026                 goOn = true;
1027             }
1028         }
1029 
1030         if( goOn )
1031         {
1032             do
1033             {
1034                 const GDALGroup* groupPtr;
1035                 if( !oQueue.empty() )
1036                 {
1037                     poGroup = oQueue.front();
1038                     oQueue.pop();
1039                     groupPtr = poGroup.get();
1040                 }
1041                 else
1042                 {
1043                     groupPtr = this;
1044                 }
1045 
1046                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
1047                 if( poArray )
1048                     return poArray;
1049 
1050                 const auto aosGroupNames = groupPtr->GetGroupNames();
1051                 for( const auto& osGroupName : aosGroupNames )
1052                 {
1053                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
1054                     if( poSubGroup &&
1055                         oSetAlreadyVisited.find(poSubGroup->GetFullName()) ==
1056                             oSetAlreadyVisited.end() )
1057                     {
1058                         oQueue.push(poSubGroup);
1059                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
1060                     }
1061                 }
1062             }
1063             while( !oQueue.empty() );
1064         }
1065 
1066         if( osPath.empty() || osPath == "/" )
1067             break;
1068 
1069         const auto nPos = osPath.rfind('/');
1070         if( nPos == 0 )
1071             osPath = "/";
1072         else
1073         {
1074             if( nPos == std::string::npos )
1075                 break;
1076             osPath.resize(nPos);
1077         }
1078     }
1079     return nullptr;
1080 }
1081 
1082 /************************************************************************/
1083 /*                       OpenGroupFromFullname()                        */
1084 /************************************************************************/
1085 
1086 /** Get a group from its fully qualified name.
1087  * @since GDAL 3.2
1088  */
OpenGroupFromFullname(const std::string & osFullName,CSLConstList papszOptions) const1089 std::shared_ptr<GDALGroup> GDALGroup::OpenGroupFromFullname(
1090                                                 const std::string& osFullName,
1091                                                 CSLConstList papszOptions) const
1092 {
1093     std::string osName;
1094     std::shared_ptr<GDALGroup> curGroupHolder;
1095     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1096     if( poGroup == nullptr )
1097         return nullptr;
1098     return poGroup->OpenGroup(osName, papszOptions);
1099 }
1100 
1101 /************************************************************************/
1102 /*                      OpenDimensionFromFullname()                     */
1103 /************************************************************************/
1104 
1105 /** Get a dimension from its fully qualified name */
OpenDimensionFromFullname(const std::string & osFullName) const1106 std::shared_ptr<GDALDimension> GDALGroup::OpenDimensionFromFullname(
1107                                         const std::string& osFullName) const
1108 {
1109     std::string osName;
1110     std::shared_ptr<GDALGroup> curGroupHolder;
1111     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
1112     if( poGroup == nullptr )
1113         return nullptr;
1114     auto dims(poGroup->GetDimensions());
1115     for( auto& dim: dims )
1116     {
1117         if( dim->GetName() == osName )
1118             return dim;
1119     }
1120     return nullptr;
1121 }
1122 
1123 /************************************************************************/
1124 /*                       ~GDALAbstractMDArray()                         */
1125 /************************************************************************/
1126 
1127 GDALAbstractMDArray::~GDALAbstractMDArray() = default;
1128 
1129 /************************************************************************/
1130 /*                        GDALAbstractMDArray()                         */
1131 /************************************************************************/
1132 
1133 //! @cond Doxygen_Suppress
GDALAbstractMDArray(const std::string & osParentName,const std::string & osName)1134 GDALAbstractMDArray::GDALAbstractMDArray(const std::string& osParentName,
1135                                          const std::string& osName):
1136     m_osName(osName),
1137     m_osFullName(!osParentName.empty() ? ((osParentName == "/" ? "/" : osParentName + "/") + osName) : osName)
1138 {}
1139 //! @endcond
1140 
1141 /************************************************************************/
1142 /*                           GetDimensions()                            */
1143 /************************************************************************/
1144 
1145 /** \fn GDALAbstractMDArray::GetDimensions() const
1146  * \brief Return the dimensions of an attribute/array.
1147  *
1148  * This is the same as the C functions GDALMDArrayGetDimensions() and
1149  * similar to GDALAttributeGetDimensionsSize().
1150  */
1151 
1152 /************************************************************************/
1153 /*                           GetDataType()                              */
1154 /************************************************************************/
1155 
1156 /** \fn GDALAbstractMDArray::GetDataType() const
1157  * \brief Return the data type of an attribute/array.
1158  *
1159  * This is the same as the C functions GDALMDArrayGetDataType() and
1160  * GDALAttributeGetDataType()
1161  */
1162 
1163 /************************************************************************/
1164 /*                        GetDimensionCount()                           */
1165 /************************************************************************/
1166 
1167 /** Return the number of dimensions.
1168  *
1169  * Default implementation is GetDimensions().size(), and may be overridden by
1170  * drivers if they have a faster / less expensive implementations.
1171  *
1172  * This is the same as the C function GDALMDArrayGetDimensionCount() or
1173  * GDALAttributeGetDimensionCount().
1174  *
1175  */
GetDimensionCount() const1176 size_t GDALAbstractMDArray::GetDimensionCount() const
1177 {
1178     return GetDimensions().size();
1179 }
1180 
1181 /************************************************************************/
1182 /*                             CopyValue()                              */
1183 /************************************************************************/
1184 
1185 /** Convert a value from a source type to a destination type.
1186  *
1187  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
1188  * that must be freed with CPLFree().
1189  */
CopyValue(const void * pSrc,const GDALExtendedDataType & srcType,void * pDst,const GDALExtendedDataType & dstType)1190 bool GDALExtendedDataType::CopyValue(const void* pSrc,
1191                                     const GDALExtendedDataType& srcType,
1192                                     void* pDst,
1193                                     const GDALExtendedDataType& dstType)
1194 {
1195     if( srcType.GetClass() == GEDTC_NUMERIC &&
1196         dstType.GetClass() == GEDTC_NUMERIC )
1197     {
1198         GDALCopyWords( pSrc, srcType.GetNumericDataType(), 0,
1199                        pDst, dstType.GetNumericDataType(), 0,
1200                        1 );
1201         return true;
1202     }
1203     if( srcType.GetClass() == GEDTC_STRING &&
1204         dstType.GetClass() == GEDTC_STRING )
1205     {
1206         const char* srcStrPtr;
1207         memcpy(&srcStrPtr, pSrc, sizeof(const char*));
1208         char* pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
1209         memcpy(pDst, &pszDup, sizeof(char*));
1210         return true;
1211     }
1212     if( srcType.GetClass() == GEDTC_NUMERIC &&
1213         dstType.GetClass() == GEDTC_STRING )
1214     {
1215         const char* str = nullptr;
1216         switch(srcType.GetNumericDataType())
1217         {
1218             case GDT_Unknown: break;
1219             case GDT_Byte:
1220                 str = CPLSPrintf("%d", *static_cast<const GByte*>(pSrc));
1221                 break;
1222             case GDT_UInt16:
1223                 str = CPLSPrintf("%d", *static_cast<const GUInt16*>(pSrc));
1224                 break;
1225             case GDT_Int16:
1226                 str = CPLSPrintf("%d", *static_cast<const GInt16*>(pSrc));
1227                 break;
1228             case GDT_UInt32:
1229                 str = CPLSPrintf("%u", *static_cast<const GUInt32*>(pSrc));
1230                 break;
1231             case GDT_Int32:
1232                 str = CPLSPrintf("%d", *static_cast<const GInt32*>(pSrc));
1233                 break;
1234             case GDT_Float32:
1235                 str = CPLSPrintf("%.9g", *static_cast<const float*>(pSrc));
1236                 break;
1237             case GDT_Float64:
1238                 str = CPLSPrintf("%.18g", *static_cast<const double*>(pSrc));
1239                 break;
1240             case GDT_CInt16:
1241             {
1242                 const GInt16* src = static_cast<const GInt16*>(pSrc);
1243                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
1244                 break;
1245             }
1246             case GDT_CInt32:
1247             {
1248                 const GInt32* src = static_cast<const GInt32*>(pSrc);
1249                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
1250                 break;
1251             }
1252             case GDT_CFloat32:
1253             {
1254                 const float* src = static_cast<const float*>(pSrc);
1255                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
1256                 break;
1257             }
1258             case GDT_CFloat64:
1259             {
1260                 const double* src = static_cast<const double*>(pSrc);
1261                 str = CPLSPrintf("%.18g+%.18gj", src[0], src[1]);
1262                 break;
1263             }
1264             case GDT_TypeCount:
1265                 CPLAssert(false);
1266                 break;
1267         }
1268         char* pszDup = str ? CPLStrdup(str) : nullptr;
1269         memcpy(pDst, &pszDup, sizeof(char*));
1270         return true;
1271     }
1272     if( srcType.GetClass() == GEDTC_STRING &&
1273         dstType.GetClass() == GEDTC_NUMERIC )
1274     {
1275         const char* srcStrPtr;
1276         memcpy(&srcStrPtr, pSrc, sizeof(const char*));
1277         const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
1278         GDALCopyWords( &dfVal, GDT_Float64, 0,
1279                        pDst, dstType.GetNumericDataType(), 0,
1280                        1 );
1281         return true;
1282     }
1283     if( srcType.GetClass() == GEDTC_COMPOUND &&
1284         dstType.GetClass() == GEDTC_COMPOUND )
1285     {
1286         const auto& srcComponents = srcType.GetComponents();
1287         const auto& dstComponents = dstType.GetComponents();
1288         const GByte* pabySrc = static_cast<const GByte*>(pSrc);
1289         GByte* pabyDst = static_cast<GByte*>(pDst);
1290 
1291         std::map<std::string, const std::unique_ptr<GDALEDTComponent>*> srcComponentMap;
1292         for( const auto& srcComp: srcComponents )
1293         {
1294             srcComponentMap[srcComp->GetName()] = &srcComp;
1295         }
1296         for( const auto& dstComp: dstComponents )
1297         {
1298             auto oIter = srcComponentMap.find(dstComp->GetName());
1299             if( oIter == srcComponentMap.end() )
1300                 return false;
1301             const auto& srcComp = *(oIter->second);
1302             if( !GDALExtendedDataType::CopyValue(pabySrc + srcComp->GetOffset(),
1303                                 srcComp->GetType(),
1304                                 pabyDst + dstComp->GetOffset(),
1305                                 dstComp->GetType()) )
1306             {
1307                 return false;
1308             };
1309         }
1310         return true;
1311     }
1312 
1313     return false;
1314 }
1315 
1316 /************************************************************************/
1317 /*                       CheckReadWriteParams()                         */
1318 /************************************************************************/
1319 //! @cond Doxygen_Suppress
CheckReadWriteParams(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * & arrayStep,const GPtrDiff_t * & bufferStride,const GDALExtendedDataType & bufferDataType,const void * buffer,const void * buffer_alloc_start,size_t buffer_alloc_size,std::vector<GInt64> & tmp_arrayStep,std::vector<GPtrDiff_t> & tmp_bufferStride) const1320 bool GDALAbstractMDArray::CheckReadWriteParams(const GUInt64* arrayStartIdx,
1321                               const size_t* count,
1322                               const GInt64*& arrayStep,
1323                               const GPtrDiff_t*& bufferStride,
1324                               const GDALExtendedDataType& bufferDataType,
1325                               const void* buffer,
1326                               const void* buffer_alloc_start,
1327                               size_t buffer_alloc_size,
1328                               std::vector<GInt64>& tmp_arrayStep,
1329                               std::vector<GPtrDiff_t>& tmp_bufferStride) const
1330 {
1331     const auto lamda_error = []() {
1332         CPLError(CE_Failure, CPLE_AppDefined,
1333                         "Not all elements pointed by buffer will fit in "
1334                         "[buffer_alloc_start, "
1335                         "buffer_alloc_start + buffer_alloc_size[");
1336     };
1337 
1338     const auto& dims = GetDimensions();
1339     if( dims.empty() )
1340     {
1341         if( buffer_alloc_start )
1342         {
1343             const size_t elementSize = bufferDataType.GetSize();
1344             const GByte* paby_buffer = static_cast<const GByte*>(buffer);
1345             const GByte* paby_buffer_alloc_start =
1346                 static_cast<const GByte*>(buffer_alloc_start);
1347             const GByte* paby_buffer_alloc_end =
1348                 paby_buffer_alloc_start + buffer_alloc_size;
1349 
1350             if( paby_buffer < paby_buffer_alloc_start ||
1351                 paby_buffer + elementSize > paby_buffer_alloc_end )
1352             {
1353                 lamda_error();
1354                 return false;
1355             }
1356         }
1357         return true;
1358     }
1359 
1360     if( arrayStep == nullptr )
1361     {
1362         tmp_arrayStep.resize(dims.size(), 1);
1363         arrayStep = tmp_arrayStep.data();
1364     }
1365     for( size_t i = 0; i < dims.size(); i++ )
1366     {
1367         if( count[i] == 0 )
1368         {
1369             CPLError(CE_Failure, CPLE_AppDefined,
1370                      "count[%u] = 0 is invalid",
1371                      static_cast<unsigned>(i));
1372             return false;
1373         }
1374     }
1375     bool bufferStride_all_positive = true;
1376     if( bufferStride == nullptr )
1377     {
1378         GPtrDiff_t stride = 1;
1379         // To compute strides we must proceed from the fastest varying dimension
1380         // (the last one), and then reverse the result
1381         for( size_t i = dims.size(); i != 0;  )
1382         {
1383             --i;
1384             tmp_bufferStride.push_back(stride);
1385             GUInt64 newStride = 0;
1386             bool bOK;
1387             try
1388             {
1389                 newStride = (CPLSM(static_cast<GUInt64>(stride)) *
1390                              CPLSM(static_cast<GUInt64>(count[i]))).v();
1391                 bOK = static_cast<size_t>(newStride) == newStride &&
1392                       newStride < std::numeric_limits<size_t>::max() / 2;
1393             }
1394             catch( ... )
1395             {
1396                 bOK = false;
1397             }
1398             if( !bOK )
1399             {
1400                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
1401                 return false;
1402             }
1403             stride = static_cast<GPtrDiff_t>(newStride);
1404         }
1405         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
1406         bufferStride = tmp_bufferStride.data();
1407     }
1408     else
1409     {
1410         for( size_t i = 0; i < dims.size(); i++ )
1411         {
1412             if( bufferStride[i] < 0 )
1413             {
1414                 bufferStride_all_positive = false;
1415                 break;
1416             }
1417         }
1418     }
1419     for( size_t i = 0; i < dims.size(); i++ )
1420     {
1421         if( arrayStartIdx[i] >= dims[i]->GetSize() )
1422         {
1423             CPLError(CE_Failure, CPLE_AppDefined,
1424                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
1425                      static_cast<unsigned>(i),
1426                      static_cast<GUInt64>(arrayStartIdx[i]),
1427                      static_cast<GUInt64>(dims[i]->GetSize()));
1428             return false;
1429         }
1430         bool bOverflow;
1431         if( arrayStep[i] >= 0)
1432         {
1433             try
1434             {
1435                 bOverflow = (CPLSM(static_cast<GUInt64>(arrayStartIdx[i])) +
1436                             CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1437                                 CPLSM(static_cast<GUInt64>(arrayStep[i]))).v() >= dims[i]->GetSize();
1438             }
1439             catch( ... )
1440             {
1441                 bOverflow = true;
1442             }
1443             if( bOverflow )
1444             {
1445                 CPLError(CE_Failure, CPLE_AppDefined,
1446                         "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] >= " CPL_FRMT_GUIB,
1447                         static_cast<unsigned>(i),
1448                         static_cast<unsigned>(i),
1449                         static_cast<unsigned>(i),
1450                         static_cast<GUInt64>(dims[i]->GetSize()));
1451                 return false;
1452             }
1453         }
1454         else
1455         {
1456             try
1457             {
1458                 bOverflow =  arrayStartIdx[i] <
1459                     (CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1460                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min() ?
1461                         (static_cast<GUInt64>(1) << 63) :
1462                         static_cast<GUInt64>(-arrayStep[i]))).v();
1463             }
1464             catch( ... )
1465             {
1466                 bOverflow = true;
1467             }
1468             if( bOverflow )
1469             {
1470                 CPLError(CE_Failure, CPLE_AppDefined,
1471                         "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
1472                         static_cast<unsigned>(i),
1473                         static_cast<unsigned>(i),
1474                         static_cast<unsigned>(i));
1475                 return false;
1476             }
1477         }
1478     }
1479 
1480     if( buffer_alloc_start )
1481     {
1482         const size_t elementSize = bufferDataType.GetSize();
1483         const GByte* paby_buffer = static_cast<const GByte*>(buffer);
1484         const GByte* paby_buffer_alloc_start = static_cast<const GByte*>(buffer_alloc_start);
1485         const GByte* paby_buffer_alloc_end = paby_buffer_alloc_start + buffer_alloc_size;
1486         if( bufferStride_all_positive )
1487         {
1488             if( paby_buffer < paby_buffer_alloc_start )
1489             {
1490                 lamda_error();
1491                 return false;
1492             }
1493             GUInt64 nOffset = elementSize;
1494             for( size_t i = 0; i < dims.size(); i++ )
1495             {
1496                 try
1497                 {
1498                     nOffset = (CPLSM(static_cast<GUInt64>(nOffset)) +
1499                                 CPLSM(static_cast<GUInt64>(bufferStride[i])) *
1500                                 CPLSM(static_cast<GUInt64>(count[i] - 1)) *
1501                                 CPLSM(static_cast<GUInt64>(elementSize))).v();
1502                 }
1503                 catch( ... )
1504                 {
1505                     lamda_error();
1506                     return false;
1507                 }
1508             }
1509 #if SIZEOF_VOID == 4
1510             if( static_cast<size_t>(nOffset) != nOffset )
1511             {
1512                 lamda_error();
1513                 return false;
1514             }
1515 #endif
1516             if( paby_buffer + nOffset > paby_buffer_alloc_end )
1517             {
1518                 lamda_error();
1519                 return false;
1520             }
1521         }
1522         else if( dims.size() < 31 )
1523         {
1524             // Check all corners of the hypercube
1525             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
1526             for( unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++ )
1527             {
1528                 const GByte* paby = paby_buffer;
1529                 for( unsigned i = 0; i < static_cast<unsigned>(dims.size()); i++ )
1530                 {
1531                     if( iCornerCode & (1U << i) )
1532                     {
1533                         // We should check for integer overflows
1534                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
1535                     }
1536                 }
1537                 if( paby < paby_buffer_alloc_start ||
1538                     paby + elementSize > paby_buffer_alloc_end )
1539                 {
1540                     lamda_error();
1541                     return false;
1542                 }
1543             }
1544         }
1545     }
1546 
1547     return true;
1548 }
1549 //! @endcond
1550 
1551 /************************************************************************/
1552 /*                               Read()                                 */
1553 /************************************************************************/
1554 
1555 /** Read part or totality of a multidimensional array or attribute.
1556  *
1557  * This will extract the content of a hyper-rectangle from the array into
1558  * a user supplied buffer.
1559  *
1560  * If bufferDataType is of type string, the values written in pDstBuffer
1561  * will be char* pointers and the strings should be freed with CPLFree().
1562  *
1563  * This is the same as the C function GDALMDArrayRead().
1564  *
1565  * @param arrayStartIdx Values representing the starting index to read
1566  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
1567  *                      Array of GetDimensionCount() values. Must not be
1568  *                      nullptr, unless for a zero-dimensional array.
1569  *
1570  * @param count         Values representing the number of values to extract in
1571  *                      each dimension.
1572  *                      Array of GetDimensionCount() values. Must not be
1573  *                      nullptr, unless for a zero-dimensional array.
1574  *
1575  * @param arrayStep     Spacing between values to extract in each dimension.
1576  *                      The spacing is in number of array elements, not bytes.
1577  *                      If provided, must contain GetDimensionCount() values.
1578  *                      If set to nullptr, [1, 1, ... 1] will be used as a default
1579  *                      to indicate consecutive elements.
1580  *
1581  * @param bufferStride  Spacing between values to store in pDstBuffer.
1582  *                      The spacing is in number of array elements, not bytes.
1583  *                      If provided, must contain GetDimensionCount() values.
1584  *                      Negative values are possible (for example to reorder
1585  *                      from bottom-to-top to top-to-bottom).
1586  *                      If set to nullptr, will be set so that pDstBuffer is
1587  *                      written in a compact way, with elements of the last /
1588  *                      fastest varying dimension being consecutive.
1589  *
1590  * @param bufferDataType Data type of values in pDstBuffer.
1591  *
1592  * @param pDstBuffer    User buffer to store the values read. Should be big
1593  *                      enough to store the number of values indicated by count[] and
1594  *                      with the spacing of bufferStride[].
1595  *
1596  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
1597  *                             validty of pDstBuffer. pDstBufferAllocStart should
1598  *                             be the pointer returned by the malloc() or equivalent
1599  *                             call used to allocate the buffer. It will generally
1600  *                             be equal to pDstBuffer (when bufferStride[] values are
1601  *                             all positive), but not necessarily.
1602  *                             If specified, nDstBufferAllocSize should be also
1603  *                             set to the appropriate value.
1604  *                             If no validation is needed, nullptr can be passed.
1605  *
1606  * @param nDstBufferAllocSize  Optional buffer size, that can be used to validate the
1607  *                             validty of pDstBuffer. This is the size of the
1608  *                             buffer starting at pDstBufferAllocStart.
1609  *                             If specified, pDstBufferAllocStart should be also
1610  *                             set to the appropriate value.
1611  *                             If no validation is needed, 0 can be passed.
1612  *
1613  * @return true in case of success.
1614  */
Read(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer,const void * pDstBufferAllocStart,size_t nDstBufferAllocSize) const1615 bool GDALAbstractMDArray::Read(const GUInt64* arrayStartIdx,
1616                       const size_t* count,
1617                       const GInt64* arrayStep,        // step in elements
1618                       const GPtrDiff_t* bufferStride, // stride in elements
1619                       const GDALExtendedDataType& bufferDataType,
1620                       void* pDstBuffer,
1621                       const void* pDstBufferAllocStart,
1622                       size_t nDstBufferAllocSize) const
1623 {
1624     if( !GetDataType().CanConvertTo(bufferDataType) )
1625     {
1626         CPLError(CE_Failure, CPLE_AppDefined,
1627                  "Array data type is not convertible to buffer data type");
1628         return false;
1629     }
1630 
1631     std::vector<GInt64> tmp_arrayStep;
1632     std::vector<GPtrDiff_t> tmp_bufferStride;
1633     if( !CheckReadWriteParams(arrayStartIdx,
1634                               count,
1635                               arrayStep,
1636                               bufferStride,
1637                               bufferDataType,
1638                               pDstBuffer,
1639                               pDstBufferAllocStart,
1640                               nDstBufferAllocSize,
1641                               tmp_arrayStep,
1642                               tmp_bufferStride) )
1643     {
1644         return false;
1645     }
1646 
1647     return IRead(arrayStartIdx, count, arrayStep,
1648                  bufferStride, bufferDataType, pDstBuffer);
1649 }
1650 
1651 /************************************************************************/
1652 /*                                IWrite()                              */
1653 /************************************************************************/
1654 
1655 //! @cond Doxygen_Suppress
IWrite(const GUInt64 *,const size_t *,const GInt64 *,const GPtrDiff_t *,const GDALExtendedDataType &,const void *)1656 bool GDALAbstractMDArray::IWrite(const GUInt64*,
1657                                const size_t*,
1658                                const GInt64*,
1659                                const GPtrDiff_t*,
1660                                const GDALExtendedDataType&,
1661                                const void*)
1662 {
1663     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
1664     return false;
1665 }
1666 //! @endcond
1667 
1668 /************************************************************************/
1669 /*                               Write()                                 */
1670 /************************************************************************/
1671 
1672 /** Write part or totality of a multidimensional array or attribute.
1673  *
1674  * This will set the content of a hyper-rectangle into the array from
1675  * a user supplied buffer.
1676  *
1677  * If bufferDataType is of type string, the values read from pSrcBuffer
1678  * will be char* pointers.
1679  *
1680  * This is the same as the C function GDALMDArrayWrite().
1681  *
1682  * @param arrayStartIdx Values representing the starting index to write
1683  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
1684  *                      Array of GetDimensionCount() values. Must not be
1685  *                      nullptr, unless for a zero-dimensional array.
1686  *
1687  * @param count         Values representing the number of values to write in
1688  *                      each dimension.
1689  *                      Array of GetDimensionCount() values. Must not be
1690  *                      nullptr, unless for a zero-dimensional array.
1691  *
1692  * @param arrayStep     Spacing between values to write in each dimension.
1693  *                      The spacing is in number of array elements, not bytes.
1694  *                      If provided, must contain GetDimensionCount() values.
1695  *                      If set to nullptr, [1, 1, ... 1] will be used as a default
1696  *                      to indicate consecutive elements.
1697  *
1698  * @param bufferStride  Spacing between values to read from pSrcBuffer.
1699  *                      The spacing is in number of array elements, not bytes.
1700  *                      If provided, must contain GetDimensionCount() values.
1701  *                      Negative values are possible (for example to reorder
1702  *                      from bottom-to-top to top-to-bottom).
1703  *                      If set to nullptr, will be set so that pSrcBuffer is
1704  *                      written in a compact way, with elements of the last /
1705  *                      fastest varying dimension being consecutive.
1706  *
1707  * @param bufferDataType Data type of values in pSrcBuffer.
1708  *
1709  * @param pSrcBuffer    User buffer to read the values from. Should be big
1710  *                      enough to store the number of values indicated by count[] and
1711  *                      with the spacing of bufferStride[].
1712  *
1713  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
1714  *                             validty of pSrcBuffer. pSrcBufferAllocStart should
1715  *                             be the pointer returned by the malloc() or equivalent
1716  *                             call used to allocate the buffer. It will generally
1717  *                             be equal to pSrcBuffer (when bufferStride[] values are
1718  *                             all positive), but not necessarily.
1719  *                             If specified, nSrcBufferAllocSize should be also
1720  *                             set to the appropriate value.
1721  *                             If no validation is needed, nullptr can be passed.
1722  *
1723  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to validate the
1724  *                             validty of pSrcBuffer. This is the size of the
1725  *                             buffer starting at pSrcBufferAllocStart.
1726  *                             If specified, pDstBufferAllocStart should be also
1727  *                             set to the appropriate value.
1728  *                             If no validation is needed, 0 can be passed.
1729  *
1730  * @return true in case of success.
1731  */
Write(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,const void * pSrcBuffer,const void * pSrcBufferAllocStart,size_t nSrcBufferAllocSize)1732 bool GDALAbstractMDArray::Write(const GUInt64* arrayStartIdx,
1733                                 const size_t* count,
1734                                 const GInt64* arrayStep,
1735                                 const GPtrDiff_t* bufferStride,
1736                                 const GDALExtendedDataType& bufferDataType,
1737                                 const void* pSrcBuffer,
1738                                 const void* pSrcBufferAllocStart,
1739                                 size_t nSrcBufferAllocSize)
1740 {
1741     if( !bufferDataType.CanConvertTo(GetDataType()) )
1742     {
1743         CPLError(CE_Failure, CPLE_AppDefined,
1744                  "Buffer data type is not convertible to array data type");
1745         return false;
1746     }
1747 
1748     std::vector<GInt64> tmp_arrayStep;
1749     std::vector<GPtrDiff_t> tmp_bufferStride;
1750     if( !CheckReadWriteParams(arrayStartIdx,
1751                               count,
1752                               arrayStep,
1753                               bufferStride,
1754                               bufferDataType,
1755                               pSrcBuffer,
1756                               pSrcBufferAllocStart,
1757                               nSrcBufferAllocSize,
1758                               tmp_arrayStep,
1759                               tmp_bufferStride) )
1760     {
1761         return false;
1762     }
1763 
1764     return IWrite(arrayStartIdx, count, arrayStep,
1765                  bufferStride, bufferDataType, pSrcBuffer);
1766 }
1767 
1768 /************************************************************************/
1769 /*                          GetTotalElementsCount()                     */
1770 /************************************************************************/
1771 
1772 /** Return the total number of values in the array.
1773  *
1774  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
1775  * and GDALAttributeGetTotalElementsCount().
1776  *
1777  */
GetTotalElementsCount() const1778 GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
1779 {
1780     const auto& dims = GetDimensions();
1781     if( dims.empty() )
1782         return 1;
1783     GUInt64 nElts = 1;
1784     for( const auto& dim: dims )
1785     {
1786         try
1787         {
1788             nElts = (CPLSM(static_cast<GUInt64>(nElts)) *
1789                      CPLSM(static_cast<GUInt64>(dim->GetSize()))).v();
1790         }
1791         catch( ... )
1792         {
1793             return 0;
1794         }
1795     }
1796     return nElts;
1797 }
1798 
1799 /************************************************************************/
1800 /*                           GetBlockSize()                             */
1801 /************************************************************************/
1802 
1803 /** Return the "natural" block size of the array along all dimensions.
1804  *
1805  * Some drivers might organize the array in tiles/blocks and reading/writing
1806  * aligned on those tile/block boundaries will be more efficient.
1807  *
1808  * The returned number of elements in the vector is the same as
1809  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
1810  * the natural block size along the considered dimension.
1811  * "Flat" arrays will typically return a vector of values set to 0.
1812  *
1813  * The default implementation will return a vector of values set to 0.
1814  *
1815  * This method is used by GetProcessingChunkSize().
1816  *
1817  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley theoretical
1818  * case of a 32-bit platform, this might exceed its size_t allocation capabilities.
1819  *
1820  * This is the same as the C function GDALMDArrayGetBlockSize().
1821  *
1822  * @return the block size, in number of elements along each dimension.
1823  */
GetBlockSize() const1824 std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
1825 {
1826     return std::vector<GUInt64>(GetDimensionCount());
1827 }
1828 
1829 /************************************************************************/
1830 /*                       GetProcessingChunkSize()                       */
1831 /************************************************************************/
1832 
1833 /** \brief Return an optimal chunk size for read/write oerations, given the natural
1834  * block size and memory constraints specified.
1835  *
1836  * This method will use GetBlockSize() to define a chunk whose dimensions are
1837  * multiple of those returned by GetBlockSize() (unless the block define by
1838  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
1839  * returned by this method).
1840  *
1841  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
1842  *
1843  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the chunk.
1844  *
1845  * @return the chunk size, in number of elements along each dimension.
1846  */
GetProcessingChunkSize(size_t nMaxChunkMemory) const1847 std::vector<size_t> GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
1848 {
1849     const auto& dims = GetDimensions();
1850     const auto& nDTSize = GetDataType().GetSize();
1851     std::vector<size_t> anChunkSize;
1852     auto blockSize = GetBlockSize();
1853     CPLAssert( blockSize.size() == dims.size() );
1854     size_t nChunkSize = nDTSize;
1855     bool bOverflow = false;
1856     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
1857     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
1858     // [1, min(sizet_max, dim_size[i])]
1859     // Also make sure that the product of all anChunkSize[i]) fits on size_t
1860     for( size_t i = 0; i < dims.size(); i++ )
1861     {
1862         const auto sizeDimI = std::max(
1863             static_cast<size_t>(1),
1864             static_cast<size_t>(
1865                 std::min(
1866                     static_cast<GUInt64>(kSIZE_T_MAX),
1867                     std::min(blockSize[i], dims[i]->GetSize()))));
1868         anChunkSize.push_back(sizeDimI);
1869         if( nChunkSize > kSIZE_T_MAX / sizeDimI )
1870         {
1871             bOverflow = true;
1872         }
1873         else
1874         {
1875             nChunkSize *= sizeDimI;
1876         }
1877     }
1878     if( nChunkSize == 0 )
1879         return anChunkSize;
1880 
1881     // If the product of all anChunkSize[i] does not fit on size_t, then
1882     // set lowest anChunkSize[i] to 1.
1883     if( bOverflow )
1884     {
1885         nChunkSize = nDTSize;
1886         bOverflow = false;
1887         for( size_t i = dims.size(); i > 0; )
1888         {
1889             --i;
1890             if( bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i] )
1891             {
1892                 bOverflow = true;
1893                 anChunkSize[i] = 1;
1894             }
1895             else
1896             {
1897                 nChunkSize *= anChunkSize[i];
1898             }
1899         }
1900     }
1901 
1902     nChunkSize = nDTSize;
1903     std::vector<size_t> anAccBlockSizeFromStart;
1904     for( size_t i = 0; i < dims.size(); i++ )
1905     {
1906         nChunkSize *= anChunkSize[i];
1907         anAccBlockSizeFromStart.push_back(nChunkSize);
1908     }
1909     if( nChunkSize <= nMaxChunkMemory / 2 )
1910     {
1911         size_t nVoxelsFromEnd = 1;
1912         for( size_t i = dims.size(); i > 0; )
1913         {
1914             --i;
1915             const auto nCurBlockSize =
1916                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
1917             const auto nMul = nMaxChunkMemory / nCurBlockSize;
1918             if( nMul >= 2 )
1919             {
1920                 const auto nSizeThisDim(dims[i]->GetSize());
1921                 const auto nBlocksThisDim =
1922                             DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
1923                 anChunkSize[i] = static_cast<size_t>(std::min(
1924                     anChunkSize[i] * std::min(
1925                         static_cast<GUInt64>(nMul), nBlocksThisDim),
1926                     nSizeThisDim ));
1927             }
1928             nVoxelsFromEnd *= anChunkSize[i];
1929         }
1930     }
1931     return anChunkSize;
1932 }
1933 
1934 /************************************************************************/
1935 /*                             SetUnit()                                */
1936 /************************************************************************/
1937 
1938 /** Set the variable unit.
1939  *
1940  * Values should conform as much as possible with those allowed by
1941  * the NetCDF CF conventions:
1942  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
1943  * but others might be returned.
1944  *
1945  * Few examples are "meter", "degrees", "second", ...
1946  * Empty value means unknown.
1947  *
1948  * This is the same as the C function GDALMDArraySetUnit()
1949  *
1950  * @note Driver implementation: optionally implemented.
1951  *
1952  * @param osUnit unit name.
1953  * @return true in case of success.
1954  */
SetUnit(CPL_UNUSED const std::string & osUnit)1955 bool GDALMDArray::SetUnit(CPL_UNUSED const std::string& osUnit)
1956 {
1957     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
1958     return false;
1959 }
1960 
1961 /************************************************************************/
1962 /*                             GetUnit()                                */
1963 /************************************************************************/
1964 
1965 /** Return the array unit.
1966  *
1967  * Values should conform as much as possible with those allowed by
1968  * the NetCDF CF conventions:
1969  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
1970  * but others might be returned.
1971  *
1972  * Few examples are "meter", "degrees", "second", ...
1973  * Empty value means unknown.
1974  *
1975  * This is the same as the C function GDALMDArrayGetUnit()
1976  */
GetUnit() const1977 const std::string& GDALMDArray::GetUnit() const
1978 {
1979     static const std::string emptyString;
1980     return emptyString;
1981 }
1982 
1983 /************************************************************************/
1984 /*                          SetSpatialRef()                             */
1985 /************************************************************************/
1986 
1987 /** Assign a spatial reference system object to the the array.
1988  *
1989  * This is the same as the C function GDALMDArraySetSpatialRef().
1990  */
SetSpatialRef(CPL_UNUSED const OGRSpatialReference * poSRS)1991 bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference* poSRS)
1992 {
1993     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
1994     return false;
1995 }
1996 
1997 /************************************************************************/
1998 /*                          GetSpatialRef()                             */
1999 /************************************************************************/
2000 
2001 /** Return the spatial reference system object associated with the array.
2002  *
2003  * This is the same as the C function GDALMDArrayGetSpatialRef().
2004  */
GetSpatialRef() const2005 std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
2006 {
2007     return nullptr;
2008 }
2009 
2010 /************************************************************************/
2011 /*                        GetRawNoDataValue()                           */
2012 /************************************************************************/
2013 
2014 /** Return the nodata value as a "raw" value.
2015  *
2016  * The value returned might be nullptr in case of no nodata value. When
2017  * a nodata value is registered, a non-nullptr will be returned whose size in
2018  * bytes is GetDataType().GetSize().
2019  *
2020  * The returned value should not be modified or freed. It is valid until
2021  * the array is destroyed, or the next call to GetRawNoDataValue() or
2022  * SetRawNoDataValue(), or any similar methods.
2023  *
2024  * @note Driver implementation: this method shall be implemented if nodata
2025  * is supported.
2026  *
2027  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
2028  *
2029  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
2030  */
GetRawNoDataValue() const2031 const void* GDALMDArray::GetRawNoDataValue() const
2032 {
2033     return nullptr;
2034 }
2035 
2036 /************************************************************************/
2037 /*                        GetNoDataValueAsDouble()                      */
2038 /************************************************************************/
2039 
2040 /** Return the nodata value as a double.
2041  *
2042  * The value returned might be nullptr in case of no nodata value. When
2043  * a nodata value is registered, a non-nullptr will be returned whose size in
2044  * bytes is GetDataType().GetSize().
2045  *
2046  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
2047  *
2048  * @param pbHasNoData Pointer to a output boolean that will be set to true if
2049  * a nodata value exists and can be converted to double. Might be nullptr.
2050  *
2051  * @return the nodata value as a double. A 0.0 value might also indicate the
2052  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
2053  * set to false then).
2054  */
GetNoDataValueAsDouble(bool * pbHasNoData) const2055 double GDALMDArray::GetNoDataValueAsDouble(bool* pbHasNoData) const
2056 {
2057     const void* pNoData = GetRawNoDataValue();
2058     if( !pNoData )
2059     {
2060         if( pbHasNoData )
2061             *pbHasNoData = false;
2062         return 0.0;
2063     }
2064     double dfNoData = 0.0;
2065     if( !GDALExtendedDataType::CopyValue(pNoData,
2066                     GetDataType(),
2067                     &dfNoData,
2068                     GDALExtendedDataType::Create(GDT_Float64)) )
2069     {
2070         if( pbHasNoData )
2071             *pbHasNoData = false;
2072         return 0.0;
2073     }
2074     if( pbHasNoData )
2075         *pbHasNoData = true;
2076     return dfNoData;
2077 }
2078 
2079 /************************************************************************/
2080 /*                        SetRawNoDataValue()                           */
2081 /************************************************************************/
2082 
2083 /** Set the nodata value as a "raw" value.
2084  *
2085  * The value passed might be nullptr in case of no nodata value. When
2086  * a nodata value is registered, a non-nullptr whose size in
2087  * bytes is GetDataType().GetSize() must be passed.
2088  *
2089  * This is the same as the C function GDALMDArraySetRawNoDataValue().
2090  *
2091  * @note Driver implementation: this method shall be implemented if setting nodata
2092  * is supported.
2093 
2094  * @return true in case of success.
2095  */
SetRawNoDataValue(CPL_UNUSED const void * pRawNoData)2096 bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void* pRawNoData)
2097 {
2098     CPLError(CE_Failure, CPLE_NotSupported, "SetRawNoDataValue() not implemented");
2099     return false;
2100 }
2101 
2102 /************************************************************************/
2103 /*                           SetNoDataValue()                           */
2104 /************************************************************************/
2105 
2106 /** Set the nodata value as a double.
2107  *
2108  * If the natural data type of the attribute/array is not double, type conversion
2109  * will occur to the type returned by GetDataType().
2110  *
2111  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
2112  *
2113  * @return true in case of success.
2114  */
SetNoDataValue(double dfNoData)2115 bool GDALMDArray::SetNoDataValue(double dfNoData)
2116 {
2117     void* pRawNoData = CPLMalloc(GetDataType().GetSize());
2118     bool bRet = false;
2119     if( GDALExtendedDataType::CopyValue(
2120                     &dfNoData, GDALExtendedDataType::Create(GDT_Float64),
2121                     pRawNoData, GetDataType()) )
2122     {
2123         bRet = SetRawNoDataValue(pRawNoData);
2124     }
2125     CPLFree(pRawNoData);
2126     return bRet;
2127 }
2128 
2129 /************************************************************************/
2130 /*                               SetScale()                             */
2131 /************************************************************************/
2132 
2133 /** Set the scale value to apply to raw values.
2134  *
2135  * unscaled_value = raw_value * GetScale() + GetOffset()
2136  *
2137  * This is the same as the C function GDALMDArraySetScale() / GDALMDArraySetScaleEx().
2138  *
2139  * @note Driver implementation: this method shall be implemented if setting scale
2140  * is supported.
2141  *
2142  * @param dfScale scale
2143  * @param eStorageType Data type to which create the potential attribute that will store
2144  *                     the scale. Added in GDAL 3.3
2145  *                     If let to its GDT_Unknown value, the implementation will decide
2146  *                     automatically the data type.
2147  *                     Note that changing the data type after initial setting might not be
2148  *                     supported.
2149  * @return true in case of success.
2150  */
SetScale(CPL_UNUSED double dfScale,CPL_UNUSED GDALDataType eStorageType)2151 bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
2152                            CPL_UNUSED GDALDataType eStorageType)
2153 {
2154     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
2155     return false;
2156 }
2157 
2158 /************************************************************************/
2159 /*                               SetOffset)                             */
2160 /************************************************************************/
2161 
2162 /** Set the offset value to apply to raw values.
2163  *
2164  * unscaled_value = raw_value * GetScale() + GetOffset()
2165  *
2166  * This is the same as the C function GDALMDArraySetOffset() / GDALMDArraySetOffsetEx().
2167  *
2168  * @note Driver implementation: this method shall be implemented if setting offset
2169  * is supported.
2170  *
2171  * @param dfOffset Offset
2172  * @param eStorageType Data type to which create the potential attribute that will store
2173  *                     the offset. Added in GDAL 3.3
2174  *                     If let to its GDT_Unknown value, the implementation will decide
2175  *                     automatically the data type.
2176  *                     Note that changing the data type after initial setting might not be
2177  *                     supported.
2178  * @return true in case of success.
2179  */
SetOffset(CPL_UNUSED double dfOffset,CPL_UNUSED GDALDataType eStorageType)2180 bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
2181                             CPL_UNUSED GDALDataType eStorageType)
2182 {
2183     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
2184     return false;
2185 }
2186 
2187 /************************************************************************/
2188 /*                               GetScale()                             */
2189 /************************************************************************/
2190 
2191 /** Get the scale value to apply to raw values.
2192  *
2193  * unscaled_value = raw_value * GetScale() + GetOffset()
2194  *
2195  * This is the same as the C function GDALMDArrayGetScale().
2196  *
2197  * @note Driver implementation: this method shall be implemented if gettings scale
2198  * is supported.
2199  *
2200  * @param pbHasScale Pointer to a output boolean that will be set to true if
2201  * a scale value exists. Might be nullptr.
2202  * @param peStorageType Pointer to a output GDALDataType that will be set to
2203  * the storage type of the scale value, when known/relevant. Otherwise will be
2204  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2205  *
2206  * @return the scale value. A 1.0 value might also indicate the
2207  * absence of a scale value.
2208  */
GetScale(CPL_UNUSED bool * pbHasScale,CPL_UNUSED GDALDataType * peStorageType) const2209 double GDALMDArray::GetScale(CPL_UNUSED bool* pbHasScale,
2210                              CPL_UNUSED GDALDataType* peStorageType) const
2211 {
2212     if( pbHasScale )
2213         *pbHasScale = false;
2214     return 1.0;
2215 }
2216 
2217 /************************************************************************/
2218 /*                               GetOffset()                            */
2219 /************************************************************************/
2220 
2221 /** Get the offset value to apply to raw values.
2222  *
2223  * unscaled_value = raw_value * GetScale() + GetOffset()
2224  *
2225  * This is the same as the C function GDALMDArrayGetOffset().
2226  *
2227  * @note Driver implementation: this method shall be implemented if gettings offset
2228  * is supported.
2229  *
2230  * @param pbHasOffset Pointer to a output boolean that will be set to true if
2231  * a offset value exists. Might be nullptr.
2232  * @param peStorageType Pointer to a output GDALDataType that will be set to
2233  * the storage type of the offset value, when known/relevant. Otherwise will be
2234  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
2235  *
2236  * @return the offset value. A 0.0 value might also indicate the
2237  * absence of a offset value.
2238  */
GetOffset(CPL_UNUSED bool * pbHasOffset,CPL_UNUSED GDALDataType * peStorageType) const2239 double GDALMDArray::GetOffset(CPL_UNUSED bool* pbHasOffset,
2240                               CPL_UNUSED GDALDataType* peStorageType) const
2241 {
2242     if( pbHasOffset )
2243         *pbHasOffset = false;
2244     return 0.0;
2245 }
2246 
2247 /************************************************************************/
2248 /*                         ProcessPerChunk()                            */
2249 /************************************************************************/
2250 
2251 namespace
2252 {
2253     enum class Caller
2254     {
2255         CALLER_END_OF_LOOP,
2256         CALLER_IN_LOOP,
2257     };
2258 }
2259 
2260 /** \brief Call a user-provided function to operate on an array chunk by chunk.
2261  *
2262  * This method is to be used when doing operations on an array, or a subset of it,
2263  * in a chunk by chunk way.
2264  *
2265  * @param arrayStartIdx Values representing the starting index to use
2266  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
2267  *                      Array of GetDimensionCount() values. Must not be
2268  *                      nullptr, unless for a zero-dimensional array.
2269  *
2270  * @param count         Values representing the number of values to use in
2271  *                      each dimension.
2272  *                      Array of GetDimensionCount() values. Must not be
2273  *                      nullptr, unless for a zero-dimensional array.
2274  *
2275  * @param chunkSize     Values representing the chunk size in each dimension.
2276  *                      Might typically the output of GetProcessingChunkSize().
2277  *                      Array of GetDimensionCount() values. Must not be
2278  *                      nullptr, unless for a zero-dimensional array.
2279  *
2280  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
2281  *                      Must NOT be nullptr.
2282  *
2283  * @param pUserData     Pointer to pass as the value of the pUserData argument of
2284  *                      FuncProcessPerChunkType. Might be nullptr (depends on
2285  *                      pfnFunc.
2286  *
2287  * @return true in case of success.
2288  */
ProcessPerChunk(const GUInt64 * arrayStartIdx,const GUInt64 * count,const size_t * chunkSize,FuncProcessPerChunkType pfnFunc,void * pUserData)2289 bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64* arrayStartIdx,
2290                                           const GUInt64* count,
2291                                           const size_t* chunkSize,
2292                                           FuncProcessPerChunkType pfnFunc,
2293                                           void* pUserData)
2294 {
2295     const auto& dims = GetDimensions();
2296     if( dims.empty() )
2297     {
2298         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
2299     }
2300 
2301     // Sanity check
2302     size_t nTotalChunkSize = 1;
2303     for( size_t i = 0; i < dims.size(); i++ )
2304     {
2305         const auto nSizeThisDim(dims[i]->GetSize());
2306         if( count[i] == 0 ||
2307             count[i] > nSizeThisDim ||
2308             arrayStartIdx[i] > nSizeThisDim - count[i] )
2309         {
2310             CPLError(CE_Failure, CPLE_AppDefined,
2311                      "Inconsistent arrayStartIdx[] / count[] values "
2312                      "regarding array size");
2313             return false;
2314         }
2315         if( chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
2316             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize )
2317         {
2318             CPLError(CE_Failure, CPLE_AppDefined,
2319                      "Inconsistent chunkSize[] values");
2320             return false;
2321         }
2322         nTotalChunkSize *= chunkSize[i];
2323     }
2324 
2325     size_t dimIdx = 0;
2326     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
2327     std::vector<size_t> chunkCount(dims.size());
2328     struct Stack
2329     {
2330         GUInt64 nBlockCounter = 0;
2331         GUInt64 nBlocksMinusOne = 0;
2332         size_t  first_count = 0; // only used if nBlocks > 1
2333         Caller  return_point = Caller::CALLER_END_OF_LOOP;
2334     };
2335     std::vector<Stack> stack(dims.size());
2336     GUInt64 iCurChunk = 0;
2337     GUInt64 nChunkCount = 1;
2338     for( size_t i = 0; i < dims.size(); i++ )
2339     {
2340         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
2341         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
2342         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
2343         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
2344         if( stack[i].nBlocksMinusOne == 0 )
2345         {
2346             chunkArrayStartIdx[i] = arrayStartIdx[i];
2347             chunkCount[i] = static_cast<size_t>(count[i]);
2348         }
2349         else
2350         {
2351             stack[i].first_count = static_cast<size_t>(
2352                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
2353         }
2354     }
2355 
2356 lbl_next_depth:
2357     if( dimIdx == dims.size() )
2358     {
2359         ++ iCurChunk;
2360         if( !pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
2361                      iCurChunk, nChunkCount, pUserData) )
2362         {
2363             return false;
2364         }
2365     }
2366     else
2367     {
2368         if( stack[dimIdx].nBlocksMinusOne != 0 )
2369         {
2370             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
2371             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
2372             chunkCount[dimIdx] = stack[dimIdx].first_count;
2373             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
2374             while(true)
2375             {
2376                 dimIdx ++;
2377                 goto lbl_next_depth;
2378 lbl_return_to_caller_in_loop:
2379                 -- stack[dimIdx].nBlockCounter;
2380                 if( stack[dimIdx].nBlockCounter == 0 )
2381                     break;
2382                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
2383                 chunkCount[dimIdx] = chunkSize[dimIdx];
2384             }
2385 
2386             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
2387             chunkCount[dimIdx] = static_cast<size_t>(
2388                 arrayStartIdx[dimIdx] + count[dimIdx] - chunkArrayStartIdx[dimIdx]);
2389             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
2390         }
2391         dimIdx ++;
2392         goto lbl_next_depth;
2393 lbl_return_to_caller_end_of_loop:
2394         if( dimIdx == 0 )
2395             goto end;
2396     }
2397 
2398     dimIdx --;
2399     // cppcheck-suppress negativeContainerIndex
2400     switch( stack[dimIdx].return_point )
2401     {
2402         case Caller::CALLER_END_OF_LOOP: goto lbl_return_to_caller_end_of_loop;
2403         case Caller::CALLER_IN_LOOP:     goto lbl_return_to_caller_in_loop;
2404     }
2405 end:
2406     return true;
2407 }
2408 
2409 /************************************************************************/
2410 /*                          GDALAttribute()                             */
2411 /************************************************************************/
2412 
2413 //! @cond Doxygen_Suppress
GDALAttribute(CPL_UNUSED const std::string & osParentName,CPL_UNUSED const std::string & osName)2414 GDALAttribute::GDALAttribute(CPL_UNUSED const std::string& osParentName,
2415                              CPL_UNUSED const std::string& osName)
2416 #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
2417     : GDALAbstractMDArray(osParentName, osName)
2418 #endif
2419 {}
2420 //! @endcond
2421 
2422 /************************************************************************/
2423 /*                        GetDimensionSize()                            */
2424 /************************************************************************/
2425 
2426 /** Return the size of the dimensions of the attribute.
2427  *
2428  * This will be an empty array for a scalar (single value) attribute.
2429  *
2430  * This is the same as the C function GDALAttributeGetDimensionsSize().
2431  */
GetDimensionsSize() const2432 std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
2433 {
2434     const auto& dims = GetDimensions();
2435     std::vector<GUInt64> ret;
2436     ret.reserve(dims.size());
2437     for( const auto& dim: dims )
2438         ret.push_back(dim->GetSize());
2439     return ret;
2440 }
2441 
2442 /************************************************************************/
2443 /*                            GDALRawResult()                           */
2444 /************************************************************************/
2445 
2446 //! @cond Doxygen_Suppress
GDALRawResult(GByte * raw,const GDALExtendedDataType & dt,size_t nEltCount)2447 GDALRawResult::GDALRawResult(GByte* raw,
2448                   const GDALExtendedDataType& dt,
2449                   size_t nEltCount):
2450     m_dt(dt),
2451     m_nEltCount(nEltCount),
2452     m_nSize(nEltCount * dt.GetSize()),
2453     m_raw(raw)
2454 {
2455 }
2456 //! @endcond
2457 
2458 /************************************************************************/
2459 /*                            GDALRawResult()                           */
2460 /************************************************************************/
2461 
2462 /** Move constructor. */
GDALRawResult(GDALRawResult && other)2463 GDALRawResult::GDALRawResult(GDALRawResult&& other):
2464     m_dt(std::move(other.m_dt)),
2465     m_nEltCount(other.m_nEltCount),
2466     m_nSize(other.m_nSize),
2467     m_raw(other.m_raw)
2468 {
2469     other.m_nEltCount = 0;
2470     other.m_nSize = 0;
2471     other.m_raw = nullptr;
2472 }
2473 
2474 /************************************************************************/
2475 /*                               FreeMe()                               */
2476 /************************************************************************/
2477 
FreeMe()2478 void GDALRawResult::FreeMe()
2479 {
2480     if( m_raw && m_dt.NeedsFreeDynamicMemory() )
2481     {
2482         GByte* pabyPtr = m_raw;
2483         const auto nDTSize(m_dt.GetSize());
2484         for( size_t i = 0; i < m_nEltCount; ++i )
2485         {
2486             m_dt.FreeDynamicMemory(pabyPtr);
2487             pabyPtr += nDTSize;
2488         }
2489     }
2490     VSIFree(m_raw);
2491 }
2492 
2493 /************************************************************************/
2494 /*                             operator=()                              */
2495 /************************************************************************/
2496 
2497 /** Move assignment. */
operator =(GDALRawResult && other)2498 GDALRawResult& GDALRawResult::operator=(GDALRawResult&& other)
2499 {
2500     FreeMe();
2501     m_dt = std::move(other.m_dt);
2502     m_nEltCount = other.m_nEltCount;
2503     m_nSize = other.m_nSize;
2504     m_raw = other.m_raw;
2505     other.m_nEltCount = 0;
2506     other.m_nSize = 0;
2507     other.m_raw = nullptr;
2508     return *this;
2509 }
2510 
2511 /************************************************************************/
2512 /*                         ~GDALRawResult()                             */
2513 /************************************************************************/
2514 
2515 /** Destructor. */
~GDALRawResult()2516 GDALRawResult::~GDALRawResult()
2517 {
2518     FreeMe();
2519 }
2520 
2521 /************************************************************************/
2522 /*                            StealData()                               */
2523 /************************************************************************/
2524 
2525 //! @cond Doxygen_Suppress
2526 /** Return buffer to caller which becomes owner of it.
2527  * Only to be used by GDALAttributeReadAsRaw().
2528  */
StealData()2529 GByte* GDALRawResult::StealData()
2530 {
2531     GByte* ret = m_raw;
2532     m_raw = nullptr;
2533     m_nEltCount = 0;
2534     m_nSize = 0;
2535     return ret;
2536 }
2537 //! @endcond
2538 
2539 /************************************************************************/
2540 /*                             ReadAsRaw()                              */
2541 /************************************************************************/
2542 
2543 /** Return the raw value of an attribute.
2544  *
2545  *
2546  * This is the same as the C function GDALAttributeReadAsRaw().
2547  */
ReadAsRaw() const2548 GDALRawResult GDALAttribute::ReadAsRaw() const
2549 {
2550     const auto nEltCount(GetTotalElementsCount());
2551     const auto dt(GetDataType());
2552     const auto nDTSize(dt.GetSize());
2553     GByte* res = static_cast<GByte*>(
2554         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
2555     if( !res )
2556         return GDALRawResult(nullptr, dt, 0);
2557     const auto& dims = GetDimensions();
2558     const auto nDims = GetDimensionCount();
2559     std::vector<GUInt64> startIdx(1 + nDims, 0);
2560     std::vector<size_t> count(1 + nDims);
2561     for( size_t i = 0; i < nDims; i++ )
2562     {
2563         count[i] = static_cast<size_t>(dims[i]->GetSize());
2564     }
2565     if( !Read(startIdx.data(), count.data(), nullptr, nullptr,
2566               dt,
2567               &res[0], &res[0], static_cast<size_t>(nEltCount * nDTSize)) )
2568     {
2569         VSIFree(res);
2570         return GDALRawResult(nullptr, dt, 0);
2571     }
2572     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
2573 }
2574 
2575 /************************************************************************/
2576 /*                            ReadAsString()                            */
2577 /************************************************************************/
2578 
2579 /** Return the value of an attribute as a string.
2580  *
2581  * The returned string should not be freed, and its lifetime does not
2582  * excess a next call to ReadAsString() on the same object, or the deletion
2583  * of the object itself.
2584  *
2585  * This function will only return the first element if there are several.
2586  *
2587  * This is the same as the C function GDALAttributeReadAsString()
2588  *
2589  * @return a string, or nullptr.
2590  */
ReadAsString() const2591 const char* GDALAttribute::ReadAsString() const
2592 {
2593     const auto nDims = GetDimensionCount();
2594     std::vector<GUInt64> startIdx(1 + nDims, 0);
2595     std::vector<size_t> count(1 + nDims, 1);
2596     char* szRet = nullptr;
2597     if( !Read(startIdx.data(), count.data(), nullptr, nullptr,
2598          GDALExtendedDataType::CreateString(),
2599          &szRet, &szRet, sizeof(szRet)) ||
2600         szRet == nullptr )
2601     {
2602         return nullptr;
2603     }
2604     m_osCachedVal = szRet;
2605     CPLFree(szRet);
2606     return m_osCachedVal.c_str();
2607 }
2608 
2609 /************************************************************************/
2610 /*                            ReadAsInt()                               */
2611 /************************************************************************/
2612 
2613 /** Return the value of an attribute as a integer.
2614  *
2615  * This function will only return the first element if there are several.
2616  *
2617  * It can fail if its value can be converted to integer.
2618  *
2619  * This is the same as the C function GDALAttributeReadAsInt()
2620  *
2621  * @return a integer, or INT_MIN in case of error.
2622  */
ReadAsInt() const2623 int GDALAttribute::ReadAsInt() const
2624 {
2625     const auto nDims = GetDimensionCount();
2626     std::vector<GUInt64> startIdx(1 + nDims, 0);
2627     std::vector<size_t> count(1 + nDims, 1);
2628     int nRet = INT_MIN;
2629     Read(startIdx.data(), count.data(), nullptr, nullptr,
2630          GDALExtendedDataType::Create(GDT_Int32),
2631          &nRet, &nRet, sizeof(nRet));
2632     return nRet;
2633 }
2634 
2635 /************************************************************************/
2636 /*                            ReadAsDouble()                            */
2637 /************************************************************************/
2638 
2639 /** Return the value of an attribute as a double.
2640  *
2641  * This function will only return the first element if there are several.
2642  *
2643  * It can fail if its value can be converted to double.
2644  *
2645  * This is the same as the C function GDALAttributeReadAsInt()
2646  *
2647  * @return a double value.
2648  */
ReadAsDouble() const2649 double GDALAttribute::ReadAsDouble() const
2650 {
2651     const auto nDims = GetDimensionCount();
2652     std::vector<GUInt64> startIdx(1 + nDims, 0);
2653     std::vector<size_t> count(1 + nDims, 1);
2654     double dfRet = 0;
2655     Read(startIdx.data(), count.data(), nullptr, nullptr,
2656          GDALExtendedDataType::Create(GDT_Float64),
2657          &dfRet, &dfRet, sizeof(dfRet));
2658     return dfRet;
2659 }
2660 
2661 /************************************************************************/
2662 /*                          ReadAsStringArray()                         */
2663 /************************************************************************/
2664 
2665 /** Return the value of an attribute as an array of strings.
2666  *
2667  * This is the same as the C function GDALAttributeReadAsStringArray()
2668  */
ReadAsStringArray() const2669 CPLStringList GDALAttribute::ReadAsStringArray() const
2670 {
2671     const auto nElts = GetTotalElementsCount();
2672     if( nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1) )
2673         return CPLStringList();
2674     char** papszList = static_cast<char**>(
2675         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char*)));
2676     const auto& dims = GetDimensions();
2677     const auto nDims = GetDimensionCount();
2678     std::vector<GUInt64> startIdx(1 + nDims, 0);
2679     std::vector<size_t> count(1 + nDims);
2680     for( size_t i = 0; i < nDims; i++ )
2681     {
2682         count[i] = static_cast<size_t>(dims[i]->GetSize());
2683     }
2684     Read(startIdx.data(), count.data(), nullptr, nullptr,
2685          GDALExtendedDataType::CreateString(),
2686          papszList, papszList, sizeof(char*) * static_cast<int>(nElts));
2687     for( int i = 0; i < static_cast<int>(nElts); i++ )
2688     {
2689         if( papszList[i] == nullptr )
2690             papszList[i] = CPLStrdup("");
2691     }
2692     return CPLStringList(papszList);
2693 }
2694 
2695 /************************************************************************/
2696 /*                          ReadAsIntArray()                            */
2697 /************************************************************************/
2698 
2699 /** Return the value of an attribute as an array of integers.
2700  *
2701  * This is the same as the C function GDALAttributeReadAsIntArray().
2702  */
ReadAsIntArray() const2703 std::vector<int> GDALAttribute::ReadAsIntArray() const
2704 {
2705     const auto nElts = GetTotalElementsCount();
2706     if( nElts > static_cast<size_t>(nElts) )
2707         return {};
2708     std::vector<int> res(static_cast<size_t>(nElts));
2709     const auto& dims = GetDimensions();
2710     const auto nDims = GetDimensionCount();
2711     std::vector<GUInt64> startIdx(1 + nDims, 0);
2712     std::vector<size_t> count(1 + nDims);
2713     for( size_t i = 0; i < nDims; i++ )
2714     {
2715         count[i] = static_cast<size_t>(dims[i]->GetSize());
2716     }
2717     Read(startIdx.data(), count.data(), nullptr, nullptr,
2718          GDALExtendedDataType::Create(GDT_Int32),
2719          &res[0], res.data(), res.size() * sizeof(res[0]));
2720     return res;
2721 }
2722 
2723 /************************************************************************/
2724 /*                         ReadAsDoubleArray()                          */
2725 /************************************************************************/
2726 
2727 /** Return the value of an attribute as an array of double.
2728  *
2729  * This is the same as the C function GDALAttributeReadAsDoubleArray().
2730  */
ReadAsDoubleArray() const2731 std::vector<double> GDALAttribute::ReadAsDoubleArray() const
2732 {
2733     const auto nElts = GetTotalElementsCount();
2734     if( nElts > static_cast<size_t>(nElts) )
2735         return {};
2736     std::vector<double> res(static_cast<size_t>(nElts));
2737     const auto& dims = GetDimensions();
2738     const auto nDims = GetDimensionCount();
2739     std::vector<GUInt64> startIdx(1 + nDims, 0);
2740     std::vector<size_t> count(1 + nDims);
2741     for( size_t i = 0; i < nDims; i++ )
2742     {
2743         count[i] = static_cast<size_t>(dims[i]->GetSize());
2744     }
2745     Read(startIdx.data(), count.data(), nullptr, nullptr,
2746          GDALExtendedDataType::Create(GDT_Float64),
2747          &res[0], res.data(), res.size() * sizeof(res[0]));
2748     return res;
2749 }
2750 
2751 /************************************************************************/
2752 /*                               Write()                                */
2753 /************************************************************************/
2754 
2755 /** Write an attribute from raw values expressed in GetDataType()
2756  *
2757  * The values should be provided in the type of GetDataType() and there should
2758  * be exactly GetTotalElementsCount() of them.
2759  * If GetDataType() is a string, each value should be a char* pointer.
2760  *
2761  * This is the same as the C function GDALAttributeWriteRaw().
2762  *
2763  * @param pabyValue Buffer of nLen bytes.
2764  * @param nLen Size of pabyValue in bytes. Should be equal to
2765  *             GetTotalElementsCount() * GetDataType().GetSize()
2766  * @return true in case of success.
2767  */
Write(const void * pabyValue,size_t nLen)2768 bool GDALAttribute::Write(const void* pabyValue, size_t nLen)
2769 {
2770     if( nLen != GetTotalElementsCount() * GetDataType().GetSize() )
2771     {
2772         CPLError(CE_Failure, CPLE_AppDefined,
2773                  "Length is not of expected value");
2774         return false;
2775     }
2776     const auto& dims = GetDimensions();
2777     const auto nDims = GetDimensionCount();
2778     std::vector<GUInt64> startIdx(1 + nDims, 0);
2779     std::vector<size_t> count(1 + nDims);
2780     for( size_t i = 0; i < nDims; i++ )
2781     {
2782         count[i] = static_cast<size_t>(dims[i]->GetSize());
2783     }
2784     return Write(startIdx.data(), count.data(), nullptr, nullptr,
2785                  GetDataType(),
2786                  pabyValue, pabyValue, nLen);
2787 }
2788 
2789 /************************************************************************/
2790 /*                               Write()                                */
2791 /************************************************************************/
2792 
2793 /** Write an attribute from a string value.
2794  *
2795  * Type conversion will be performed if needed. If the attribute contains
2796  * multiple values, only the first one will be updated.
2797  *
2798  * This is the same as the C function GDALAttributeWriteString().
2799  *
2800  * @param pszValue Pointer to a string.
2801  * @return true in case of success.
2802  */
Write(const char * pszValue)2803 bool GDALAttribute::Write(const char* pszValue)
2804 {
2805     const auto nDims = GetDimensionCount();
2806     std::vector<GUInt64> startIdx(1 + nDims, 0);
2807     std::vector<size_t> count(1 + nDims, 1);
2808     return Write(startIdx.data(), count.data(), nullptr, nullptr,
2809                  GDALExtendedDataType::CreateString(),
2810                  &pszValue, &pszValue, sizeof(pszValue));
2811 }
2812 
2813 /************************************************************************/
2814 /*                              WriteInt()                              */
2815 /************************************************************************/
2816 
2817 /** Write an attribute from a integer value.
2818  *
2819  * Type conversion will be performed if needed. If the attribute contains
2820  * multiple values, only the first one will be updated.
2821  *
2822  * This is the same as the C function GDALAttributeWriteInt().
2823  *
2824  * @param nVal Value.
2825  * @return true in case of success.
2826  */
WriteInt(int nVal)2827 bool GDALAttribute::WriteInt(int nVal)
2828 {
2829     const auto nDims = GetDimensionCount();
2830     std::vector<GUInt64> startIdx(1 + nDims, 0);
2831     std::vector<size_t> count(1 + nDims, 1);
2832     return Write(startIdx.data(), count.data(), nullptr, nullptr,
2833                  GDALExtendedDataType::Create(GDT_Int32),
2834                  &nVal, &nVal, sizeof(nVal));
2835 }
2836 
2837 /************************************************************************/
2838 /*                                Write()                               */
2839 /************************************************************************/
2840 
2841 /** Write an attribute from a double value.
2842  *
2843  * Type conversion will be performed if needed. If the attribute contains
2844  * multiple values, only the first one will be updated.
2845  *
2846  * This is the same as the C function GDALAttributeWriteDouble().
2847  *
2848  * @param dfVal Value.
2849  * @return true in case of success.
2850  */
Write(double dfVal)2851 bool GDALAttribute::Write(double dfVal)
2852 {
2853     const auto nDims = GetDimensionCount();
2854     std::vector<GUInt64> startIdx(1 + nDims, 0);
2855     std::vector<size_t> count(1 + nDims, 1);
2856     return Write(startIdx.data(), count.data(), nullptr, nullptr,
2857                  GDALExtendedDataType::Create(GDT_Float64),
2858                  &dfVal, &dfVal, sizeof(dfVal));
2859 }
2860 
2861 /************************************************************************/
2862 /*                                Write()                               */
2863 /************************************************************************/
2864 
2865 /** Write an attribute from an array of strings.
2866  *
2867  * Type conversion will be performed if needed.
2868  *
2869  * Exactly GetTotalElementsCount() strings must be provided
2870  *
2871  * This is the same as the C function GDALAttributeWriteStringArray().
2872  *
2873  * @param vals Array of strings.
2874  * @return true in case of success.
2875  */
Write(CSLConstList vals)2876 bool GDALAttribute::Write(CSLConstList vals)
2877 {
2878     if( static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount() )
2879     {
2880         CPLError(CE_Failure, CPLE_AppDefined,
2881                  "Invalid number of input values");
2882         return false;
2883     }
2884     const auto nDims = GetDimensionCount();
2885     std::vector<GUInt64> startIdx(1 + nDims, 0);
2886     std::vector<size_t> count(1 + nDims);
2887     const auto& dims = GetDimensions();
2888     for( size_t i = 0; i < nDims; i++ )
2889         count[i] = static_cast<size_t>(dims[i]->GetSize());
2890     return Write(startIdx.data(), count.data(), nullptr, nullptr,
2891                  GDALExtendedDataType::CreateString(),
2892                  vals, vals,
2893                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char*));
2894 }
2895 
2896 /************************************************************************/
2897 /*                                Write()                               */
2898 /************************************************************************/
2899 
2900 /** Write an attribute from an array of double.
2901  *
2902  * Type conversion will be performed if needed.
2903  *
2904  * Exactly GetTotalElementsCount() strings must be provided
2905  *
2906  * This is the same as the C function GDALAttributeWriteDoubleArray()
2907  *
2908  * @param vals Array of double.
2909  * @param nVals Should be equal to GetTotalElementsCount().
2910  * @return true in case of success.
2911  */
Write(const double * vals,size_t nVals)2912 bool GDALAttribute::Write(const double *vals, size_t nVals)
2913 {
2914     if( nVals != GetTotalElementsCount() )
2915     {
2916         CPLError(CE_Failure, CPLE_AppDefined,
2917                  "Invalid number of input values");
2918         return false;
2919     }
2920     const auto nDims = GetDimensionCount();
2921     std::vector<GUInt64> startIdx(1 + nDims, 0);
2922     std::vector<size_t> count(1 + nDims);
2923     const auto& dims = GetDimensions();
2924     for( size_t i = 0; i < nDims; i++ )
2925         count[i] = static_cast<size_t>(dims[i]->GetSize());
2926     return Write(startIdx.data(), count.data(), nullptr, nullptr,
2927                  GDALExtendedDataType::Create(GDT_Float64),
2928                  vals, vals,
2929                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
2930 }
2931 
2932 /************************************************************************/
2933 /*                           GDALMDArray()                              */
2934 /************************************************************************/
2935 
2936 //! @cond Doxygen_Suppress
GDALMDArray(CPL_UNUSED const std::string & osParentName,CPL_UNUSED const std::string & osName)2937 GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
2938                          CPL_UNUSED const std::string& osName)
2939 #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
2940     : GDALAbstractMDArray(osParentName, osName)
2941 #endif
2942 {}
2943 //! @endcond
2944 
2945 /************************************************************************/
2946 /*                           GetTotalCopyCost()                         */
2947 /************************************************************************/
2948 
2949 /** Return a total "cost" to copy the array.
2950  *
2951  * Used as a parameter for CopyFrom()
2952  */
GetTotalCopyCost() const2953 GUInt64 GDALMDArray::GetTotalCopyCost() const
2954 {
2955     return COPY_COST +
2956                 GetAttributes().size() * GDALAttribute::COPY_COST +
2957            GetTotalElementsCount() * GetDataType().GetSize();
2958 }
2959 
2960 
2961 /************************************************************************/
2962 /*                       CopyFromAllExceptValues()                      */
2963 /************************************************************************/
2964 
2965 //! @cond Doxygen_Suppress
2966 
CopyFromAllExceptValues(const GDALMDArray * poSrcArray,bool bStrict,GUInt64 & nCurCost,const GUInt64 nTotalCost,GDALProgressFunc pfnProgress,void * pProgressData)2967 bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray* poSrcArray,
2968                                           bool bStrict,
2969                                           GUInt64& nCurCost,
2970                                           const GUInt64 nTotalCost,
2971                                           GDALProgressFunc pfnProgress,
2972                                           void * pProgressData)
2973 {
2974     const bool bThisIsUnscaledArray =
2975         dynamic_cast<GDALMDArrayUnscaled*>(this) != nullptr;
2976     auto attrs = poSrcArray->GetAttributes();
2977     for( const auto& attr: attrs )
2978     {
2979         const auto& osAttrName = attr->GetName();
2980         if( bThisIsUnscaledArray )
2981         {
2982             if( osAttrName == "missing_value" ||
2983                 osAttrName == "_FillValue" ||
2984                 osAttrName == "valid_min" ||
2985                 osAttrName == "valid_max" ||
2986                 osAttrName == "valid_range" )
2987             {
2988                 continue;
2989             }
2990         }
2991 
2992         auto dstAttr = CreateAttribute(osAttrName,
2993                                        attr->GetDimensionsSize(),
2994                                        attr->GetDataType());
2995         if( !dstAttr )
2996         {
2997             if( bStrict )
2998                 return false;
2999             continue;
3000         }
3001         auto raw = attr->ReadAsRaw();
3002         if( !dstAttr->Write(raw.data(), raw.size()) && bStrict )
3003             return false;
3004     }
3005     if( !attrs.empty() )
3006     {
3007         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
3008         if( pfnProgress &&
3009             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData) )
3010             return false;
3011     }
3012 
3013     auto srcSRS = poSrcArray->GetSpatialRef();
3014     if( srcSRS )
3015     {
3016         SetSpatialRef(srcSRS.get());
3017     }
3018 
3019     const void* pNoData = poSrcArray->GetRawNoDataValue();
3020     if( pNoData && poSrcArray->GetDataType() == GetDataType() )
3021     {
3022         SetRawNoDataValue(pNoData);
3023     }
3024 
3025     const std::string& osUnit(poSrcArray->GetUnit());
3026     if( !osUnit.empty() )
3027     {
3028         SetUnit(osUnit);
3029     }
3030 
3031     bool bGotValue = false;
3032     GDALDataType eOffsetStorageType = GDT_Unknown;
3033     const double dfOffset = poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
3034     if( bGotValue )
3035     {
3036         SetOffset(dfOffset, eOffsetStorageType);
3037     }
3038 
3039     bGotValue = false;
3040     GDALDataType eScaleStorageType = GDT_Unknown;
3041     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
3042     if( bGotValue )
3043     {
3044         SetScale(dfScale, eScaleStorageType);
3045     }
3046 
3047     return true;
3048 }
3049 
3050 //! @endcond
3051 
3052 /************************************************************************/
3053 /*                               CopyFrom()                             */
3054 /************************************************************************/
3055 
3056 /** Copy the content of an array into a new (generally empty) array.
3057  *
3058  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
3059  *                   of some output drivers this is not recommended)
3060  * @param poSrcArray Source array. Should NOT be nullptr.
3061  * @param bStrict Whether to enable stict mode. In strict mode, any error will
3062  *                stop the copy. In relaxed mode, the copy will be attempted to
3063  *                be pursued.
3064  * @param nCurCost  Should be provided as a variable initially set to 0.
3065  * @param nTotalCost Total cost from GetTotalCopyCost().
3066  * @param pfnProgress Progress callback, or nullptr.
3067  * @param pProgressData Progress user data, or nulptr.
3068  *
3069  * @return true in case of success (or partial success if bStrict == false).
3070  */
CopyFrom(CPL_UNUSED GDALDataset * poSrcDS,const GDALMDArray * poSrcArray,bool bStrict,GUInt64 & nCurCost,const GUInt64 nTotalCost,GDALProgressFunc pfnProgress,void * pProgressData)3071 bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset* poSrcDS,
3072                            const GDALMDArray* poSrcArray,
3073                            bool bStrict,
3074                            GUInt64& nCurCost,
3075                            const GUInt64 nTotalCost,
3076                            GDALProgressFunc pfnProgress,
3077                            void * pProgressData)
3078 {
3079     if( pfnProgress == nullptr )
3080         pfnProgress = GDALDummyProgress;
3081 
3082     nCurCost += GDALMDArray::COPY_COST;
3083 
3084     if( !CopyFromAllExceptValues(poSrcArray, bStrict,
3085                                  nCurCost, nTotalCost,
3086                                  pfnProgress, pProgressData) )
3087     {
3088         return false;
3089     }
3090 
3091     const auto& dims = poSrcArray->GetDimensions();
3092     const auto nDTSize = poSrcArray->GetDataType().GetSize();
3093     if( dims.empty() )
3094     {
3095         std::vector<GByte> abyTmp(nDTSize);
3096         if( !(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
3097                               GetDataType(), &abyTmp[0]) &&
3098               Write(nullptr, nullptr, nullptr, nullptr,
3099                                GetDataType(), &abyTmp[0])) &&
3100             bStrict )
3101         {
3102             return false;
3103         }
3104         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
3105         if( !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData) )
3106             return false;
3107     }
3108     else
3109     {
3110         std::vector<GUInt64> arrayStartIdx(dims.size());
3111         std::vector<GUInt64> count(dims.size());
3112         for( size_t i = 0; i < dims.size(); i++ )
3113         {
3114             count[i] = static_cast<size_t>(dims[i]->GetSize());
3115         }
3116 
3117         struct CopyFunc
3118         {
3119             GDALMDArray* poDstArray = nullptr;
3120             std::vector<GByte> abyTmp{};
3121             GDALProgressFunc pfnProgress = nullptr;
3122             void* pProgressData = nullptr;
3123             GUInt64 nCurCost = 0;
3124             GUInt64 nTotalCost = 0;
3125             GUInt64 nTotalBytesThisArray = 0;
3126             bool bStop = false;
3127 
3128             static bool f(GDALAbstractMDArray* l_poSrcArray,
3129                           const GUInt64* chunkArrayStartIdx,
3130                           const size_t* chunkCount,
3131                           GUInt64 iCurChunk,
3132                           GUInt64 nChunkCount,
3133                           void* pUserData)
3134             {
3135                 const auto dt(l_poSrcArray->GetDataType());
3136                 auto data = static_cast<CopyFunc*>(pUserData);
3137                 auto poDstArray = data->poDstArray;
3138                 if( !l_poSrcArray->Read(chunkArrayStartIdx,
3139                                       chunkCount,
3140                                       nullptr, nullptr,
3141                                       dt,
3142                                       &data->abyTmp[0]) )
3143                 {
3144                     return false;
3145                 }
3146                 bool bRet =
3147                     poDstArray->Write(chunkArrayStartIdx,
3148                                        chunkCount,
3149                                        nullptr, nullptr,
3150                                        dt,
3151                                        &data->abyTmp[0]);
3152                 if( dt.NeedsFreeDynamicMemory() )
3153                 {
3154                     const auto l_nDTSize = dt.GetSize();
3155                     GByte* ptr = &data->abyTmp[0];
3156                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
3157                     size_t nEltCount = 1;
3158                     for( size_t i = 0; i < l_nDims; ++i )
3159                     {
3160                         nEltCount *= chunkCount[i];
3161                     }
3162                     for( size_t i = 0; i < nEltCount; i++ )
3163                     {
3164                         dt.FreeDynamicMemory(ptr);
3165                         ptr += l_nDTSize;
3166                     }
3167                 }
3168                 if( !bRet )
3169                 {
3170                     return false;
3171                 }
3172 
3173                 double dfCurCost = double(data->nCurCost) +
3174                     double(iCurChunk) / nChunkCount * data->nTotalBytesThisArray;
3175                 if( !data->pfnProgress(dfCurCost / data->nTotalCost, "",
3176                                        data->pProgressData) )
3177                 {
3178                     data->bStop = true;
3179                     return false;
3180                 }
3181 
3182                 return true;
3183             }
3184         };
3185 
3186         CopyFunc copyFunc;
3187         copyFunc.poDstArray = this;
3188         copyFunc.nCurCost = nCurCost;
3189         copyFunc.nTotalCost = nTotalCost;
3190         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
3191         copyFunc.pfnProgress = pfnProgress;
3192         copyFunc.pProgressData = pProgressData;
3193         const char* pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
3194         const size_t nMaxChunkSize = pszSwathSize ?
3195             static_cast<size_t>(
3196                 std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
3197                          CPLAtoGIntBig(pszSwathSize))) :
3198             static_cast<size_t>(
3199                 std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
3200                          GDALGetCacheMax64() / 4));
3201         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
3202         size_t nRealChunkSize = nDTSize;
3203         for( const auto& nChunkSize: anChunkSizes )
3204         {
3205             nRealChunkSize *= nChunkSize;
3206         }
3207         try
3208         {
3209             copyFunc.abyTmp.resize(nRealChunkSize);
3210         }
3211         catch( const std::exception& )
3212         {
3213             CPLError(CE_Failure, CPLE_OutOfMemory,
3214                         "Cannot allocate temporary buffer");
3215             nCurCost += copyFunc.nTotalBytesThisArray;
3216             return false;
3217         }
3218         if( copyFunc.nTotalBytesThisArray != 0 &&
3219             !const_cast<GDALMDArray*>(poSrcArray)->
3220                 ProcessPerChunk(arrayStartIdx.data(), count.data(),
3221                                 anChunkSizes.data(),
3222                                 CopyFunc::f, &copyFunc) &&
3223             (bStrict || copyFunc.bStop) )
3224         {
3225             nCurCost += copyFunc.nTotalBytesThisArray;
3226             return false;
3227         }
3228         nCurCost += copyFunc.nTotalBytesThisArray;
3229     }
3230 
3231     return true;
3232 }
3233 
3234 /************************************************************************/
3235 /*                         GetStructuralInfo()                          */
3236 /************************************************************************/
3237 
3238 /** Return structural information on the array.
3239  *
3240  * This may be the compression, etc..
3241  *
3242  * The return value should not be freed and is valid until GDALMDArray is
3243  * released or this function called again.
3244  *
3245  * This is the same as the C function GDALMDArrayGetStruturalInfo().
3246  */
GetStructuralInfo() const3247 CSLConstList GDALMDArray::GetStructuralInfo() const
3248 {
3249     return nullptr;
3250 }
3251 
3252 /************************************************************************/
3253 /*                          AdviseRead()                                */
3254 /************************************************************************/
3255 
3256 /** Advise driver of upcoming read requests.
3257  *
3258  * Some GDAL drivers operate more efficiently if they know in advance what
3259  * set of upcoming read requests will be made.  The AdviseRead() method allows
3260  * an application to notify the driver of the region of interest.
3261  *
3262  * Many drivers just ignore the AdviseRead() call, but it can dramatically
3263  * accelerate access via some drivers. One such case is when reading through
3264  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
3265  * with the region of interest defined by AdviseRead())
3266  *
3267  * This is the same as the C function GDALMDArrayAdviseRead().
3268  *
3269  * @param arrayStartIdx Values representing the starting index to read
3270  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
3271  *                      Array of GetDimensionCount() values.
3272  *                      Can be nullptr as a synonymous for [0 for i in range(GetDimensionCount() ]
3273  *
3274  * @param count         Values representing the number of values to extract in
3275  *                      each dimension.
3276  *                      Array of GetDimensionCount() values.
3277  *                      Can be nullptr as a synonymous for
3278  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in range(GetDimensionCount() ]
3279  * @return true in case of success (ignoring the advice is a success)
3280  *
3281  * @since GDAL 3.2
3282  */
AdviseRead(const GUInt64 * arrayStartIdx,const size_t * count) const3283 bool GDALMDArray::AdviseRead(const GUInt64* arrayStartIdx,
3284                              const size_t* count) const
3285 {
3286     const auto nDimCount = GetDimensionCount();
3287     if( nDimCount == 0 )
3288         return true;
3289 
3290     std::vector<GUInt64> tmp_arrayStartIdx;
3291     if( arrayStartIdx == nullptr )
3292     {
3293         tmp_arrayStartIdx.resize(nDimCount);
3294         arrayStartIdx = tmp_arrayStartIdx.data();
3295     }
3296 
3297     std::vector<size_t> tmp_count;
3298     if( count == nullptr )
3299     {
3300         tmp_count.resize(nDimCount);
3301         const auto& dims = GetDimensions();
3302         for( size_t i = 0; i < nDimCount; i++ )
3303         {
3304             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
3305 #if SIZEOF_VOIDP < 8
3306             if( nSize != static_cast<size_t>(nSize) )
3307             {
3308                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
3309                 return false;
3310             }
3311 #endif
3312             tmp_count[i] = static_cast<size_t>(nSize);
3313         }
3314         count = tmp_count.data();
3315     }
3316 
3317     std::vector<GInt64> tmp_arrayStep;
3318     std::vector<GPtrDiff_t> tmp_bufferStride;
3319     const GInt64* arrayStep = nullptr;
3320     const GPtrDiff_t* bufferStride = nullptr;
3321     if( !CheckReadWriteParams(arrayStartIdx,
3322                               count,
3323                               arrayStep,
3324                               bufferStride,
3325                               GDALExtendedDataType::Create(GDT_Unknown),
3326                               nullptr,
3327                               nullptr,
3328                               0,
3329                               tmp_arrayStep,
3330                               tmp_bufferStride) )
3331     {
3332         return false;
3333     }
3334 
3335     return IAdviseRead(arrayStartIdx, count);
3336 }
3337 
3338 /************************************************************************/
3339 /*                             IAdviseRead()                            */
3340 /************************************************************************/
3341 
3342 //! @cond Doxygen_Suppress
IAdviseRead(const GUInt64 *,const size_t *) const3343 bool GDALMDArray::IAdviseRead(const GUInt64*, const size_t*) const
3344 {
3345     return true;
3346 }
3347 //! @endcond
3348 
3349 /************************************************************************/
3350 /*                       GDALSlicedMDArray                              */
3351 /************************************************************************/
3352 
3353 class GDALSlicedMDArray final: public GDALMDArray
3354 {
3355 private:
3356     std::shared_ptr<GDALMDArray> m_poParent{};
3357     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
3358     std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
3359     std::vector<Range> m_parentRanges{} ; // of size m_poParent->GetDimensionCount()
3360 
3361     mutable std::vector<GUInt64> m_parentStart;
3362     mutable std::vector<size_t> m_parentCount;
3363     mutable std::vector<GInt64> m_parentStep;
3364     mutable std::vector<GPtrDiff_t> m_parentStride;
3365 
3366     void PrepareParentArrays(const GUInt64* arrayStartIdx,
3367                              const size_t* count,
3368                              const GInt64* arrayStep,
3369                              const GPtrDiff_t* bufferStride) const;
3370 
3371 protected:
GDALSlicedMDArray(const std::shared_ptr<GDALMDArray> & poParent,const std::string & viewExpr,std::vector<std::shared_ptr<GDALDimension>> && dims,std::vector<size_t> && mapDimIdxToParentDimIdx,std::vector<Range> && parentRanges)3372     explicit GDALSlicedMDArray(
3373         const std::shared_ptr<GDALMDArray>& poParent,
3374         const std::string& viewExpr,
3375         std::vector<std::shared_ptr<GDALDimension>>&& dims,
3376         std::vector<size_t>&& mapDimIdxToParentDimIdx,
3377         std::vector<Range>&& parentRanges)
3378     :
3379         GDALAbstractMDArray(std::string(), "Sliced view of " + poParent->GetFullName() + " (" + viewExpr + ")"),
3380         GDALMDArray(std::string(), "Sliced view of " + poParent->GetFullName() + " (" + viewExpr + ")"),
3381         m_poParent(std::move(poParent)),
3382         m_dims(std::move(dims)),
3383         m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
3384         m_parentRanges(parentRanges),
3385         m_parentStart(m_poParent->GetDimensionCount()),
3386         m_parentCount(m_poParent->GetDimensionCount(), 1),
3387         m_parentStep(m_poParent->GetDimensionCount()),
3388         m_parentStride(m_poParent->GetDimensionCount())
3389     {
3390     }
3391 
3392     bool IRead(const GUInt64* arrayStartIdx,
3393                       const size_t* count,
3394                       const GInt64* arrayStep,
3395                       const GPtrDiff_t* bufferStride,
3396                       const GDALExtendedDataType& bufferDataType,
3397                       void* pDstBuffer) const override;
3398 
3399     bool IWrite(const GUInt64* arrayStartIdx,
3400                       const size_t* count,
3401                       const GInt64* arrayStep,
3402                       const GPtrDiff_t* bufferStride,
3403                       const GDALExtendedDataType& bufferDataType,
3404                       const void* pSrcBuffer) override;
3405 
3406     bool IAdviseRead(const GUInt64* arrayStartIdx,
3407                      const size_t* count) const override;
3408 
3409 public:
Create(const std::shared_ptr<GDALMDArray> & poParent,const std::string & viewExpr,std::vector<std::shared_ptr<GDALDimension>> && dims,std::vector<size_t> && mapDimIdxToParentDimIdx,std::vector<Range> && parentRanges)3410     static std::shared_ptr<GDALSlicedMDArray> Create(
3411                     const std::shared_ptr<GDALMDArray>& poParent,
3412                     const std::string& viewExpr,
3413                     std::vector<std::shared_ptr<GDALDimension>>&& dims,
3414                     std::vector<size_t>&& mapDimIdxToParentDimIdx,
3415                     std::vector<Range>&& parentRanges)
3416     {
3417         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
3418         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
3419 
3420         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
3421             poParent, viewExpr, std::move(dims), std::move(mapDimIdxToParentDimIdx),
3422             std::move(parentRanges))));
3423         newAr->SetSelf(newAr);
3424         return newAr;
3425     }
3426 
IsWritable() const3427     bool IsWritable() const override { return m_poParent->IsWritable(); }
3428 
GetDimensions() const3429     const std::vector<std::shared_ptr<GDALDimension>>& GetDimensions() const override { return m_dims; }
3430 
GetDataType() const3431     const GDALExtendedDataType &GetDataType() const override { return m_poParent->GetDataType(); }
3432 
GetUnit() const3433     const std::string& GetUnit() const override { return m_poParent->GetUnit(); }
3434 
3435     // bool SetUnit(const std::string& osUnit) override  { return m_poParent->SetUnit(osUnit); }
3436 
GetSpatialRef() const3437     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
3438     {
3439         auto poSrcSRS = m_poParent->GetSpatialRef();
3440         if( !poSrcSRS )
3441             return nullptr;
3442         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
3443         std::vector<int> dstMapping;
3444         for( int srcAxis: srcMapping )
3445         {
3446             bool bFound = false;
3447             for( size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++ )
3448             {
3449                 if( static_cast<int>(m_mapDimIdxToParentDimIdx[i]) == srcAxis - 1 )
3450                 {
3451                     dstMapping.push_back(static_cast<int>(i) + 1);
3452                     bFound = true;
3453                     break;
3454                 }
3455             }
3456             if( !bFound )
3457             {
3458                 dstMapping.push_back(0);
3459             }
3460         }
3461         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
3462         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
3463         return poClone;
3464     }
3465 
GetRawNoDataValue() const3466     const void* GetRawNoDataValue() const override { return m_poParent->GetRawNoDataValue(); }
3467 
3468     // bool SetRawNoDataValue(const void* pRawNoData) override { return m_poParent->SetRawNoDataValue(pRawNoData); }
3469 
GetOffset(bool * pbHasOffset,GDALDataType * peStorageType) const3470     double GetOffset(bool* pbHasOffset, GDALDataType* peStorageType) const override { return m_poParent->GetOffset(pbHasOffset, peStorageType); }
3471 
GetScale(bool * pbHasScale,GDALDataType * peStorageType) const3472     double GetScale(bool* pbHasScale, GDALDataType* peStorageType) const override { return m_poParent->GetScale(pbHasScale, peStorageType); }
3473 
3474     // bool SetOffset(double dfOffset) override { return m_poParent->SetOffset(dfOffset); }
3475 
3476     // bool SetScale(double dfScale) override { return m_poParent->SetScale(dfScale); }
3477 
GetBlockSize() const3478     std::vector<GUInt64> GetBlockSize() const override
3479     {
3480         std::vector<GUInt64> ret(GetDimensionCount());
3481         const auto parentBlockSize(m_poParent->GetBlockSize());
3482         for( size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i )
3483         {
3484             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
3485             if( iOldAxis != static_cast<size_t>(-1) )
3486             {
3487                 ret[i] = parentBlockSize[iOldAxis];
3488             }
3489         }
3490         return ret;
3491     }
3492 
GetAttribute(const std::string & osName) const3493     std::shared_ptr<GDALAttribute> GetAttribute(const std::string& osName) const override
3494         { return m_poParent->GetAttribute(osName); }
3495 
GetAttributes(CSLConstList papszOptions=nullptr) const3496     std::vector<std::shared_ptr<GDALAttribute>> GetAttributes(CSLConstList papszOptions = nullptr) const override
3497         { return m_poParent->GetAttributes(papszOptions); }
3498 };
3499 
3500 /************************************************************************/
3501 /*                        PrepareParentArrays()                         */
3502 /************************************************************************/
3503 
PrepareParentArrays(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride) const3504 void GDALSlicedMDArray::PrepareParentArrays(const GUInt64* arrayStartIdx,
3505                                             const size_t* count,
3506                                             const GInt64* arrayStep,
3507                                             const GPtrDiff_t* bufferStride) const
3508 {
3509     const size_t nParentDimCount = m_parentRanges.size();
3510     for( size_t i = 0; i < nParentDimCount; i++ )
3511     {
3512         // For dimensions in parent that have no existence in sliced array
3513         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
3514     }
3515 
3516     for( size_t i = 0; i < m_dims.size(); i++ )
3517     {
3518         const auto iParent = m_mapDimIdxToParentDimIdx[i];
3519         if( iParent != static_cast<size_t>(-1) )
3520         {
3521             m_parentStart[iParent] =
3522                 m_parentRanges[iParent].m_nIncr >= 0 ?
3523                     m_parentRanges[iParent].m_nStartIdx +
3524                         arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr :
3525                     m_parentRanges[iParent].m_nStartIdx -
3526                         arrayStartIdx[i] *
3527                         static_cast<GUInt64>(-m_parentRanges[iParent].m_nIncr);
3528             m_parentCount[iParent] = count[i];
3529             if( arrayStep )
3530             {
3531                 m_parentStep[iParent] = count[i] == 1 ? 1 :
3532                             // other checks should have ensured this does not
3533                             // overflow
3534                             arrayStep[i] * m_parentRanges[iParent].m_nIncr;
3535             }
3536             if( bufferStride )
3537             {
3538                 m_parentStride[iParent] = bufferStride[i];
3539             }
3540         }
3541     }
3542 }
3543 
3544 /************************************************************************/
3545 /*                             IRead()                                  */
3546 /************************************************************************/
3547 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const3548 bool GDALSlicedMDArray::IRead(const GUInt64* arrayStartIdx,
3549                               const size_t* count,
3550                               const GInt64* arrayStep,
3551                               const GPtrDiff_t* bufferStride,
3552                               const GDALExtendedDataType& bufferDataType,
3553                               void* pDstBuffer) const
3554 {
3555     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
3556     return m_poParent->Read(m_parentStart.data(),
3557                             m_parentCount.data(),
3558                             m_parentStep.data(),
3559                             m_parentStride.data(),
3560                             bufferDataType,
3561                             pDstBuffer);
3562 }
3563 
3564 /************************************************************************/
3565 /*                             IWrite()                                  */
3566 /************************************************************************/
3567 
IWrite(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,const void * pSrcBuffer)3568 bool GDALSlicedMDArray::IWrite(const GUInt64* arrayStartIdx,
3569                               const size_t* count,
3570                               const GInt64* arrayStep,
3571                               const GPtrDiff_t* bufferStride,
3572                               const GDALExtendedDataType& bufferDataType,
3573                               const void* pSrcBuffer)
3574 {
3575     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
3576     return m_poParent->Write(m_parentStart.data(),
3577                              m_parentCount.data(),
3578                              m_parentStep.data(),
3579                              m_parentStride.data(),
3580                              bufferDataType,
3581                              pSrcBuffer);
3582 }
3583 
3584 /************************************************************************/
3585 /*                             IAdviseRead()                            */
3586 /************************************************************************/
3587 
IAdviseRead(const GUInt64 * arrayStartIdx,const size_t * count) const3588 bool GDALSlicedMDArray::IAdviseRead(const GUInt64* arrayStartIdx,
3589                                     const size_t* count) const
3590 {
3591     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
3592     return m_poParent->AdviseRead(m_parentStart.data(),
3593                                   m_parentCount.data());
3594 }
3595 
3596 /************************************************************************/
3597 /*                        CreateSlicedArray()                           */
3598 /************************************************************************/
3599 
CreateSlicedArray(const std::shared_ptr<GDALMDArray> & self,const std::string & viewExpr,const std::string & activeSlice,bool bRenameDimensions,std::vector<GDALMDArray::ViewSpec> & viewSpecs)3600 static std::shared_ptr<GDALMDArray> CreateSlicedArray(
3601                                 const std::shared_ptr<GDALMDArray>& self,
3602                                 const std::string& viewExpr,
3603                                 const std::string& activeSlice,
3604                                 bool bRenameDimensions,
3605                                 std::vector<GDALMDArray::ViewSpec>& viewSpecs)
3606 {
3607     const auto& srcDims(self->GetDimensions());
3608     if( srcDims.empty() )
3609     {
3610         CPLError(CE_Failure, CPLE_AppDefined,
3611                     "Cannot slice a 0-d array");
3612         return nullptr;
3613     }
3614 
3615     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
3616     const auto nTokens = static_cast<size_t>(aosTokens.size());
3617 
3618     std::vector<std::shared_ptr<GDALDimension>> newDims;
3619     std::vector<size_t> mapDimIdxToParentDimIdx;
3620     std::vector<GDALSlicedMDArray::Range> parentRanges;
3621     newDims.reserve(nTokens);
3622     mapDimIdxToParentDimIdx.reserve(nTokens);
3623     parentRanges.reserve(nTokens);
3624 
3625     bool bGotEllipsis = false;
3626     size_t nCurSrcDim = 0;
3627     for( size_t i = 0; i < nTokens; i++ )
3628     {
3629         const char* pszIdxSpec = aosTokens[i];
3630         if( EQUAL(pszIdxSpec, "...") )
3631         {
3632             if( bGotEllipsis )
3633             {
3634                 CPLError(CE_Failure, CPLE_AppDefined,
3635                             "Only one single ellipsis is supported");
3636                 return nullptr;
3637             }
3638             bGotEllipsis = true;
3639             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
3640             for( size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++ )
3641             {
3642                 parentRanges.emplace_back(0, 1);
3643                 newDims.push_back(srcDims[nCurSrcDim]);
3644                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
3645             }
3646             continue;
3647         }
3648         else if( EQUAL(pszIdxSpec, "newaxis") ||
3649                     EQUAL(pszIdxSpec, "np.newaxis") )
3650         {
3651             newDims.push_back(std::make_shared<GDALDimension>(
3652                 std::string(),
3653                 "newaxis",
3654                 std::string(),
3655                 std::string(),
3656                 1));
3657             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
3658             continue;
3659         }
3660         else if( CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER )
3661         {
3662             if( nCurSrcDim >= srcDims.size() )
3663             {
3664                 CPLError(CE_Failure, CPLE_AppDefined,
3665                         "Too many values in %s", activeSlice.c_str());
3666                 return nullptr;
3667             }
3668 
3669             auto nVal = CPLAtoGIntBig(pszIdxSpec);
3670             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
3671             if( (nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
3672                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)) )
3673             {
3674                 CPLError(CE_Failure, CPLE_AppDefined,
3675                             "Index " CPL_FRMT_GIB " is out of bounds", nVal);
3676                 return nullptr;
3677             }
3678             if( nVal < 0 )
3679                 nVal += nDimSize;
3680             parentRanges.emplace_back(nVal, 0);
3681         }
3682         else
3683         {
3684             if( nCurSrcDim >= srcDims.size() )
3685             {
3686                 CPLError(CE_Failure, CPLE_AppDefined,
3687                         "Too many values in %s", activeSlice.c_str());
3688                 return nullptr;
3689             }
3690 
3691             CPLStringList aosRangeTokens(CSLTokenizeString2(
3692                             pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
3693             int nRangeTokens = aosRangeTokens.size();
3694             if( nRangeTokens > 3 )
3695             {
3696                 CPLError(CE_Failure, CPLE_AppDefined,
3697                             "Too many : in %s", pszIdxSpec);
3698                 return nullptr;
3699             }
3700             if( nRangeTokens <= 1 )
3701             {
3702                 CPLError(CE_Failure, CPLE_AppDefined,
3703                             "Invalid value %s", pszIdxSpec);
3704                 return nullptr;
3705             }
3706             const char* pszStart = aosRangeTokens[0];
3707             const char* pszEnd = aosRangeTokens[1];
3708             const char* pszInc = (nRangeTokens == 3) ? aosRangeTokens[2]: "";
3709             GDALSlicedMDArray::Range range;
3710             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
3711             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
3712             if( range.m_nIncr == 0 )
3713             {
3714                 CPLError(CE_Failure, CPLE_AppDefined,
3715                             "Invalid increment 0");
3716                 return nullptr;
3717             }
3718             auto startIdx(CPLAtoGIntBig(pszStart));
3719             if( startIdx < 0 )
3720             {
3721                 if( nDimSize < static_cast<GUInt64>(-startIdx) )
3722                     startIdx = 0;
3723                 else
3724                     startIdx = nDimSize + startIdx;
3725             }
3726             range.m_nStartIdx = startIdx;
3727             range.m_nStartIdx = EQUAL(pszStart, "") ?
3728                 (range.m_nIncr > 0 ? 0 : nDimSize-1) :
3729                 range.m_nStartIdx;
3730             if( range.m_nStartIdx >= nDimSize - 1)
3731                 range.m_nStartIdx = nDimSize - 1;
3732             auto endIdx(CPLAtoGIntBig(pszEnd));
3733             if( endIdx < 0 )
3734             {
3735                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
3736                 if( nDimSize < positiveEndIdx )
3737                     endIdx = 0;
3738                 else
3739                     endIdx = nDimSize - positiveEndIdx;
3740             }
3741             GUInt64 nEndIdx = endIdx;
3742             nEndIdx = EQUAL(pszEnd, "") ?
3743                 (range.m_nIncr < 0 ? 0 : nDimSize) :
3744                     nEndIdx;
3745             if( (range.m_nIncr > 0 && range.m_nStartIdx >= nEndIdx) ||
3746                 (range.m_nIncr < 0 && range.m_nStartIdx <= nEndIdx) )
3747             {
3748                 CPLError(CE_Failure, CPLE_AppDefined,
3749                             "Output dimension of size 0 is not allowed");
3750                 return nullptr;
3751             }
3752             int inc = (EQUAL(pszEnd, "") && range.m_nIncr < 0) ? 1 : 0;
3753             const GUInt64 newSize = range.m_nIncr > 0 ?
3754                 (nEndIdx - range.m_nStartIdx) / range.m_nIncr +
3755                     (((inc + nEndIdx - range.m_nStartIdx) % range.m_nIncr) ? 1 : 0):
3756                 (inc + range.m_nStartIdx - nEndIdx) / -range.m_nIncr +
3757                     (((inc + range.m_nStartIdx - nEndIdx) % -range.m_nIncr) ? 1 : 0);
3758             if( range.m_nStartIdx == 0 &&
3759                 range.m_nIncr == 1 &&
3760                 newSize == srcDims[nCurSrcDim]->GetSize() )
3761             {
3762                 newDims.push_back(srcDims[nCurSrcDim]);
3763             }
3764             else
3765             {
3766                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
3767                 if( bRenameDimensions )
3768                 {
3769                     osNewDimName =
3770                         "subset_" + srcDims[nCurSrcDim]->GetName() +
3771                             CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB "_" CPL_FRMT_GUIB,
3772                                     static_cast<GUIntBig>(range.m_nStartIdx),
3773                                     static_cast<GIntBig>(range.m_nIncr),
3774                                     static_cast<GUIntBig>(newSize));
3775                 }
3776                 newDims.push_back(std::make_shared<GDALDimension>(
3777                     std::string(),
3778                     osNewDimName,
3779                     srcDims[nCurSrcDim]->GetType(),
3780                     range.m_nIncr > 0 ?
3781                         srcDims[nCurSrcDim]->GetDirection():
3782                         std::string(),
3783                     newSize));
3784             }
3785             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
3786             parentRanges.emplace_back(range);
3787         }
3788 
3789         nCurSrcDim++;
3790     }
3791     for( ; nCurSrcDim < srcDims.size(); nCurSrcDim++ )
3792     {
3793         parentRanges.emplace_back(0, 1);
3794         newDims.push_back(srcDims[nCurSrcDim]);
3795         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
3796     }
3797 
3798     GDALMDArray::ViewSpec viewSpec;
3799     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
3800     viewSpec.m_parentRanges = parentRanges;
3801     viewSpecs.emplace_back(std::move(viewSpec));
3802 
3803     return GDALSlicedMDArray::Create(self,
3804                                      viewExpr,
3805                                      std::move(newDims),
3806                                      std::move(mapDimIdxToParentDimIdx),
3807                                      std::move(parentRanges));
3808 }
3809 
3810 /************************************************************************/
3811 /*                       GDALExtractFieldMDArray                        */
3812 /************************************************************************/
3813 
3814 class GDALExtractFieldMDArray final: public GDALMDArray
3815 {
3816 private:
3817     std::shared_ptr<GDALMDArray> m_poParent{};
3818     GDALExtendedDataType m_dt;
3819     std::string m_srcCompName;
3820     mutable std::vector<GByte> m_pabyNoData{};
3821 
3822 protected:
GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> & poParent,const std::string & fieldName,const std::unique_ptr<GDALEDTComponent> & srcComp)3823     GDALExtractFieldMDArray(
3824         const std::shared_ptr<GDALMDArray>& poParent,
3825         const std::string& fieldName,
3826         const std::unique_ptr<GDALEDTComponent>& srcComp)
3827     :
3828         GDALAbstractMDArray(
3829             std::string(), "Extract field " + fieldName + " of " + poParent->GetFullName()),
3830         GDALMDArray(
3831             std::string(), "Extract field " + fieldName + " of " + poParent->GetFullName()),
3832         m_poParent(poParent),
3833         m_dt(srcComp->GetType()),
3834         m_srcCompName(srcComp->GetName())
3835     {
3836         m_pabyNoData.resize(m_dt.GetSize());
3837     }
3838 
3839     bool IRead(const GUInt64* arrayStartIdx,
3840                       const size_t* count,
3841                       const GInt64* arrayStep,
3842                       const GPtrDiff_t* bufferStride,
3843                       const GDALExtendedDataType& bufferDataType,
3844                       void* pDstBuffer) const override;
3845 
IAdviseRead(const GUInt64 * arrayStartIdx,const size_t * count) const3846     bool IAdviseRead(const GUInt64* arrayStartIdx,
3847                      const size_t* count) const override
3848         { return m_poParent->AdviseRead(arrayStartIdx, count); }
3849 
3850 public:
Create(const std::shared_ptr<GDALMDArray> & poParent,const std::string & fieldName,const std::unique_ptr<GDALEDTComponent> & srcComp)3851     static std::shared_ptr<GDALExtractFieldMDArray> Create(
3852                     const std::shared_ptr<GDALMDArray>& poParent,
3853                     const std::string& fieldName,
3854                     const std::unique_ptr<GDALEDTComponent>& srcComp)
3855     {
3856         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
3857             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
3858         newAr->SetSelf(newAr);
3859         return newAr;
3860     }
~GDALExtractFieldMDArray()3861     ~GDALExtractFieldMDArray()
3862     {
3863         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
3864     }
3865 
IsWritable() const3866     bool IsWritable() const override { return m_poParent->IsWritable(); }
3867 
GetDimensions() const3868     const std::vector<std::shared_ptr<GDALDimension>>& GetDimensions() const override
3869     { return m_poParent->GetDimensions(); }
3870 
GetDataType() const3871     const GDALExtendedDataType &GetDataType() const override { return m_dt; }
3872 
GetUnit() const3873     const std::string& GetUnit() const override { return m_poParent->GetUnit(); }
3874 
GetSpatialRef() const3875     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override { return m_poParent->GetSpatialRef(); }
3876 
GetRawNoDataValue() const3877     const void* GetRawNoDataValue() const override
3878     {
3879         const void* parentNoData = m_poParent->GetRawNoDataValue();
3880         if( parentNoData == nullptr )
3881             return nullptr;
3882 
3883         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
3884         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
3885 
3886         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
3887         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
3888             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
3889         auto tmpDT(GDALExtendedDataType::Create(std::string(),
3890                                                 m_dt.GetSize(),
3891                                                 std::move(comps)));
3892 
3893         GDALExtendedDataType::CopyValue(
3894             parentNoData, m_poParent->GetDataType(),
3895             &m_pabyNoData[0], tmpDT);
3896 
3897         return &m_pabyNoData[0];
3898     }
3899 
GetOffset(bool * pbHasOffset,GDALDataType * peStorageType) const3900     double GetOffset(bool* pbHasOffset, GDALDataType* peStorageType) const override { return m_poParent->GetOffset(pbHasOffset, peStorageType); }
3901 
GetScale(bool * pbHasScale,GDALDataType * peStorageType) const3902     double GetScale(bool* pbHasScale, GDALDataType* peStorageType) const override { return m_poParent->GetScale(pbHasScale, peStorageType); }
3903 
GetBlockSize() const3904     std::vector<GUInt64> GetBlockSize() const override { return m_poParent->GetBlockSize(); }
3905 };
3906 
3907 /************************************************************************/
3908 /*                             IRead()                                  */
3909 /************************************************************************/
3910 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const3911 bool GDALExtractFieldMDArray::IRead(const GUInt64* arrayStartIdx,
3912                               const size_t* count,
3913                               const GInt64* arrayStep,
3914                               const GPtrDiff_t* bufferStride,
3915                               const GDALExtendedDataType& bufferDataType,
3916                               void* pDstBuffer) const
3917 {
3918     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
3919     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
3920         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
3921     auto tmpDT(GDALExtendedDataType::Create(std::string(),
3922                                             bufferDataType.GetSize(),
3923                                             std::move(comps)));
3924 
3925     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
3926                             tmpDT, pDstBuffer);
3927 }
3928 
3929 /************************************************************************/
3930 /*                      CreateFieldNameExtractArray()                   */
3931 /************************************************************************/
3932 
CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> & self,const std::string & fieldName)3933 static std::shared_ptr<GDALMDArray> CreateFieldNameExtractArray(
3934                                 const std::shared_ptr<GDALMDArray>& self,
3935                                 const std::string& fieldName)
3936 {
3937     CPLAssert( self->GetDataType().GetClass() == GEDTC_COMPOUND );
3938     const std::unique_ptr<GDALEDTComponent>* srcComp = nullptr;
3939     for( const auto& comp: self->GetDataType().GetComponents() )
3940     {
3941         if( comp->GetName() == fieldName )
3942         {
3943             srcComp = &comp;
3944             break;
3945         }
3946     }
3947     if( srcComp == nullptr )
3948     {
3949         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
3950                  fieldName.c_str());
3951         return nullptr;
3952     }
3953     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
3954 }
3955 
3956 /************************************************************************/
3957 /*                             GetView()                                */
3958 /************************************************************************/
3959 
3960 /** Return a view of the array using slicing or field access.
3961  *
3962  * The slice expression uses the same syntax as NumPy basic slicing and
3963  * indexing. See
3964  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
3965  * Or it can use field access by name. See
3966  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
3967  *
3968  * Multiple [] bracket elements can be concatenated, with a slice expression
3969  * or field name inside each.
3970  *
3971  * For basic slicing and indexing, inside each [] bracket element, a list of
3972  * indexes that apply to successive source dimensions, can be specified, using
3973  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
3974  * or newaxis, using a comma separator.
3975  *
3976  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
3977  * <ul>
3978  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
3979  *     at index 1 in the first dimension, and index 2 in the second dimension
3980  *     from the source array. That is 5</li>
3981  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually implemented
3982  *     internally doing this intermediate slicing approach.</li>
3983  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
3984  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
3985  *     first dimension. That is [4,5,6,7].</li>
3986  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
3987  *     second dimension. That is [2,6].</li>
3988  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in the
3989  *     second dimension. That is [[2],[6]].</li>
3990  * <li>GetView("[::,2]"): Same as above.</li>
3991  * <li>GetView("[...,2]"): same as above, in that case, since the ellipsis only
3992  *     expands to one dimension here.</li>
3993  * <li>GetView("[:,::2]"): returns a 2-dimensional array, with even-indexed elements
3994  *     of the second dimension. That is [[0,2],[4,6]].</li>
3995  * <li>GetView("[:,1::2]"): returns a 2-dimensional array, with odd-indexed elements
3996  *     of the second dimension. That is [[1,3],[5,7]].</li>
3997  * <li>GetView("[:,1:3:]"): returns a 2-dimensional array, with elements of the
3998  *     second dimension with index in the range [1,3[. That is [[1,2],[5,6]].</li>
3999  * <li>GetView("[::-1,:]"): returns a 2-dimensional array, with the values in
4000  *     first dimension reversed. That is [[4,5,6,7],[0,1,2,3]].</li>
4001  * <li>GetView("[newaxis,...]"): returns a 3-dimensional array, with an addditional
4002  *     dimension of size 1 put at the beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
4003  * </ul>
4004  *
4005  * One difference with NumPy behavior is that ranges that would result in
4006  * zero elements are not allowed (dimensions of size 0 not being allowed in the
4007  * GDAL multidimensional model).
4008  *
4009  * For field access, the syntax to use is ["field_name"] or ['field_name'].
4010  * Multiple field specification is not supported currently.
4011  *
4012  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
4013  *
4014  * \note When using the GDAL Python bindings, natural Python syntax can be
4015  * used. That is ar[0,::,1]["foo"] will be internally translated to
4016  * ar.GetView("[0,::,1]['foo']")
4017  * \note When using the C++ API and integer indexing only, you may use the
4018  * at(idx0, idx1, ...) method.
4019  *
4020  * The returned array holds a reference to the original one, and thus is
4021  * a view of it (not a copy). If the content of the original array changes,
4022  * the content of the view array too. When using basic slicing and indexing,
4023  * the view can be written if the underlying array is writable.
4024  *
4025  * This is the same as the C function GDALMDArrayGetView()
4026  *
4027  * @param viewExpr Expression expressing basic slicing and indexing, or field access.
4028  * @return a new array, that holds a reference to the original one, and thus is
4029  * a view of it (not a copy), or nullptr in case of error.
4030  */
GetView(const std::string & viewExpr) const4031 std::shared_ptr<GDALMDArray> GDALMDArray::GetView(const std::string& viewExpr) const
4032 {
4033     std::vector<ViewSpec> viewSpecs;
4034     return GetView(viewExpr, true, viewSpecs);
4035 }
4036 
4037 //! @cond Doxygen_Suppress
GetView(const std::string & viewExpr,bool bRenameDimensions,std::vector<ViewSpec> & viewSpecs) const4038 std::shared_ptr<GDALMDArray> GDALMDArray::GetView(const std::string& viewExpr,
4039                                                   bool bRenameDimensions,
4040                                                   std::vector<ViewSpec>& viewSpecs) const
4041 {
4042     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
4043     if( !self )
4044     {
4045         CPLError(CE_Failure, CPLE_AppDefined,
4046                 "Driver implementation issue: m_pSelf not set !");
4047         return nullptr;
4048     }
4049     std::string curExpr(viewExpr);
4050     while( true )
4051     {
4052         if( curExpr.empty() || curExpr[0] != '[' )
4053         {
4054             CPLError(CE_Failure, CPLE_AppDefined,
4055                     "Slice string should start with ['");
4056             return nullptr;
4057         }
4058 
4059         std::string fieldName;
4060         size_t endExpr;
4061         if( curExpr.size() > 2 &&
4062             (curExpr[1] == '"' || curExpr[1] == '\'') )
4063         {
4064             if( self->GetDataType().GetClass() != GEDTC_COMPOUND )
4065             {
4066                 CPLError(CE_Failure, CPLE_AppDefined,
4067                          "Field access not allowed on non-compound data type");
4068                 return nullptr;
4069             }
4070             size_t idx = 2;
4071             for(; idx < curExpr.size(); idx++ )
4072             {
4073                 const char ch = curExpr[idx];
4074                 if( ch == curExpr[1] )
4075                     break;
4076                 if( ch == '\\' && idx + 1 < curExpr.size() )
4077                 {
4078                     fieldName += curExpr[idx+1];
4079                     idx++;
4080                 }
4081                 else
4082                 {
4083                     fieldName += ch;
4084                 }
4085             }
4086             if( idx + 1 >= curExpr.size() ||
4087                 curExpr[idx+1] != ']' )
4088             {
4089                 CPLError(CE_Failure, CPLE_AppDefined,
4090                          "Invalid field access specification");
4091                 return nullptr;
4092             }
4093             endExpr = idx + 1;
4094         }
4095         else
4096         {
4097             endExpr = curExpr.find(']');
4098         }
4099         if( endExpr == std::string::npos )
4100         {
4101             CPLError(CE_Failure, CPLE_AppDefined,
4102                     "Missing ]'");
4103             return nullptr;
4104         }
4105         if( endExpr == 1 )
4106         {
4107             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
4108             return nullptr;
4109         }
4110         std::string activeSlice(curExpr.substr(1, endExpr-1));
4111 
4112         if( !fieldName.empty() )
4113         {
4114             ViewSpec viewSpec;
4115             viewSpec.m_osFieldName = fieldName;
4116             viewSpecs.emplace_back(std::move(viewSpec));
4117         }
4118 
4119         auto newArray = !fieldName.empty() ?
4120             CreateFieldNameExtractArray(self, fieldName):
4121             CreateSlicedArray(self, viewExpr, activeSlice, bRenameDimensions, viewSpecs);
4122 
4123         if( endExpr == curExpr.size() - 1 )
4124         {
4125             return newArray;
4126         }
4127         self = std::move(newArray);
4128         curExpr = curExpr.substr(endExpr+1);
4129     }
4130 }
4131 //! @endcond
4132 
GetView(const std::vector<GUInt64> & indices) const4133 std::shared_ptr<GDALMDArray> GDALMDArray::GetView(const std::vector<GUInt64>& indices) const
4134 {
4135     std::string osExpr("[");
4136     bool bFirst = true;
4137     for(const auto& idx: indices )
4138     {
4139         if( !bFirst )
4140             osExpr += ',';
4141         bFirst = false;
4142         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
4143     }
4144     return GetView(osExpr + ']');
4145 }
4146 
4147 /************************************************************************/
4148 /*                            operator[]                                */
4149 /************************************************************************/
4150 
4151 /** Return a view of the array using field access
4152  *
4153  * Equivalent of GetView("['fieldName']")
4154  *
4155  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
4156  */
operator [](const std::string & fieldName) const4157 std::shared_ptr<GDALMDArray> GDALMDArray::operator[](const std::string& fieldName) const
4158 {
4159     return GetView(CPLSPrintf("['%s']",
4160         CPLString(fieldName).replaceAll('\\', "\\\\").
4161                              replaceAll('\'', "\\\'").c_str()));
4162 }
4163 
4164 /************************************************************************/
4165 /*                      GDALMDArrayTransposed                           */
4166 /************************************************************************/
4167 
4168 class GDALMDArrayTransposed final: public GDALMDArray
4169 {
4170 private:
4171     std::shared_ptr<GDALMDArray> m_poParent{};
4172     std::vector<int> m_anMapNewAxisToOldAxis{};
4173     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
4174 
4175     mutable std::vector<GUInt64> m_parentStart;
4176     mutable std::vector<size_t> m_parentCount;
4177     mutable std::vector<GInt64> m_parentStep;
4178     mutable std::vector<GPtrDiff_t> m_parentStride;
4179 
4180     void PrepareParentArrays(const GUInt64* arrayStartIdx,
4181                              const size_t* count,
4182                              const GInt64* arrayStep,
4183                              const GPtrDiff_t* bufferStride) const;
4184 
MappingToStr(const std::vector<int> & anMapNewAxisToOldAxis)4185     static std::string MappingToStr(const std::vector<int>& anMapNewAxisToOldAxis)
4186     {
4187         std::string ret;
4188         ret += '[';
4189         for( size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i )
4190         {
4191             if( i > 0 )
4192                 ret += ',';
4193             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
4194         }
4195         ret += ']';
4196         return ret;
4197     }
4198 
4199 protected:
GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> & poParent,const std::vector<int> & anMapNewAxisToOldAxis,std::vector<std::shared_ptr<GDALDimension>> && dims)4200     GDALMDArrayTransposed(
4201         const std::shared_ptr<GDALMDArray>& poParent,
4202         const std::vector<int>& anMapNewAxisToOldAxis,
4203         std::vector<std::shared_ptr<GDALDimension>>&& dims)
4204     :
4205         GDALAbstractMDArray(std::string(), "Transposed view of " + poParent->GetFullName() + " along " + MappingToStr(anMapNewAxisToOldAxis)),
4206         GDALMDArray(std::string(), "Transposed view of " + poParent->GetFullName() + " along " + MappingToStr(anMapNewAxisToOldAxis)),
4207         m_poParent(std::move(poParent)),
4208         m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
4209         m_dims(std::move(dims)),
4210         m_parentStart(m_poParent->GetDimensionCount()),
4211         m_parentCount(m_poParent->GetDimensionCount()),
4212         m_parentStep(m_poParent->GetDimensionCount()),
4213         m_parentStride(m_poParent->GetDimensionCount())
4214     {
4215     }
4216 
4217     bool IRead(const GUInt64* arrayStartIdx,
4218                       const size_t* count,
4219                       const GInt64* arrayStep,
4220                       const GPtrDiff_t* bufferStride,
4221                       const GDALExtendedDataType& bufferDataType,
4222                       void* pDstBuffer) const override;
4223 
4224     bool IWrite(const GUInt64* arrayStartIdx,
4225                       const size_t* count,
4226                       const GInt64* arrayStep,
4227                       const GPtrDiff_t* bufferStride,
4228                       const GDALExtendedDataType& bufferDataType,
4229                       const void* pSrcBuffer) override;
4230 
4231     bool IAdviseRead(const GUInt64* arrayStartIdx,
4232                      const size_t* count) const override;
4233 
4234 public:
Create(const std::shared_ptr<GDALMDArray> & poParent,const std::vector<int> & anMapNewAxisToOldAxis)4235     static std::shared_ptr<GDALMDArrayTransposed> Create(
4236                     const std::shared_ptr<GDALMDArray>& poParent,
4237                     const std::vector<int>& anMapNewAxisToOldAxis)
4238     {
4239         const auto& parentDims(poParent->GetDimensions());
4240         std::vector<std::shared_ptr<GDALDimension>> dims;
4241         for( const auto iOldAxis: anMapNewAxisToOldAxis )
4242         {
4243             if( iOldAxis < 0 )
4244             {
4245                 dims.push_back(std::make_shared<GDALDimension>(
4246                                     std::string(),
4247                                     "newaxis",
4248                                     std::string(),
4249                                     std::string(),
4250                                     1));
4251             }
4252             else
4253             {
4254                 dims.emplace_back(parentDims[iOldAxis]);
4255             }
4256         }
4257 
4258         auto newAr(std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
4259             poParent, anMapNewAxisToOldAxis, std::move(dims))));
4260         newAr->SetSelf(newAr);
4261         return newAr;
4262     }
4263 
IsWritable() const4264     bool IsWritable() const override { return m_poParent->IsWritable(); }
4265 
GetDimensions() const4266     const std::vector<std::shared_ptr<GDALDimension>>& GetDimensions() const override { return m_dims; }
4267 
GetDataType() const4268     const GDALExtendedDataType &GetDataType() const override { return m_poParent->GetDataType(); }
4269 
GetUnit() const4270     const std::string& GetUnit() const override { return m_poParent->GetUnit(); }
4271 
GetSpatialRef() const4272     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
4273     {
4274         auto poSrcSRS = m_poParent->GetSpatialRef();
4275         if( !poSrcSRS )
4276             return nullptr;
4277         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
4278         std::vector<int> dstMapping;
4279         for( int srcAxis: srcMapping )
4280         {
4281             bool bFound = false;
4282             for( size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++ )
4283             {
4284                 if( m_anMapNewAxisToOldAxis[i] == srcAxis - 1 )
4285                 {
4286                     dstMapping.push_back(static_cast<int>(i) + 1);
4287                     bFound = true;
4288                     break;
4289                 }
4290             }
4291             if( !bFound )
4292             {
4293                 dstMapping.push_back(0);
4294             }
4295         }
4296         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
4297         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
4298         return poClone;
4299     }
4300 
GetRawNoDataValue() const4301     const void* GetRawNoDataValue() const override { return m_poParent->GetRawNoDataValue(); }
4302 
4303     // bool SetRawNoDataValue(const void* pRawNoData) override { return m_poParent->SetRawNoDataValue(pRawNoData); }
4304 
GetOffset(bool * pbHasOffset,GDALDataType * peStorageType) const4305     double GetOffset(bool* pbHasOffset, GDALDataType* peStorageType) const override { return m_poParent->GetOffset(pbHasOffset, peStorageType); }
4306 
GetScale(bool * pbHasScale,GDALDataType * peStorageType) const4307     double GetScale(bool* pbHasScale, GDALDataType* peStorageType) const override { return m_poParent->GetScale(pbHasScale, peStorageType); }
4308 
4309     // bool SetOffset(double dfOffset) override { return m_poParent->SetOffset(dfOffset); }
4310 
4311     // bool SetScale(double dfScale) override { return m_poParent->SetScale(dfScale); }
4312 
GetBlockSize() const4313     std::vector<GUInt64> GetBlockSize() const override
4314     {
4315         std::vector<GUInt64> ret(GetDimensionCount());
4316         const auto parentBlockSize(m_poParent->GetBlockSize());
4317         for( size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i )
4318         {
4319             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
4320             if( iOldAxis >= 0 )
4321             {
4322                 ret[i] = parentBlockSize[iOldAxis];
4323             }
4324         }
4325         return ret;
4326     }
4327 
GetAttribute(const std::string & osName) const4328     std::shared_ptr<GDALAttribute> GetAttribute(const std::string& osName) const override
4329         { return m_poParent->GetAttribute(osName); }
4330 
GetAttributes(CSLConstList papszOptions=nullptr) const4331     std::vector<std::shared_ptr<GDALAttribute>> GetAttributes(CSLConstList papszOptions = nullptr) const override
4332         { return m_poParent->GetAttributes(papszOptions); }
4333 };
4334 
4335 /************************************************************************/
4336 /*                         PrepareParentArrays()                        */
4337 /************************************************************************/
4338 
PrepareParentArrays(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride) const4339 void GDALMDArrayTransposed::PrepareParentArrays(const GUInt64* arrayStartIdx,
4340                                                 const size_t* count,
4341                                                 const GInt64* arrayStep,
4342                                                 const GPtrDiff_t* bufferStride) const
4343 {
4344     for( size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i )
4345     {
4346         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
4347         if( iOldAxis >= 0 )
4348         {
4349             m_parentStart[iOldAxis] = arrayStartIdx[i];
4350             m_parentCount[iOldAxis] = count[i];
4351             if( arrayStep )
4352             {
4353                 m_parentStep[iOldAxis] = arrayStep[i];
4354             }
4355             if( bufferStride )
4356             {
4357                 m_parentStride[iOldAxis] = bufferStride[i];
4358             }
4359         }
4360     }
4361 }
4362 
4363 /************************************************************************/
4364 /*                             IRead()                                  */
4365 /************************************************************************/
4366 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const4367 bool GDALMDArrayTransposed::IRead(const GUInt64* arrayStartIdx,
4368                               const size_t* count,
4369                               const GInt64* arrayStep,
4370                               const GPtrDiff_t* bufferStride,
4371                               const GDALExtendedDataType& bufferDataType,
4372                               void* pDstBuffer) const
4373 {
4374     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
4375     return m_poParent->Read(m_parentStart.data(),
4376                             m_parentCount.data(),
4377                             m_parentStep.data(),
4378                             m_parentStride.data(),
4379                             bufferDataType,
4380                             pDstBuffer);
4381 }
4382 
4383 /************************************************************************/
4384 /*                            IWrite()                                  */
4385 /************************************************************************/
4386 
IWrite(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,const void * pSrcBuffer)4387 bool GDALMDArrayTransposed::IWrite(const GUInt64* arrayStartIdx,
4388                               const size_t* count,
4389                               const GInt64* arrayStep,
4390                               const GPtrDiff_t* bufferStride,
4391                               const GDALExtendedDataType& bufferDataType,
4392                               const void* pSrcBuffer)
4393 {
4394     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
4395     return m_poParent->Write(m_parentStart.data(),
4396                              m_parentCount.data(),
4397                              m_parentStep.data(),
4398                              m_parentStride.data(),
4399                              bufferDataType,
4400                              pSrcBuffer);
4401 }
4402 
4403 /************************************************************************/
4404 /*                             IAdviseRead()                            */
4405 /************************************************************************/
4406 
IAdviseRead(const GUInt64 * arrayStartIdx,const size_t * count) const4407 bool GDALMDArrayTransposed::IAdviseRead(const GUInt64* arrayStartIdx,
4408                                         const size_t* count) const
4409 {
4410     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
4411     return m_poParent->AdviseRead(m_parentStart.data(),
4412                                   m_parentCount.data());
4413 }
4414 
4415 /************************************************************************/
4416 /*                           Transpose()                                */
4417 /************************************************************************/
4418 
4419 /** Return a view of the array whose axis have been reordered.
4420  *
4421  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
4422  * and GetDimensionCount() - 1, and each only once.
4423  * -1 can be used as a special index value to ask for the insertion of a new
4424  * axis of size 1.
4425  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
4426  * index of one of its dimension, it corresponds to the axis of index
4427  * anMapNewAxisToOldAxis[i] from the current array.
4428  *
4429  * This is similar to the numpy.transpose() method
4430  *
4431  * The returned array holds a reference to the original one, and thus is
4432  * a view of it (not a copy). If the content of the original array changes,
4433  * the content of the view array too. The view can be written if the underlying
4434  * array is writable.
4435  *
4436  * Note that I/O performance in such a transposed view might be poor.
4437  *
4438  * This is the same as the C function GDALMDArrayTranspose().
4439  *
4440  * @return a new array, that holds a reference to the original one, and thus is
4441  * a view of it (not a copy), or nullptr in case of error.
4442  */
Transpose(const std::vector<int> & anMapNewAxisToOldAxis) const4443 std::shared_ptr<GDALMDArray> GDALMDArray::Transpose(
4444                         const std::vector<int>& anMapNewAxisToOldAxis) const
4445 {
4446     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
4447     if( !self )
4448     {
4449         CPLError(CE_Failure, CPLE_AppDefined,
4450                 "Driver implementation issue: m_pSelf not set !");
4451         return nullptr;
4452     }
4453     const int nDims = static_cast<int>(GetDimensionCount());
4454     std::vector<bool> alreadyUsedOldAxis(nDims, false);
4455     int nCountOldAxis = 0;
4456     for( const auto iOldAxis: anMapNewAxisToOldAxis )
4457     {
4458         if( iOldAxis < -1 || iOldAxis >= nDims )
4459         {
4460             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
4461             return nullptr;
4462         }
4463         if( iOldAxis >= 0 )
4464         {
4465             if( alreadyUsedOldAxis[iOldAxis] )
4466             {
4467                 CPLError(CE_Failure, CPLE_AppDefined,
4468                         "Axis %d is repeated", iOldAxis);
4469                 return nullptr;
4470             }
4471             alreadyUsedOldAxis[iOldAxis] = true;
4472             nCountOldAxis ++;
4473         }
4474     }
4475     if( nCountOldAxis != nDims )
4476     {
4477         CPLError(CE_Failure, CPLE_AppDefined,
4478                  "One or several original axis missing");
4479         return nullptr;
4480     }
4481     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
4482 }
4483 
4484 /************************************************************************/
4485 /*                             IRead()                                  */
4486 /************************************************************************/
4487 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const4488 bool GDALMDArrayUnscaled::IRead(const GUInt64* arrayStartIdx,
4489                               const size_t* count,
4490                               const GInt64* arrayStep,
4491                               const GPtrDiff_t* bufferStride,
4492                               const GDALExtendedDataType& bufferDataType,
4493                               void* pDstBuffer) const
4494 {
4495     const double dfScale = m_poParent->GetScale();
4496     const double dfOffset = m_poParent->GetOffset();
4497     const bool bDTIsComplex = m_dt.GetNumericDataType() == GDT_CFloat64;
4498     const size_t nDTSize = m_dt.GetSize();
4499     const bool bTempBufferNeeded = ( m_dt != bufferDataType );
4500 
4501     double adfSrcNoData[2] = { 0, 0 };
4502     if( m_bHasNoData )
4503     {
4504         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
4505                                         m_poParent->GetDataType(),
4506                                         &adfSrcNoData[0], m_dt);
4507     }
4508 
4509     const auto nDims = GetDimensions().size();
4510     if( nDims == 0 )
4511     {
4512         double adfVal[2];
4513         if( !m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
4514                               m_dt, &adfVal[0]) )
4515         {
4516             return false;
4517         }
4518         if( !m_bHasNoData || adfVal[0] != adfSrcNoData[0] )
4519         {
4520             adfVal[0] = adfVal[0] * dfScale + dfOffset;
4521             if( bDTIsComplex )
4522             {
4523                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
4524             }
4525             GDALExtendedDataType::CopyValue(&adfVal[0], m_dt,
4526                                             pDstBuffer, bufferDataType);
4527         }
4528         else
4529         {
4530             GDALExtendedDataType::CopyValue(&m_adfNoData[0], m_dt,
4531                                             pDstBuffer, bufferDataType);
4532         }
4533         return true;
4534     }
4535 
4536     std::vector<GPtrDiff_t> actualBufferStrideVector;
4537     const GPtrDiff_t* actualBufferStridePtr = bufferStride;
4538     void* pTempBuffer = pDstBuffer;
4539     if( bTempBufferNeeded )
4540     {
4541         size_t nElts = 1;
4542         actualBufferStrideVector.resize(nDims);
4543         for( size_t i = 0; i < nDims; i++ )
4544             nElts *= count[i];
4545         actualBufferStrideVector.back() = 1;
4546         for( size_t i = nDims - 1; i > 0; )
4547         {
4548             --i;
4549             actualBufferStrideVector[i] =
4550                 actualBufferStrideVector[i+1] * count[i+1];
4551         }
4552         actualBufferStridePtr = actualBufferStrideVector.data();
4553         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
4554         if( !pTempBuffer )
4555             return false;
4556     }
4557     if( !m_poParent->Read(arrayStartIdx,
4558                           count,
4559                           arrayStep,
4560                           actualBufferStridePtr,
4561                           m_dt,
4562                           pTempBuffer) )
4563     {
4564         if( bTempBufferNeeded )
4565             VSIFree(pTempBuffer);
4566         return false;
4567     }
4568 
4569     struct Stack
4570     {
4571         size_t       nIters = 0;
4572         double*      src_ptr = nullptr;
4573         GByte*       dst_ptr = nullptr;
4574         GPtrDiff_t   src_inc_offset = 0;
4575         GPtrDiff_t   dst_inc_offset = 0;
4576     };
4577     std::vector<Stack> stack(nDims);
4578     const size_t nBufferDTSize = bufferDataType.GetSize();
4579     for( size_t i = 0; i < nDims; i++ )
4580     {
4581         stack[i].src_inc_offset = actualBufferStridePtr[i] *
4582                                         (bDTIsComplex ? 2 : 1);
4583         stack[i].dst_inc_offset = static_cast<GPtrDiff_t>(
4584             bufferStride[i] * nBufferDTSize);
4585     }
4586     stack[0].src_ptr = static_cast<double*>(pTempBuffer);
4587     stack[0].dst_ptr = static_cast<GByte*>(pDstBuffer);
4588 
4589     size_t dimIdx = 0;
4590     const size_t nDimsMinus1 = nDims - 1;
4591     GByte abyDstNoData[16];
4592     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
4593     GDALExtendedDataType::CopyValue(&m_adfNoData[0], m_dt,
4594                                     abyDstNoData, bufferDataType);
4595 
4596 lbl_next_depth:
4597     if( dimIdx == nDimsMinus1 )
4598     {
4599         auto nIters = count[dimIdx];
4600         double* padfVal = stack[dimIdx].src_ptr;
4601         GByte* dst_ptr = stack[dimIdx].dst_ptr;
4602         while(true)
4603         {
4604             if( !m_bHasNoData || padfVal[0] != adfSrcNoData[0] )
4605             {
4606                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
4607                 if( bDTIsComplex )
4608                 {
4609                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
4610                 }
4611                 if( bTempBufferNeeded )
4612                 {
4613                     GDALExtendedDataType::CopyValue(&padfVal[0], m_dt,
4614                                                     dst_ptr,
4615                                                     bufferDataType);
4616                 }
4617             }
4618             else
4619             {
4620                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
4621             }
4622 
4623             if( (--nIters) == 0 )
4624                 break;
4625             padfVal += stack[dimIdx].src_inc_offset;
4626             dst_ptr += stack[dimIdx].dst_inc_offset;
4627         }
4628     }
4629     else
4630     {
4631         stack[dimIdx].nIters = count[dimIdx];
4632         while(true)
4633         {
4634             dimIdx ++;
4635             stack[dimIdx].src_ptr = stack[dimIdx-1].src_ptr;
4636             stack[dimIdx].dst_ptr = stack[dimIdx-1].dst_ptr;
4637             goto lbl_next_depth;
4638 lbl_return_to_caller:
4639             dimIdx --;
4640             if( (--stack[dimIdx].nIters) == 0 )
4641                 break;
4642             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
4643             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
4644         }
4645     }
4646     if( dimIdx > 0 )
4647         goto lbl_return_to_caller;
4648 
4649     if( bTempBufferNeeded )
4650         VSIFree(pTempBuffer);
4651     return true;
4652 }
4653 
4654 /************************************************************************/
4655 /*                             IWrite()                                 */
4656 /************************************************************************/
4657 
IWrite(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,const void * pSrcBuffer)4658 bool GDALMDArrayUnscaled::IWrite(const GUInt64* arrayStartIdx,
4659                                 const size_t* count,
4660                                 const GInt64* arrayStep,
4661                                 const GPtrDiff_t* bufferStride,
4662                                 const GDALExtendedDataType& bufferDataType,
4663                                 const void* pSrcBuffer)
4664 {
4665     const double dfScale = m_poParent->GetScale();
4666     const double dfOffset = m_poParent->GetOffset();
4667     const bool bDTIsComplex = m_dt.GetNumericDataType() == GDT_CFloat64;
4668     const size_t nDTSize = m_dt.GetSize();
4669     CPLAssert( nDTSize == 8 || nDTSize == 16 );
4670     const bool bIsBufferDataTypeNativeDataType = ( m_dt == bufferDataType );
4671     const bool bSelfAndParentHaveNoData =
4672         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
4673 
4674     double adfSrcNoData[2] = { 0, 0 };
4675     if( bSelfAndParentHaveNoData )
4676     {
4677         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
4678                                         m_poParent->GetDataType(),
4679                                         &adfSrcNoData[0], m_dt);
4680     }
4681 
4682     const auto nDims = GetDimensions().size();
4683     if( nDims == 0 )
4684     {
4685         double adfVal[2];
4686         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType,
4687                                         &adfVal[0], m_dt);
4688         if( bSelfAndParentHaveNoData &&
4689             (std::isnan(adfVal[0]) || adfVal[0] == m_adfNoData[0]) )
4690         {
4691             return m_poParent->Write(arrayStartIdx, count, arrayStep, bufferStride,
4692                                      m_poParent->GetDataType(),
4693                                      m_poParent->GetRawNoDataValue());
4694         }
4695         else
4696         {
4697             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
4698             if( bDTIsComplex )
4699             {
4700                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
4701             }
4702             return m_poParent->Write(arrayStartIdx, count, arrayStep, bufferStride,
4703                                      m_dt, &adfVal[0]);
4704         }
4705     }
4706 
4707     std::vector<GPtrDiff_t> tmpBufferStrideVector;
4708     size_t nElts = 1;
4709     tmpBufferStrideVector.resize(nDims);
4710     for( size_t i = 0; i < nDims; i++ )
4711         nElts *= count[i];
4712     tmpBufferStrideVector.back() = 1;
4713     for( size_t i = nDims - 1; i > 0; )
4714     {
4715         --i;
4716         tmpBufferStrideVector[i] =
4717             tmpBufferStrideVector[i+1] * count[i+1];
4718     }
4719     const GPtrDiff_t* tmpBufferStridePtr = tmpBufferStrideVector.data();
4720     void* pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
4721     if( !pTempBuffer )
4722         return false;
4723 
4724     struct Stack
4725     {
4726         size_t       nIters = 0;
4727         double*      dst_ptr = nullptr;
4728         const GByte* src_ptr = nullptr;
4729         GPtrDiff_t   src_inc_offset = 0;
4730         GPtrDiff_t   dst_inc_offset = 0;
4731     };
4732     std::vector<Stack> stack(nDims);
4733     const size_t nBufferDTSize = bufferDataType.GetSize();
4734     for( size_t i = 0; i < nDims; i++ )
4735     {
4736         stack[i].dst_inc_offset = tmpBufferStridePtr[i] *
4737                                         (bDTIsComplex ? 2 : 1);
4738         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
4739             bufferStride[i] * nBufferDTSize);
4740     }
4741     stack[0].dst_ptr = static_cast<double*>(pTempBuffer);
4742     stack[0].src_ptr = static_cast<const GByte*>(pSrcBuffer);
4743 
4744     size_t dimIdx = 0;
4745     const size_t nDimsMinus1 = nDims - 1;
4746 
4747 lbl_next_depth:
4748     if( dimIdx == nDimsMinus1 )
4749     {
4750         auto nIters = count[dimIdx];
4751         double* dst_ptr = stack[dimIdx].dst_ptr;
4752         const GByte* src_ptr = stack[dimIdx].src_ptr;
4753         while(true)
4754         {
4755             double adfVal[2];
4756             const double* padfSrcVal;
4757             if( bIsBufferDataTypeNativeDataType )
4758             {
4759                 padfSrcVal = reinterpret_cast<const double*>(src_ptr);
4760             }
4761             else
4762             {
4763                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
4764                                                 &adfVal[0], m_dt);
4765                 padfSrcVal = adfVal;
4766             }
4767 
4768             if( bSelfAndParentHaveNoData &&
4769                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == m_adfNoData[0]) )
4770             {
4771                 dst_ptr[0] = adfSrcNoData[0];
4772                 if( bDTIsComplex )
4773                 {
4774                     dst_ptr[1] = adfSrcNoData[1];
4775                 }
4776             }
4777             else
4778             {
4779                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
4780                 if( bDTIsComplex )
4781                 {
4782                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
4783                 }
4784             }
4785 
4786             if( (--nIters) == 0 )
4787                 break;
4788             dst_ptr += stack[dimIdx].dst_inc_offset;
4789             src_ptr += stack[dimIdx].src_inc_offset;
4790         }
4791     }
4792     else
4793     {
4794         stack[dimIdx].nIters = count[dimIdx];
4795         while(true)
4796         {
4797             dimIdx ++;
4798             stack[dimIdx].src_ptr = stack[dimIdx-1].src_ptr;
4799             stack[dimIdx].dst_ptr = stack[dimIdx-1].dst_ptr;
4800             goto lbl_next_depth;
4801 lbl_return_to_caller:
4802             dimIdx --;
4803             if( (--stack[dimIdx].nIters) == 0 )
4804                 break;
4805             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
4806             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
4807         }
4808     }
4809     if( dimIdx > 0 )
4810         goto lbl_return_to_caller;
4811 
4812     // If the parent array is not double/complex-double, then convert the
4813     // values to it, before calling Write(), as some implementations can be
4814     // very slow when doing the type conversion.
4815     const auto& eParentDT = m_poParent->GetDataType();
4816     const size_t nParentDTSize = eParentDT.GetSize();
4817     if( nParentDTSize <= nDTSize / 2 )
4818     {
4819         // Copy in-place by making sure that source and target do not overlap
4820         const auto eNumericDT = m_dt.GetNumericDataType();
4821         const auto eParentNumericDT = eParentDT.GetNumericDataType();
4822 
4823         // Copy first element
4824         {
4825             std::vector<GByte> abyTemp(nParentDTSize);
4826             GDALCopyWords64( static_cast<GByte*>(pTempBuffer),
4827                              eNumericDT, static_cast<int>(nDTSize),
4828                              &abyTemp[0],
4829                              eParentNumericDT, static_cast<int>(nParentDTSize),
4830                              1 );
4831             memcpy( pTempBuffer, abyTemp.data(), abyTemp.size() );
4832         }
4833         // Remaining elements
4834         for( size_t i = 1; i < nElts; ++i )
4835         {
4836             GDALCopyWords( static_cast<GByte*>(pTempBuffer) + i * nDTSize,
4837                            eNumericDT, 0,
4838                            static_cast<GByte*>(pTempBuffer) + i * nParentDTSize,
4839                            eParentNumericDT, 0,
4840                            1 );
4841         }
4842     }
4843 
4844     const bool ret = m_poParent->Write(arrayStartIdx,
4845                                        count,
4846                                        arrayStep,
4847                                        tmpBufferStridePtr,
4848                                        eParentDT,
4849                                        pTempBuffer);
4850 
4851     VSIFree(pTempBuffer);
4852     return ret;
4853 }
4854 
4855 /************************************************************************/
4856 /*                           GetUnscaled()                              */
4857 /************************************************************************/
4858 
4859 /** Return an array that is the unscaled version of the current one.
4860  *
4861  * That is each value of the unscaled array will be
4862  * unscaled_value = raw_value * GetScale() + GetOffset()
4863  *
4864  * Starting with GDAL 3.3, the Write() method is implemented and will convert
4865  * from unscaled values to raw values.
4866  *
4867  * This is the same as the C function GDALMDArrayGetUnscaled().
4868  *
4869  * @return a new array, that holds a reference to the original one, and thus is
4870  * a view of it (not a copy), or nullptr in case of error.
4871  */
GetUnscaled() const4872 std::shared_ptr<GDALMDArray> GDALMDArray::GetUnscaled() const
4873 {
4874     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
4875     if( !self )
4876     {
4877         CPLError(CE_Failure, CPLE_AppDefined,
4878                 "Driver implementation issue: m_pSelf not set !");
4879         return nullptr;
4880     }
4881     if( GetDataType().GetClass() != GEDTC_NUMERIC )
4882     {
4883         CPLError(CE_Failure, CPLE_AppDefined,
4884                  "GetUnscaled() only supports numeric data type");
4885         return nullptr;
4886     }
4887     const double dfScale = GetScale();
4888     const double dfOffset = GetOffset();
4889     if( dfScale == 1.0 && dfOffset == 0.0 )
4890         return self;
4891 
4892     return GDALMDArrayUnscaled::Create(self);
4893 }
4894 
4895 /************************************************************************/
4896 /*                      GDALMDArrayTransposed                           */
4897 /************************************************************************/
4898 
4899 class GDALMDArrayMask final: public GDALMDArray
4900 {
4901 private:
4902     std::shared_ptr<GDALMDArray> m_poParent{};
4903     GDALExtendedDataType m_dt {GDALExtendedDataType::Create(GDT_Byte) };
4904 
4905     template<typename Type> void ReadInternal(const size_t* count,
4906                                           const GPtrDiff_t* bufferStride,
4907                                           const GDALExtendedDataType& bufferDataType,
4908                                           void* pDstBuffer,
4909                                           const void* pTempBuffer,
4910                                           const GDALExtendedDataType& oTmpBufferDT,
4911                                           const std::vector<GPtrDiff_t>& tmpBufferStrideVector,
4912                                           bool bHasMissingValue, double dfMissingValue,
4913                                           bool bHasFillValue, double dfFillValue,
4914                                           bool bHasValidMin, double dfValidMin,
4915                                           bool bHasValidMax, double dfValidMax) const;
4916 
4917 protected:
GDALMDArrayMask(const std::shared_ptr<GDALMDArray> & poParent)4918     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray>& poParent):
4919         GDALAbstractMDArray(std::string(), "Mask of " + poParent->GetFullName()),
4920         GDALMDArray(std::string(), "Mask of " + poParent->GetFullName()),
4921         m_poParent(std::move(poParent))
4922     {
4923     }
4924 
4925     bool IRead(const GUInt64* arrayStartIdx,
4926                       const size_t* count,
4927                       const GInt64* arrayStep,
4928                       const GPtrDiff_t* bufferStride,
4929                       const GDALExtendedDataType& bufferDataType,
4930                       void* pDstBuffer) const override;
4931 
IAdviseRead(const GUInt64 * arrayStartIdx,const size_t * count) const4932     bool IAdviseRead(const GUInt64* arrayStartIdx,
4933                      const size_t* count) const override
4934         { return m_poParent->AdviseRead(arrayStartIdx, count); }
4935 
4936 public:
Create(const std::shared_ptr<GDALMDArray> & poParent)4937     static std::shared_ptr<GDALMDArrayMask> Create(
4938                     const std::shared_ptr<GDALMDArray>& poParent)
4939     {
4940         auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(
4941             poParent)));
4942         newAr->SetSelf(newAr);
4943         return newAr;
4944     }
4945 
IsWritable() const4946     bool IsWritable() const override { return false; }
4947 
GetDimensions() const4948     const std::vector<std::shared_ptr<GDALDimension>>& GetDimensions() const override { return m_poParent->GetDimensions(); }
4949 
GetDataType() const4950     const GDALExtendedDataType &GetDataType() const override { return m_dt; }
4951 
GetSpatialRef() const4952     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override { return m_poParent->GetSpatialRef(); }
4953 
GetBlockSize() const4954     std::vector<GUInt64> GetBlockSize() const override { return m_poParent->GetBlockSize(); }
4955 };
4956 
4957 /************************************************************************/
4958 /*                             IRead()                                  */
4959 /************************************************************************/
4960 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const4961 bool GDALMDArrayMask::IRead(const GUInt64* arrayStartIdx,
4962                               const size_t* count,
4963                               const GInt64* arrayStep,
4964                               const GPtrDiff_t* bufferStride,
4965                               const GDALExtendedDataType& bufferDataType,
4966                               void* pDstBuffer) const
4967 {
4968     size_t nElts = 1;
4969     const size_t nDims = GetDimensionCount();
4970     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
4971     for( size_t i = 0; i < nDims; i++ )
4972         nElts *= count[i];
4973     if( nDims > 0 )
4974     {
4975         tmpBufferStrideVector.back() = 1;
4976         for( size_t i = nDims - 1; i > 0; )
4977         {
4978             --i;
4979             tmpBufferStrideVector[i] =
4980                 tmpBufferStrideVector[i+1] * count[i+1];
4981         }
4982     }
4983 
4984     const auto GetSingleValNumericAttr = [this]
4985         (const char* pszAttrName, bool& bHasVal, double& dfVal)
4986     {
4987         auto poAttr = m_poParent->GetAttribute(pszAttrName);
4988         if( poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC )
4989         {
4990             const auto anDimSizes = poAttr->GetDimensionsSize();
4991             if( anDimSizes.empty() ||
4992                 (anDimSizes.size() == 1 && anDimSizes[0] == 1) )
4993             {
4994                 bHasVal = true;
4995                 dfVal = poAttr->ReadAsDouble();
4996             }
4997         }
4998     };
4999 
5000     double dfMissingValue = 0.0;
5001     bool bHasMissingValue = false;
5002     GetSingleValNumericAttr("missing_value", bHasMissingValue, dfMissingValue);
5003 
5004     double dfFillValue = 0.0;
5005     bool bHasFillValue = false;
5006     GetSingleValNumericAttr("_FillValue", bHasFillValue, dfFillValue);
5007 
5008     double dfValidMin = 0.0;
5009     bool bHasValidMin = false;
5010     GetSingleValNumericAttr("valid_min", bHasValidMin, dfValidMin);
5011 
5012     double dfValidMax = 0.0;
5013     bool bHasValidMax = false;
5014     GetSingleValNumericAttr("valid_max", bHasValidMax, dfValidMax);
5015 
5016     {
5017         auto poValidRange = m_poParent->GetAttribute("valid_range");
5018         if( poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
5019             poValidRange->GetDimensionsSize()[0] == 2 &&
5020             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC )
5021         {
5022             bHasValidMin = true;
5023             bHasValidMax = true;
5024             auto vals = poValidRange->ReadAsDoubleArray();
5025             CPLAssert(vals.size() == 2);
5026             dfValidMin = vals[0];
5027             dfValidMax = vals[1];
5028         }
5029     }
5030 
5031     /* Optimized case: if we are an integer data type and that there is no */
5032     /* attribute that can be used to set mask = 0, then fill the mask buffer */
5033     /* directly */
5034     if( !bHasMissingValue && !bHasFillValue && !bHasValidMin && !bHasValidMax &&
5035         m_poParent->GetRawNoDataValue() == nullptr &&
5036         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()) )
5037     {
5038         if( bufferDataType == m_dt ) // Byte case
5039         {
5040             bool bContiguous = true;
5041             for( size_t i = 0; i < nDims; i++)
5042             {
5043                 if( bufferStride[i] != tmpBufferStrideVector[i] )
5044                 {
5045                     bContiguous = false;
5046                     break;
5047                 }
5048             }
5049             if( bContiguous )
5050             {
5051                 // CPLDebug("GDAL", "GetMask(): contiguous case");
5052                 memset(pDstBuffer, 1, nElts);
5053                 return true;
5054             }
5055         }
5056 
5057         struct Stack
5058         {
5059             size_t       nIters = 0;
5060             GByte*       dst_ptr = nullptr;
5061             GPtrDiff_t   dst_inc_offset = 0;
5062         };
5063         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
5064         const size_t nBufferDTSize = bufferDataType.GetSize();
5065         for( size_t i = 0; i < nDims; i++ )
5066         {
5067             stack[i].dst_inc_offset = static_cast<GPtrDiff_t>(
5068                 bufferStride[i] * nBufferDTSize);
5069         }
5070         stack[0].dst_ptr = static_cast<GByte*>(pDstBuffer);
5071 
5072         size_t dimIdx = 0;
5073         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
5074         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
5075         GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
5076         CPLAssert(nBufferDTSize <= 16);
5077         const GByte flag = 1;
5078         // Coverity misses that m_dt is of type Byte
5079         // coverity[overrun-buffer-val]
5080         GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
5081 
5082 lbl_next_depth:
5083         if( dimIdx == nDimsMinus1 )
5084         {
5085             auto nIters = nDims > 0 ? count[dimIdx] : 1;
5086             GByte* dst_ptr = stack[dimIdx].dst_ptr;
5087 
5088             while(true)
5089             {
5090                 if( bBufferDataTypeIsByte )
5091                 {
5092                     *dst_ptr = flag;
5093                 }
5094                 else
5095                 {
5096                     memcpy(dst_ptr, abyOne, nBufferDTSize);
5097                 }
5098 
5099                 if( (--nIters) == 0 )
5100                     break;
5101                 dst_ptr += stack[dimIdx].dst_inc_offset;
5102             }
5103         }
5104         else
5105         {
5106             stack[dimIdx].nIters = count[dimIdx];
5107             while(true)
5108             {
5109                 dimIdx ++;
5110                 stack[dimIdx].dst_ptr = stack[dimIdx-1].dst_ptr;
5111                 goto lbl_next_depth;
5112 lbl_return_to_caller:
5113                 dimIdx --;
5114                 if( (--stack[dimIdx].nIters) == 0 )
5115                     break;
5116                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
5117             }
5118         }
5119         if( dimIdx > 0 )
5120             goto lbl_return_to_caller;
5121 
5122         return true;
5123     }
5124 
5125     const auto oTmpBufferDT = GDALDataTypeIsComplex(
5126             m_poParent->GetDataType().GetNumericDataType()) ?
5127                 GDALExtendedDataType::Create(GDT_Float64) :
5128                 m_poParent->GetDataType();
5129     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
5130     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
5131     if( !pTempBuffer )
5132         return false;
5133     if( !m_poParent->Read(arrayStartIdx,
5134                           count,
5135                           arrayStep,
5136                           tmpBufferStrideVector.data(),
5137                           oTmpBufferDT,
5138                           pTempBuffer) )
5139     {
5140         VSIFree(pTempBuffer);
5141         return false;
5142     }
5143 
5144     switch( oTmpBufferDT.GetNumericDataType() )
5145     {
5146         case GDT_Byte:
5147             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
5148                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5149                                 bHasMissingValue, dfMissingValue,
5150                                 bHasFillValue, dfFillValue,
5151                                 bHasValidMin, dfValidMin,
5152                                 bHasValidMax, dfValidMax);
5153             break;
5154 
5155         case GDT_UInt16:
5156             ReadInternal<GUInt16>(count, bufferStride, bufferDataType, pDstBuffer,
5157                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5158                                 bHasMissingValue, dfMissingValue,
5159                                 bHasFillValue, dfFillValue,
5160                                 bHasValidMin, dfValidMin,
5161                                 bHasValidMax, dfValidMax);
5162             break;
5163 
5164         case GDT_Int16:
5165             ReadInternal<GInt16>(count, bufferStride, bufferDataType, pDstBuffer,
5166                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5167                                 bHasMissingValue, dfMissingValue,
5168                                 bHasFillValue, dfFillValue,
5169                                 bHasValidMin, dfValidMin,
5170                                 bHasValidMax, dfValidMax);
5171             break;
5172 
5173         case GDT_UInt32:
5174             ReadInternal<GUInt32>(count, bufferStride, bufferDataType, pDstBuffer,
5175                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5176                                 bHasMissingValue, dfMissingValue,
5177                                 bHasFillValue, dfFillValue,
5178                                 bHasValidMin, dfValidMin,
5179                                 bHasValidMax, dfValidMax);
5180             break;
5181 
5182         case GDT_Int32:
5183             ReadInternal<GInt32>(count, bufferStride, bufferDataType, pDstBuffer,
5184                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5185                                 bHasMissingValue, dfMissingValue,
5186                                 bHasFillValue, dfFillValue,
5187                                 bHasValidMin, dfValidMin,
5188                                 bHasValidMax, dfValidMax);
5189             break;
5190 
5191         case GDT_Float32:
5192             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
5193                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5194                                 bHasMissingValue, dfMissingValue,
5195                                 bHasFillValue, dfFillValue,
5196                                 bHasValidMin, dfValidMin,
5197                                 bHasValidMax, dfValidMax);
5198             break;
5199 
5200         default:
5201             CPLAssert(oTmpBufferDT.GetNumericDataType() == GDT_Float64);
5202             ReadInternal<double>(count, bufferStride, bufferDataType, pDstBuffer,
5203                                 pTempBuffer, oTmpBufferDT, tmpBufferStrideVector,
5204                                 bHasMissingValue, dfMissingValue,
5205                                 bHasFillValue, dfFillValue,
5206                                 bHasValidMin, dfValidMin,
5207                                 bHasValidMax, dfValidMax);
5208             break;
5209 
5210     }
5211 
5212     VSIFree(pTempBuffer);
5213 
5214     return true;
5215 }
5216 
5217 /************************************************************************/
5218 /*                          IsValidForDT()                              */
5219 /************************************************************************/
5220 
IsValidForDT(double dfVal)5221 template<typename Type> static bool IsValidForDT(double dfVal)
5222 {
5223     if( std::isnan(dfVal) )
5224         return false;
5225     if( dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()) )
5226         return false;
5227     if( dfVal > static_cast<double>(std::numeric_limits<Type>::max()) )
5228         return false;
5229     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
5230 }
5231 
IsValidForDT(double)5232 template<> bool IsValidForDT<double>(double)
5233 {
5234     return true;
5235 }
5236 
5237 /************************************************************************/
5238 /*                              IsNan()                                 */
5239 /************************************************************************/
5240 
IsNan(Type)5241 template<typename Type> inline bool IsNan(Type)
5242 {
5243     return false;
5244 }
5245 
IsNan(double val)5246 template<> bool IsNan<double>(double val)
5247 {
5248     return std::isnan(val);
5249 }
5250 
IsNan(float val)5251 template<> bool IsNan<float>(float val)
5252 {
5253     return std::isnan(val);
5254 }
5255 
5256 /************************************************************************/
5257 /*                         ReadInternal()                               */
5258 /************************************************************************/
5259 
ReadInternal(const size_t * count,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer,const void * pTempBuffer,const GDALExtendedDataType & oTmpBufferDT,const std::vector<GPtrDiff_t> & tmpBufferStrideVector,bool bHasMissingValue,double dfMissingValue,bool bHasFillValue,double dfFillValue,bool bHasValidMin,double dfValidMin,bool bHasValidMax,double dfValidMax) const5260 template<typename Type> void GDALMDArrayMask::ReadInternal(
5261                                           const size_t* count,
5262                                           const GPtrDiff_t* bufferStride,
5263                                           const GDALExtendedDataType& bufferDataType,
5264                                           void* pDstBuffer,
5265                                           const void* pTempBuffer,
5266                                           const GDALExtendedDataType& oTmpBufferDT,
5267                                           const std::vector<GPtrDiff_t>& tmpBufferStrideVector,
5268                                           bool bHasMissingValue, double dfMissingValue,
5269                                           bool bHasFillValue, double dfFillValue,
5270                                           bool bHasValidMin, double dfValidMin,
5271                                           bool bHasValidMax, double dfValidMax) const
5272 {
5273     const size_t nDims = GetDimensionCount();
5274 
5275     const auto castValue = [](bool& bHasVal, double dfVal) -> Type
5276     {
5277         if( bHasVal )
5278         {
5279             if( IsValidForDT<Type>(dfVal) )
5280             {
5281                 return static_cast<Type>(dfVal);
5282             }
5283             else
5284             {
5285                 bHasVal = false;
5286             }
5287         }
5288         return 0;
5289     };
5290 
5291     const void* pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
5292     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
5293     const Type nNoDataValue = castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
5294     const Type nMissingValue = castValue(bHasMissingValue, dfMissingValue);
5295     const Type nFillValue = castValue(bHasFillValue, dfFillValue);
5296     const Type nValidMin = castValue(bHasValidMin, dfValidMin);
5297     const Type nValidMax = castValue(bHasValidMax, dfValidMax);
5298 
5299 #define GET_MASK_FOR_SAMPLE(v) \
5300     static_cast<GByte>( !IsNan(v) && \
5301       !(bHasNodataValue && v == nNoDataValue) && \
5302       !(bHasMissingValue && v == nMissingValue) && \
5303       !(bHasFillValue && v == nFillValue) && \
5304       !(bHasValidMin && v < nValidMin) && \
5305       !(bHasValidMax && v > nValidMax) )
5306 
5307     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
5308     /* Optimized case: Byte output and output buffer is contiguous */
5309     if( bBufferDataTypeIsByte )
5310     {
5311         bool bContiguous = true;
5312         for( size_t i = 0; i < nDims; i++)
5313         {
5314             if( bufferStride[i] != tmpBufferStrideVector[i] )
5315             {
5316                 bContiguous = false;
5317                 break;
5318             }
5319         }
5320         if( bContiguous )
5321         {
5322             size_t nElts = 1;
5323             for( size_t i = 0; i < nDims; i++ )
5324                 nElts *= count[i];
5325 
5326             for( size_t i = 0; i < nElts; i++)
5327             {
5328                 const Type* pSrc = static_cast<const Type*>(pTempBuffer) + i;
5329                 static_cast<GByte*>(pDstBuffer)[i] = GET_MASK_FOR_SAMPLE(*pSrc);
5330             }
5331             return;
5332         }
5333     }
5334 
5335     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
5336     struct Stack
5337     {
5338         size_t       nIters = 0;
5339         const GByte* src_ptr = nullptr;
5340         GByte*       dst_ptr = nullptr;
5341         GPtrDiff_t   src_inc_offset = 0;
5342         GPtrDiff_t   dst_inc_offset = 0;
5343     };
5344     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
5345     const size_t nBufferDTSize = bufferDataType.GetSize();
5346     for( size_t i = 0; i < nDims; i++ )
5347     {
5348         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
5349             tmpBufferStrideVector[i] * nTmpBufferDTSize);
5350         stack[i].dst_inc_offset = static_cast<GPtrDiff_t>(
5351             bufferStride[i] * nBufferDTSize);
5352     }
5353     stack[0].src_ptr = static_cast<const GByte*>(pTempBuffer);
5354     stack[0].dst_ptr = static_cast<GByte*>(pDstBuffer);
5355 
5356     size_t dimIdx = 0;
5357     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
5358     GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
5359     CPLAssert(nBufferDTSize <= 16);
5360     for( GByte flag = 0; flag <= 1; flag++ )
5361     {
5362         // Coverity misses that m_dt is of type Byte
5363         // coverity[overrun-buffer-val]
5364         GDALExtendedDataType::CopyValue(&flag, m_dt,
5365                                         abyZeroOrOne[flag], bufferDataType);
5366     }
5367 
5368 lbl_next_depth:
5369     if( dimIdx == nDimsMinus1 )
5370     {
5371         auto nIters = nDims > 0 ? count[dimIdx] : 1;
5372         const GByte* src_ptr = stack[dimIdx].src_ptr;
5373         GByte* dst_ptr = stack[dimIdx].dst_ptr;
5374 
5375         while(true)
5376         {
5377             const Type* pSrc = reinterpret_cast<const Type*>(src_ptr);
5378             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
5379 
5380             if( bBufferDataTypeIsByte )
5381             {
5382                 *dst_ptr = flag;
5383             }
5384             else
5385             {
5386                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
5387             }
5388 
5389             if( (--nIters) == 0 )
5390                 break;
5391             src_ptr += stack[dimIdx].src_inc_offset;
5392             dst_ptr += stack[dimIdx].dst_inc_offset;
5393         }
5394     }
5395     else
5396     {
5397         stack[dimIdx].nIters = count[dimIdx];
5398         while(true)
5399         {
5400             dimIdx ++;
5401             stack[dimIdx].src_ptr = stack[dimIdx-1].src_ptr;
5402             stack[dimIdx].dst_ptr = stack[dimIdx-1].dst_ptr;
5403             goto lbl_next_depth;
5404 lbl_return_to_caller:
5405             dimIdx --;
5406             if( (--stack[dimIdx].nIters) == 0 )
5407                 break;
5408             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
5409             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
5410         }
5411     }
5412     if( dimIdx > 0 )
5413         goto lbl_return_to_caller;
5414 }
5415 
5416 /************************************************************************/
5417 /*                            GetMask()                                 */
5418 /************************************************************************/
5419 
5420 /** Return an array that is a mask for the current array
5421  *
5422  * This array will be of type Byte, with values set to 0 to indicate invalid
5423  * pixels of the current array, and values set to 1 to indicate valid pixels.
5424  *
5425  * The generic implementation honours the NoDataValue, as well as various
5426  * netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
5427  * and valid_range.
5428  *
5429  * This is the same as the C function GDALMDArrayGetMask().
5430  *
5431  * @param papszOptions NULL-terminated list of options, or NULL.
5432  *
5433  * @return a new array, that holds a reference to the original one, and thus is
5434  * a view of it (not a copy), or nullptr in case of error.
5435  */
GetMask(CPL_UNUSED CSLConstList papszOptions) const5436 std::shared_ptr<GDALMDArray> GDALMDArray::GetMask(CPL_UNUSED
5437                                                   CSLConstList papszOptions) const
5438 {
5439     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5440     if( !self )
5441     {
5442         CPLError(CE_Failure, CPLE_AppDefined,
5443                 "Driver implementation issue: m_pSelf not set !");
5444         return nullptr;
5445     }
5446     if( GetDataType().GetClass() != GEDTC_NUMERIC )
5447     {
5448         CPLError(CE_Failure, CPLE_AppDefined,
5449                  "GetMask() only supports numeric data type");
5450         return nullptr;
5451     }
5452     return GDALMDArrayMask::Create(self);
5453 }
5454 
5455 /************************************************************************/
5456 /*                         GDALDatasetFromArray()                       */
5457 /************************************************************************/
5458 
5459 class GDALDatasetFromArray;
5460 
5461 class GDALRasterBandFromArray final: public GDALRasterBand
5462 {
5463     std::vector<GUInt64>     m_anOffset{};
5464     std::vector<size_t>      m_anCount{};
5465     std::vector<GPtrDiff_t>  m_anStride{};
5466 
5467 protected:
5468     CPLErr IReadBlock( int, int, void * ) override;
5469     CPLErr IWriteBlock( int, int, void * ) override;
5470     CPLErr IRasterIO( GDALRWFlag eRWFlag,
5471                                   int nXOff, int nYOff, int nXSize, int nYSize,
5472                                   void * pData, int nBufXSize, int nBufYSize,
5473                                   GDALDataType eBufType,
5474                                   GSpacing nPixelSpaceBuf,
5475                                   GSpacing nLineSpaceBuf,
5476                                   GDALRasterIOExtraArg* psExtraArg ) override;
5477 public:
5478     explicit GDALRasterBandFromArray(GDALDatasetFromArray* poDSIn,
5479                                      const std::vector<GUInt64>& anOtherDimCoord);
5480 
5481     double GetNoDataValue(int* pbHasNoData) override;
5482     double GetOffset(int* pbHasOffset) override;
5483     double GetScale(int* pbHasScale) override;
5484     const char* GetUnitType() override;
5485 };
5486 
5487 
5488 class GDALDatasetFromArray final: public GDALDataset
5489 {
5490     friend class GDALRasterBandFromArray;
5491 
5492     std::shared_ptr<GDALMDArray> m_poArray;
5493     size_t m_iXDim;
5494     size_t m_iYDim;
5495     double m_adfGeoTransform[6]{0,1,0,0,0,1};
5496     bool m_bHasGT = false;
5497     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
5498 
5499 public:
GuessGeoTransform()5500     void GuessGeoTransform()
5501     {
5502         const auto& dims(m_poArray->GetDimensions());
5503         if( dims.size() < 2 )
5504             return;
5505         auto poVarX = dims[m_iXDim]->GetIndexingVariable();
5506         auto poVarY = dims[m_iYDim]->GetIndexingVariable();
5507         if( poVarX && poVarX->GetDimensionCount() == 1 &&
5508             poVarX->GetDimensions()[0]->GetSize() == dims[m_iXDim]->GetSize() &&
5509             poVarY && poVarY->GetDimensionCount() == 1 &&
5510             poVarY->GetDimensions()[0]->GetSize() == dims[m_iYDim]->GetSize() &&
5511             dims[m_iXDim]->GetSize() > 1 &&
5512             dims[m_iXDim]->GetSize() < 10 * 1000 * 1000 &&
5513             dims[m_iYDim]->GetSize() > 1 &&
5514             dims[m_iYDim]->GetSize() < 10 * 1000 * 1000 )
5515         {
5516             std::vector<double> adfTmp(static_cast<size_t>(std::max(
5517                  dims[m_iXDim]->GetSize(), dims[m_iYDim]->GetSize())));
5518             const GUInt64 anStart[1] = { 0 };
5519             size_t nCount = static_cast<size_t>(dims[m_iXDim]->GetSize());
5520             size_t anCount[1] = { nCount };
5521             if( !poVarX->Read(anStart, anCount, nullptr, nullptr,
5522                          GDALExtendedDataType::Create(GDT_Float64),
5523                          &adfTmp[0]) )
5524             {
5525                 return;
5526             }
5527 
5528             double dfSpacing = (adfTmp[nCount-1] - adfTmp[0]) / (nCount - 1);
5529             for(size_t i = 1; i < nCount; i++ )
5530             {
5531                 if( fabs((adfTmp[i] - adfTmp[i-1]) - dfSpacing) > 1e-3 * fabs(dfSpacing) )
5532                 {
5533                     return;
5534                 }
5535             }
5536             const double dfXStart = adfTmp[0];
5537             const double dfXSpacing = dfSpacing;
5538 
5539             nCount = static_cast<size_t>(dims[m_iYDim]->GetSize());
5540             anCount[0] = nCount;
5541             if( !poVarY->Read(anStart, anCount, nullptr, nullptr,
5542                          GDALExtendedDataType::Create(GDT_Float64),
5543                          &adfTmp[0]) )
5544             {
5545                 return;
5546             }
5547             dfSpacing = (adfTmp[nCount-1] - adfTmp[0]) / (nCount - 1);
5548             for(size_t i = 1; i < nCount; i++ )
5549             {
5550                 if( fabs((adfTmp[i] - adfTmp[i-1]) - dfSpacing) > 1e-3 * fabs(dfSpacing) )
5551                 {
5552                     return;
5553                 }
5554             }
5555             const double dfYStart = adfTmp[0];
5556             const double dfYSpacing = dfSpacing;
5557             m_bHasGT = true;
5558             m_adfGeoTransform[0] = dfXStart - dfXSpacing / 2;
5559             m_adfGeoTransform[1] = dfXSpacing;
5560             m_adfGeoTransform[2] = 0;
5561             m_adfGeoTransform[3] = dfYStart - dfYSpacing / 2;
5562             m_adfGeoTransform[4] = 0;
5563             m_adfGeoTransform[5] = dfYSpacing;
5564         }
5565 
5566     }
5567 
GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> & array,size_t iXDim,size_t iYDim)5568     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray>& array,
5569                          size_t iXDim, size_t iYDim):
5570         m_poArray(array),
5571         m_iXDim(iXDim),
5572         m_iYDim(iYDim)
5573     {
5574         const auto& dims(m_poArray->GetDimensions());
5575         const auto nDimCount = dims.size();
5576         nRasterYSize = nDimCount < 2 ? 1 : static_cast<int>(
5577             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
5578         nRasterXSize = static_cast<int>(
5579             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
5580         eAccess = array->IsWritable() ? GA_Update: GA_ReadOnly;
5581 
5582         const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
5583         std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
5584         std::vector<GUInt64> anStackIters(nDimCount);
5585         std::vector<size_t> anMapNewToOld(nNewDimCount);
5586         for( size_t i = 0, j = 0; i < nDimCount; ++i )
5587         {
5588             if( i != iXDim && !(nDimCount >= 2 && i == iYDim) )
5589             {
5590                 anMapNewToOld[j] = i;
5591                 j++;
5592             }
5593         }
5594 
5595         GuessGeoTransform();
5596 
5597         const auto attrs(array->GetAttributes());
5598         for( const auto& attr: attrs )
5599         {
5600             auto stringArray = attr->ReadAsStringArray();
5601             std::string val;
5602             if( stringArray.size() > 1 )
5603             {
5604                 val += '{';
5605             }
5606             for( int i = 0; i < stringArray.size(); ++i )
5607             {
5608                 if( i > 0 )
5609                     val += ',';
5610                 val += stringArray[i];
5611             }
5612             if( stringArray.size() > 1 )
5613             {
5614                 val += '}';
5615             }
5616             SetMetadataItem(attr->GetName().c_str(), val.c_str());
5617         }
5618 
5619         // Instantiate bands by iterating over non-XY variables
5620         size_t iDim = 0;
5621 lbl_next_depth:
5622         if( iDim < nNewDimCount )
5623         {
5624             anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
5625             anOtherDimCoord[iDim] = 0;
5626             while( true )
5627             {
5628                 ++iDim;
5629                 goto lbl_next_depth;
5630 lbl_return_to_caller:
5631                 --iDim;
5632                 --anStackIters[iDim];
5633                 if( anStackIters[iDim] == 0 )
5634                     break;
5635                 ++anOtherDimCoord[iDim];
5636             }
5637         }
5638         else
5639         {
5640             SetBand(nBands + 1, new GDALRasterBandFromArray(this, anOtherDimCoord));
5641         }
5642         if( iDim > 0 )
5643             goto lbl_return_to_caller;
5644     }
5645 
GetGeoTransform(double * padfGeoTransform)5646     CPLErr GetGeoTransform(double* padfGeoTransform) override
5647     {
5648         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
5649         return m_bHasGT ? CE_None : CE_Failure;
5650     }
5651 
GetSpatialRef() const5652     const OGRSpatialReference* GetSpatialRef() const override
5653     {
5654         if( m_poArray->GetDimensionCount() < 2 )
5655             return nullptr;
5656         m_poSRS = m_poArray->GetSpatialRef();
5657         if( m_poSRS )
5658         {
5659             m_poSRS.reset(m_poSRS->Clone());
5660             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
5661             for( auto& m: axisMapping )
5662             {
5663                 if( m == static_cast<int>(m_iXDim) + 1 )
5664                     m = 1;
5665                 else if( m == static_cast<int>(m_iYDim) + 1 )
5666                     m = 2;
5667                 else
5668                     m = 0;
5669             }
5670             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
5671         }
5672         return m_poSRS.get();
5673     }
5674 };
5675 
5676 /************************************************************************/
5677 /*                      GDALRasterBandFromArray()                       */
5678 /************************************************************************/
5679 
GDALRasterBandFromArray(GDALDatasetFromArray * poDSIn,const std::vector<GUInt64> & anOtherDimCoord)5680 GDALRasterBandFromArray::GDALRasterBandFromArray(
5681                                     GDALDatasetFromArray* poDSIn,
5682                                     const std::vector<GUInt64>& anOtherDimCoord)
5683 {
5684     const auto& poArray(poDSIn->m_poArray);
5685     const auto& dims(poArray->GetDimensions());
5686     const auto nDimCount(dims.size());
5687     const auto blockSize(poArray->GetBlockSize());
5688     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim]) ? static_cast<int>(
5689         std::min(static_cast<GUInt64>(INT_MAX), blockSize[poDSIn->m_iYDim])) : 1;
5690     nBlockXSize = blockSize[poDSIn->m_iXDim] ? static_cast<int>(
5691         std::min(static_cast<GUInt64>(INT_MAX), blockSize[poDSIn->m_iXDim])) :
5692         poDSIn->GetRasterXSize();
5693     eDataType = poArray->GetDataType().GetNumericDataType();
5694     eAccess = poDSIn->eAccess;
5695     m_anOffset.resize(nDimCount);
5696     m_anCount.resize(nDimCount, 1);
5697     m_anStride.resize(nDimCount);
5698     for( size_t i = 0, j = 0; i < nDimCount; ++i )
5699     {
5700         if( i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim) )
5701         {
5702             std::string dimName(dims[i]->GetName());
5703             GUInt64 nIndex = anOtherDimCoord[j];
5704             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
5705             // subsetted dimensions as generated by GetView()
5706             if( STARTS_WITH(dimName.c_str(), "subset_") )
5707             {
5708                 CPLStringList aosTokens(CSLTokenizeString2(dimName.c_str(), "_", 0));
5709                 if( aosTokens.size() == 5 )
5710                 {
5711                     dimName = aosTokens[1];
5712                     const auto nStartDim = static_cast<GUInt64>(
5713                         CPLScanUIntBig(aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
5714                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
5715                     nIndex = nIncrDim > 0 ?
5716                         nStartDim + nIndex * nIncrDim :
5717                         nStartDim - (nIndex * -nIncrDim);
5718                 }
5719             }
5720             SetMetadataItem(
5721                 CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
5722                 CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)) );
5723             auto indexingVar = dims[i]->GetIndexingVariable();
5724             if( indexingVar && indexingVar->GetDimensionCount() == 1 &&
5725                 indexingVar->GetDimensions()[0]->GetSize() == dims[i]->GetSize() )
5726             {
5727                 size_t nCount = 1;
5728                 const auto& dt(indexingVar->GetDataType());
5729                 std::vector<GByte> abyTmp(dt.GetSize());
5730                 if( indexingVar->Read(&(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
5731                                       dt, &abyTmp[0]) )
5732                 {
5733                     char* pszTmp = nullptr;
5734                     GDALExtendedDataType::CopyValue(
5735                         &abyTmp[0], dt,
5736                         &pszTmp, GDALExtendedDataType::CreateString());
5737                     if( pszTmp )
5738                     {
5739                          SetMetadataItem(
5740                             CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
5741                             pszTmp );
5742                         CPLFree(pszTmp);
5743                     }
5744 
5745                     const auto unit(indexingVar->GetUnit());
5746                     if( !unit.empty() )
5747                     {
5748                          SetMetadataItem(
5749                             CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
5750                             unit.c_str() );
5751                     }
5752                 }
5753             }
5754             m_anOffset[i] = anOtherDimCoord[j];
5755             j++;
5756         }
5757     }
5758 }
5759 
5760 /************************************************************************/
5761 /*                           GetNoDataValue()                           */
5762 /************************************************************************/
5763 
GetNoDataValue(int * pbHasNoData)5764 double GDALRasterBandFromArray::GetNoDataValue(int* pbHasNoData)
5765 {
5766     auto l_poDS(cpl::down_cast<GDALDatasetFromArray*>(poDS));
5767     const auto& poArray(l_poDS->m_poArray);
5768     bool bHasNodata = false;
5769     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
5770     if( pbHasNoData )
5771         *pbHasNoData = bHasNodata;
5772     return dfRes;
5773 }
5774 
5775 /************************************************************************/
5776 /*                             GetOffset()                              */
5777 /************************************************************************/
5778 
GetOffset(int * pbHasOffset)5779 double GDALRasterBandFromArray::GetOffset(int* pbHasOffset)
5780 {
5781     auto l_poDS(cpl::down_cast<GDALDatasetFromArray*>(poDS));
5782     const auto& poArray(l_poDS->m_poArray);
5783     bool bHasValue = false;
5784     double dfRes = poArray->GetOffset(&bHasValue);
5785     if( pbHasOffset )
5786         *pbHasOffset = bHasValue;
5787     return dfRes;
5788 }
5789 
5790 /************************************************************************/
5791 /*                           GetUnitType()                              */
5792 /************************************************************************/
5793 
GetUnitType()5794 const char* GDALRasterBandFromArray::GetUnitType()
5795 {
5796     auto l_poDS(cpl::down_cast<GDALDatasetFromArray*>(poDS));
5797     const auto& poArray(l_poDS->m_poArray);
5798     return poArray->GetUnit().c_str();
5799 }
5800 
5801 /************************************************************************/
5802 /*                             GetScale()                              */
5803 /************************************************************************/
5804 
GetScale(int * pbHasScale)5805 double GDALRasterBandFromArray::GetScale(int* pbHasScale)
5806 {
5807     auto l_poDS(cpl::down_cast<GDALDatasetFromArray*>(poDS));
5808     const auto& poArray(l_poDS->m_poArray);
5809     bool bHasValue = false;
5810     double dfRes = poArray->GetScale(&bHasValue);
5811     if( pbHasScale )
5812         *pbHasScale = bHasValue;
5813     return dfRes;
5814 }
5815 
5816 /************************************************************************/
5817 /*                            IReadBlock()                              */
5818 /************************************************************************/
5819 
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)5820 CPLErr GDALRasterBandFromArray::IReadBlock( int nBlockXOff,
5821                                             int nBlockYOff,
5822                                             void * pImage )
5823 {
5824     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
5825     const int nXOff = nBlockXOff * nBlockXSize;
5826     const int nYOff = nBlockYOff * nBlockYSize;
5827     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
5828     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
5829     GDALRasterIOExtraArg sExtraArg;
5830     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
5831     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
5832                      pImage, nReqXSize, nReqYSize, eDataType,
5833                      nDTSize, nDTSize * nBlockXSize, &sExtraArg);
5834 }
5835 
5836 /************************************************************************/
5837 /*                            IWriteBlock()                             */
5838 /************************************************************************/
5839 
IWriteBlock(int nBlockXOff,int nBlockYOff,void * pImage)5840 CPLErr GDALRasterBandFromArray::IWriteBlock( int nBlockXOff,
5841                                              int nBlockYOff,
5842                                              void * pImage )
5843 {
5844     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
5845     const int nXOff = nBlockXOff * nBlockXSize;
5846     const int nYOff = nBlockYOff * nBlockYSize;
5847     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
5848     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
5849     GDALRasterIOExtraArg sExtraArg;
5850     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
5851     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize,
5852                      pImage, nReqXSize, nReqYSize, eDataType,
5853                      nDTSize, nDTSize * nBlockXSize, &sExtraArg);
5854 }
5855 
5856 /************************************************************************/
5857 /*                            IRasterIO()                               */
5858 /************************************************************************/
5859 
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpaceBuf,GSpacing nLineSpaceBuf,GDALRasterIOExtraArg * psExtraArg)5860 CPLErr GDALRasterBandFromArray::IRasterIO( GDALRWFlag eRWFlag,
5861                                   int nXOff, int nYOff, int nXSize, int nYSize,
5862                                   void * pData, int nBufXSize, int nBufYSize,
5863                                   GDALDataType eBufType,
5864                                   GSpacing nPixelSpaceBuf,
5865                                   GSpacing nLineSpaceBuf,
5866                                   GDALRasterIOExtraArg* psExtraArg )
5867 {
5868     auto l_poDS(cpl::down_cast<GDALDatasetFromArray*>(poDS));
5869     const auto& poArray(l_poDS->m_poArray);
5870     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
5871     if( nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
5872         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
5873         (nLineSpaceBuf % nBufferDTSize) == 0 )
5874     {
5875         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
5876         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
5877         m_anStride[l_poDS->m_iXDim] =
5878             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
5879         if( poArray->GetDimensionCount() >= 2 )
5880         {
5881             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
5882             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
5883             m_anStride[l_poDS->m_iYDim] =
5884                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
5885         }
5886         if( eRWFlag == GF_Read )
5887         {
5888             return poArray->Read(m_anOffset.data(),
5889                                  m_anCount.data(),
5890                                  nullptr, m_anStride.data(),
5891                                  GDALExtendedDataType::Create(eBufType), pData) ?
5892                                  CE_None : CE_Failure;
5893         }
5894         else
5895         {
5896             return poArray->Write(m_anOffset.data(),
5897                                   m_anCount.data(),
5898                                   nullptr, m_anStride.data(),
5899                                   GDALExtendedDataType::Create(eBufType), pData) ?
5900                                   CE_None : CE_Failure;
5901         }
5902     }
5903     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
5904                                      pData, nBufXSize, nBufYSize,
5905                                      eBufType,
5906                                      nPixelSpaceBuf, nLineSpaceBuf,
5907                                      psExtraArg);
5908 }
5909 
5910 /************************************************************************/
5911 /*                          AsClassicDataset()                         */
5912 /************************************************************************/
5913 
5914 /** Return a view of this array as a "classic" GDALDataset (ie 2D)
5915  *
5916  * In the case of > 2D arrays, additional dimensions will be represented as
5917  * raster bands.
5918  *
5919  * The "reverse" method is GDALRasterBand::AsMDArray().
5920  *
5921  * This is the same as the C function GDALMDArrayAsClassicDataset().
5922  *
5923  * @param iXDim Index of the dimension that will be used as the X/width axis.
5924  * @param iYDim Index of the dimension that will be used as the Y/height axis.
5925  *              Ignored if the dimension count is 1.
5926  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
5927  */
AsClassicDataset(size_t iXDim,size_t iYDim) const5928 GDALDataset* GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim) const
5929 {
5930     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
5931     if( !self )
5932     {
5933         CPLError(CE_Failure, CPLE_AppDefined,
5934                 "Driver implementation issue: m_pSelf not set !");
5935         return nullptr;
5936     }
5937     const auto nDimCount(GetDimensionCount());
5938     if( nDimCount == 0 )
5939     {
5940         CPLError(CE_Failure, CPLE_NotSupported,
5941                  "Unsupported number of dimensions");
5942         return nullptr;
5943     }
5944     if( GetDataType().GetClass() != GEDTC_NUMERIC ||
5945         GetDataType().GetNumericDataType() == GDT_Unknown )
5946     {
5947         CPLError(CE_Failure, CPLE_NotSupported,
5948                  "Only arrays with numeric data types "
5949                  "can be exposed as classic GDALDataset");
5950         return nullptr;
5951     }
5952     if( iXDim >= nDimCount ||
5953         (nDimCount >=2 && (iYDim >= nDimCount || iXDim == iYDim)) )
5954     {
5955         CPLError(CE_Failure, CPLE_NotSupported,
5956                  "Invalid iXDim and/or iYDim");
5957         return nullptr;
5958     }
5959     GUInt64 nBands = 1;
5960     const auto& dims(GetDimensions());
5961     for( size_t i = 0; i < nDimCount; ++i )
5962     {
5963         if( i != iXDim && !(nDimCount >= 2 && i == iYDim) )
5964         {
5965             if( dims[i]->GetSize() > 65536 / nBands )
5966             {
5967                 CPLError(CE_Failure, CPLE_AppDefined,
5968                          "Too many bands. Operate on a sliced view");
5969                 return nullptr;
5970             }
5971             nBands *= dims[i]->GetSize();
5972         }
5973     }
5974     return new GDALDatasetFromArray(self, iXDim, iYDim);
5975 }
5976 
5977 /************************************************************************/
5978 /*                           GetStatistics()                            */
5979 /************************************************************************/
5980 
5981 /**
5982  * \brief Fetch statistics.
5983  *
5984  * Returns the minimum, maximum, mean and standard deviation of all
5985  * pixel values in this array.
5986  *
5987  * If bForce is FALSE results will only be returned if it can be done
5988  * quickly (i.e. without scanning the data).  If bForce is FALSE and
5989  * results cannot be returned efficiently, the method will return CE_Warning
5990  * but no warning will have been issued.   This is a non-standard use of
5991  * the CE_Warning return value to indicate "nothing done".
5992  *
5993  * When cached statistics are not available, and bForce is TRUE,
5994  * ComputeStatistics() is called.
5995  *
5996  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
5997  * will generally cache statistics in the .aux.xml file allowing fast fetch
5998  * after the first request.
5999  *
6000  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
6001  *
6002  * This method is the same as the C function GDALMDArrayGetStatistics().
6003  *
6004  * @param poDS Owing dataset. If set to NULL, this method will always call
6005  * ComputeStatistics(). If set to non-NULL, the method will attempt to retrieve
6006  * from it (generally its persistent auxiliary metadata) already cached
6007  * statistics.
6008  *
6009  * @param bApproxOK Currently ignored. In the future, should be set to true
6010  * if statistics on the whole array are wished, or to false if a subset of it
6011  * may be used.
6012  *
6013  * @param bForce If false statistics will only be returned if it can
6014  * be done without rescanning the image.
6015  *
6016  * @param pdfMin Location into which to load image minimum (may be NULL).
6017  *
6018  * @param pdfMax Location into which to load image maximum (may be NULL).-
6019  *
6020  * @param pdfMean Location into which to load image mean (may be NULL).
6021  *
6022  * @param pdfStdDev Location into which to load image standard deviation
6023  * (may be NULL).
6024  *
6025  * @param pnValidCount Number of samples whose value is different from the nodata
6026  * value. (may be NULL)
6027  *
6028  * @param pfnProgress a function to call to report progress, or NULL.
6029  *
6030  * @param pProgressData application data to pass to the progress function.
6031  *
6032  * @return CE_None on success, CE_Warning if no values returned,
6033  * CE_Failure if an error occurs.
6034  *
6035  * @since GDAL 3.2
6036  */
6037 
GetStatistics(GDALDataset * poDS,bool bApproxOK,bool bForce,double * pdfMin,double * pdfMax,double * pdfMean,double * pdfStdDev,GUInt64 * pnValidCount,GDALProgressFunc pfnProgress,void * pProgressData)6038 CPLErr GDALMDArray::GetStatistics( GDALDataset* poDS,
6039                                    bool bApproxOK, bool bForce,
6040                                    double *pdfMin, double *pdfMax,
6041                                    double *pdfMean, double *pdfStdDev,
6042                                    GUInt64* pnValidCount,
6043                                    GDALProgressFunc pfnProgress, void *pProgressData )
6044 {
6045     GDALPamDataset* poPamDS = dynamic_cast<GDALPamDataset*>(poDS);
6046     if( poPamDS != nullptr )
6047     {
6048         bool bGotApproxStats = false;
6049         if( poPamDS->GetMDArrayStatistics(GetFullName().c_str(),
6050                                           &bGotApproxStats,
6051                                           pdfMin, pdfMax,
6052                                           pdfMean, pdfStdDev,
6053                                           pnValidCount) )
6054         {
6055             if( bApproxOK )
6056                 return CE_None;
6057             if( !bGotApproxStats )
6058                 return CE_None;
6059         }
6060     }
6061 
6062     if( !bForce )
6063         return CE_Warning;
6064 
6065     return ComputeStatistics(poDS, bApproxOK, pdfMin, pdfMax, pdfMean,
6066                              pdfStdDev, pnValidCount,
6067                              pfnProgress, pProgressData)
6068                 ? CE_None: CE_Failure;
6069 }
6070 
6071 /************************************************************************/
6072 /*                         ComputeStatistics()                          */
6073 /************************************************************************/
6074 
6075 /**
6076  * \brief Compute statistics.
6077  *
6078  * Returns the minimum, maximum, mean and standard deviation of all
6079  * pixel values in this array.
6080  *
6081  * Pixels taken into account in statistics are those whose mask value
6082  * (as determined by GetMask()) is non-zero.
6083  *
6084  * Once computed, the statistics will generally be "set" back on the
6085  * owing dataset (if poDS is not NULL).
6086  *
6087  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
6088  *
6089  * This method is the same as the C function GDALMDArrayComputeStatistics().
6090  *
6091  * @param poDS Owing dataset. If set to non-NULL, the method will attempt to
6092  * store computed statistics in it (generally its persistent auxiliary metadata)
6093  * for further retrieval by GetStatistics()
6094  *
6095  * @param bApproxOK Currently ignored. In the future, should be set to true
6096  * if statistics on the whole array are wished, or to false if a subset of it
6097  * may be used.
6098  *
6099  * @param pdfMin Location into which to load image minimum (may be NULL).
6100  *
6101  * @param pdfMax Location into which to load image maximum (may be NULL).-
6102  *
6103  * @param pdfMean Location into which to load image mean (may be NULL).
6104  *
6105  * @param pdfStdDev Location into which to load image standard deviation
6106  * (may be NULL).
6107  *
6108  * @param pnValidCount Number of samples whose value is different from the nodata
6109  * value. (may be NULL)
6110  *
6111  * @param pfnProgress a function to call to report progress, or NULL.
6112  *
6113  * @param pProgressData application data to pass to the progress function.
6114  *
6115  * @return true on success
6116  *
6117  * @since GDAL 3.2
6118  */
6119 
ComputeStatistics(GDALDataset * poDS,bool bApproxOK,double * pdfMin,double * pdfMax,double * pdfMean,double * pdfStdDev,GUInt64 * pnValidCount,GDALProgressFunc pfnProgress,void * pProgressData)6120 bool GDALMDArray::ComputeStatistics( GDALDataset* poDS,
6121                                     bool bApproxOK,
6122                                     double *pdfMin, double *pdfMax,
6123                                     double *pdfMean, double *pdfStdDev,
6124                                     GUInt64* pnValidCount,
6125                                     GDALProgressFunc pfnProgress, void *pProgressData )
6126 {
6127     struct StatsPerChunkType
6128     {
6129         const GDALMDArray* array = nullptr;
6130         std::shared_ptr<GDALMDArray> poMask{};
6131         double dfMin = std::numeric_limits<double>::max();
6132         double dfMax = -std::numeric_limits<double>::max();
6133         double dfMean = 0.0;
6134         double dfM2 = 0.0;
6135         GUInt64 nValidCount = 0;
6136         std::vector<GByte> abyData{};
6137         std::vector<double> adfData{};
6138         std::vector<GByte> abyMaskData{};
6139         GDALProgressFunc pfnProgress = nullptr;
6140         void* pProgressData = nullptr;
6141     };
6142 
6143     const auto PerChunkFunc = [](GDALAbstractMDArray*,
6144                                  const GUInt64* chunkArrayStartIdx,
6145                                  const size_t* chunkCount,
6146                                  GUInt64 iCurChunk,
6147                                  GUInt64 nChunkCount,
6148                                  void* pUserData)
6149     {
6150         StatsPerChunkType* data = static_cast<StatsPerChunkType*>(pUserData);
6151         const GDALMDArray* array = data->array;
6152         const GDALMDArray* poMask = data->poMask.get();
6153         const size_t nDims = array->GetDimensionCount();
6154         size_t nVals = 1;
6155         for( size_t i = 0; i < nDims; i++ )
6156             nVals *= chunkCount[i];
6157 
6158         // Get mask
6159         data->abyMaskData.resize(nVals);
6160         if( !(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
6161                            poMask->GetDataType(), &data->abyMaskData[0])) )
6162         {
6163             return false;
6164         }
6165 
6166         // Get data
6167         const auto& oType = array->GetDataType();
6168         if( oType.GetNumericDataType() == GDT_Float64 )
6169         {
6170             data->adfData.resize(nVals);
6171             if( !array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
6172                             oType, &data->adfData[0]) )
6173             {
6174                 return false;
6175             }
6176         }
6177         else
6178         {
6179             data->abyData.resize(nVals * oType.GetSize());
6180             if( !array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
6181                             oType, &data->abyData[0]) )
6182             {
6183                 return false;
6184             }
6185             data->adfData.resize(nVals);
6186             GDALCopyWords64( &data->abyData[0], oType.GetNumericDataType(),
6187                              static_cast<int>(oType.GetSize()),
6188                              &data->adfData[0], GDT_Float64,
6189                              static_cast<int>(sizeof(double)),
6190                              static_cast<GPtrDiff_t>(nVals) );
6191         }
6192         for( size_t i = 0; i < nVals; i++ )
6193         {
6194             if( data->abyMaskData[i] )
6195             {
6196                 const double dfValue = data->adfData[i];
6197                 data->dfMin = std::min(data->dfMin, dfValue);
6198                 data->dfMax = std::max(data->dfMax, dfValue);
6199                 data->nValidCount++;
6200                 const double dfDelta = dfValue - data->dfMean;
6201                 data->dfMean += dfDelta / data->nValidCount;
6202                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
6203             }
6204         }
6205         if( data->pfnProgress &&
6206             !data->pfnProgress(static_cast<double>(iCurChunk+1) / nChunkCount,
6207                                "", data->pProgressData) )
6208         {
6209             return false;
6210         }
6211         return true;
6212     };
6213 
6214     const auto& oType = GetDataType();
6215     if( oType.GetClass() != GEDTC_NUMERIC ||
6216         GDALDataTypeIsComplex(oType.GetNumericDataType()) )
6217     {
6218         CPLError(CE_Failure, CPLE_NotSupported,
6219                  "Statistics can only be computed on non-complex numeric data type");
6220         return false;
6221     }
6222 
6223     const size_t nDims = GetDimensionCount();
6224     std::vector<GUInt64> arrayStartIdx(nDims);
6225     std::vector<GUInt64> count(nDims);
6226     const auto& poDims = GetDimensions();
6227     for( size_t i = 0; i < nDims; i++ )
6228     {
6229         count[i] = poDims[i]->GetSize();
6230     }
6231     const char* pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
6232     const size_t nMaxChunkSize = pszSwathSize ?
6233         static_cast<size_t>(
6234             std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
6235                         CPLAtoGIntBig(pszSwathSize))) :
6236         static_cast<size_t>(
6237             std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
6238                         GDALGetCacheMax64() / 4));
6239     StatsPerChunkType sData;
6240     sData.array = this;
6241     sData.poMask = GetMask(nullptr);
6242     if( sData.poMask == nullptr )
6243     {
6244         return false;
6245     }
6246     sData.pfnProgress = pfnProgress;
6247     sData.pProgressData = pProgressData;
6248     if( !ProcessPerChunk(arrayStartIdx.data(), count.data(),
6249                          GetProcessingChunkSize(nMaxChunkSize).data(),
6250                          PerChunkFunc, &sData) )
6251     {
6252         return false;
6253     }
6254 
6255     if( pdfMin )
6256         *pdfMin = sData.dfMin;
6257 
6258     if( pdfMax )
6259         *pdfMax = sData.dfMax;
6260 
6261     if( pdfMean )
6262         *pdfMean = sData.dfMean;
6263 
6264     const double dfStdDev = sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
6265     if( pdfStdDev )
6266         *pdfStdDev = dfStdDev;
6267 
6268     if( pnValidCount )
6269         *pnValidCount = sData.nValidCount;
6270 
6271     if( poDS )
6272     {
6273         SetStatistics(poDS, bApproxOK,
6274                       sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
6275                       sData.nValidCount);
6276     }
6277 
6278     return true;
6279 }
6280 
SetStatistics(GDALDataset * poDS,bool bApproxStats,double dfMin,double dfMax,double dfMean,double dfStdDev,GUInt64 nValidCount)6281 bool GDALMDArray::SetStatistics( GDALDataset* poDS,
6282                                  bool bApproxStats,
6283                                  double dfMin, double dfMax,
6284                                  double dfMean, double dfStdDev,
6285                                  GUInt64 nValidCount )
6286 {
6287     if( poDS == nullptr )
6288     {
6289         CPLError(CE_Failure, CPLE_IllegalArg,
6290                  "Dataset should be defined to enable serialization");
6291         return false;
6292     }
6293 
6294     GDALPamDataset* poPamDS = dynamic_cast<GDALPamDataset*>(poDS);
6295     if( poPamDS == nullptr )
6296     {
6297         CPLDebug("GDAL", "Cannot save statistics on a non-PAM dataset");
6298         return false;
6299     }
6300 
6301     poPamDS->StoreMDArrayStatistics(GetFullName().c_str(),
6302                                     bApproxStats,
6303                                     dfMin, dfMax,
6304                                     dfMean, dfStdDev,
6305                                     nValidCount );
6306 
6307     return false;
6308 }
6309 
6310 
6311 /************************************************************************/
6312 /*                       ~GDALExtendedDataType()                        */
6313 /************************************************************************/
6314 
6315 GDALExtendedDataType::~GDALExtendedDataType() = default;
6316 
6317 /************************************************************************/
6318 /*                        GDALExtendedDataType()                        */
6319 /************************************************************************/
6320 
GDALExtendedDataType(size_t nMaxStringLength)6321 GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength):
6322     m_eClass(GEDTC_STRING),
6323     m_nSize(sizeof(char*)),
6324     m_nMaxStringLength(nMaxStringLength)
6325 {}
6326 
6327 /************************************************************************/
6328 /*                        GDALExtendedDataType()                        */
6329 /************************************************************************/
6330 
GDALExtendedDataType(GDALDataType eType)6331 GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType):
6332     m_eClass(GEDTC_NUMERIC),
6333     m_eNumericDT(eType),
6334     m_nSize(GDALGetDataTypeSizeBytes(eType))
6335 {}
6336 
6337 /************************************************************************/
6338 /*                        GDALExtendedDataType()                        */
6339 /************************************************************************/
6340 
GDALExtendedDataType(const std::string & osName,size_t nTotalSize,std::vector<std::unique_ptr<GDALEDTComponent>> && components)6341 GDALExtendedDataType::GDALExtendedDataType(
6342     const std::string& osName,
6343     size_t nTotalSize,
6344     std::vector<std::unique_ptr<GDALEDTComponent>>&& components):
6345     m_osName(osName),
6346     m_eClass(GEDTC_COMPOUND),
6347     m_aoComponents(std::move(components)),
6348     m_nSize(nTotalSize)
6349 {
6350 }
6351 
6352 /************************************************************************/
6353 /*                        GDALExtendedDataType()                        */
6354 /************************************************************************/
6355 
6356 /** Copy constructor. */
GDALExtendedDataType(const GDALExtendedDataType & other)6357 GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType& other):
6358     m_osName(other.m_osName),
6359     m_eClass(other.m_eClass),
6360     m_eNumericDT(other.m_eNumericDT),
6361     m_nSize(other.m_nSize),
6362     m_nMaxStringLength(other.m_nMaxStringLength)
6363 {
6364     if( m_eClass == GEDTC_COMPOUND )
6365     {
6366         for( const auto& elt: other.m_aoComponents )
6367         {
6368             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
6369         }
6370     }
6371 }
6372 
6373 /************************************************************************/
6374 /*                            operator= ()                              */
6375 /************************************************************************/
6376 
6377 /** Move assignment. */
operator =(GDALExtendedDataType && other)6378 GDALExtendedDataType& GDALExtendedDataType::operator= (GDALExtendedDataType&& other)
6379 {
6380     m_osName = std::move(other.m_osName);
6381     m_eClass = other.m_eClass;
6382     m_eNumericDT = other.m_eNumericDT;
6383     m_nSize = other.m_nSize;
6384     m_nMaxStringLength = other.m_nMaxStringLength;
6385     m_aoComponents = std::move(other.m_aoComponents);
6386     other.m_eClass = GEDTC_NUMERIC;
6387     other.m_eNumericDT = GDT_Unknown;
6388     other.m_nSize = 0;
6389     other.m_nMaxStringLength = 0;
6390     return *this;
6391 }
6392 
6393 /************************************************************************/
6394 /*                           Create()                                   */
6395 /************************************************************************/
6396 
6397 /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
6398  *
6399  * This is the same as the C function GDALExtendedDataTypeCreate()
6400  *
6401  * @param eType Numeric data type.
6402  */
Create(GDALDataType eType)6403 GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
6404 {
6405     return GDALExtendedDataType(eType);
6406 }
6407 
6408 /************************************************************************/
6409 /*                           Create()                                   */
6410 /************************************************************************/
6411 
6412 /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
6413  *
6414  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
6415  *
6416  * @param osName Type name.
6417  * @param nTotalSize Total size of the type in bytes.
6418  *                   Should be large enough to store all components.
6419  * @param components Components of the compound type.
6420  */
Create(const std::string & osName,size_t nTotalSize,std::vector<std::unique_ptr<GDALEDTComponent>> && components)6421 GDALExtendedDataType GDALExtendedDataType::Create(
6422     const std::string& osName,
6423     size_t nTotalSize,
6424     std::vector<std::unique_ptr<GDALEDTComponent>>&& components)
6425 {
6426     size_t nLastOffset = 0;
6427     // Some arbitrary threshold to avoid potential integer overflows
6428     if( nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2) )
6429     {
6430         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
6431         return GDALExtendedDataType(GDT_Unknown);
6432     }
6433     for( const auto& comp: components )
6434     {
6435         // Check alignment too ?
6436         if( comp->GetOffset() < nLastOffset )
6437         {
6438             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
6439             return GDALExtendedDataType(GDT_Unknown);
6440         }
6441         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
6442     }
6443     if( nTotalSize < nLastOffset )
6444     {
6445         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
6446         return GDALExtendedDataType(GDT_Unknown);
6447     }
6448     if( nTotalSize == 0 || components.empty() )
6449     {
6450         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
6451         return GDALExtendedDataType(GDT_Unknown);
6452     }
6453     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
6454 }
6455 
6456 /************************************************************************/
6457 /*                           Create()                                   */
6458 /************************************************************************/
6459 
6460 /** Return a new GDALExtendedDataType of class GEDTC_STRING.
6461  *
6462  * This is the same as the C function GDALExtendedDataTypeCreateString().
6463  *
6464  * @param nMaxStringLength maximum length of a string in bytes. 0 if unknown/unlimited
6465  */
CreateString(size_t nMaxStringLength)6466 GDALExtendedDataType GDALExtendedDataType::CreateString(size_t nMaxStringLength)
6467 {
6468     return GDALExtendedDataType(nMaxStringLength);
6469 }
6470 
6471 /************************************************************************/
6472 /*                           operator==()                               */
6473 /************************************************************************/
6474 
6475 /** Equality operator.
6476  *
6477  * This is the same as the C function GDALExtendedDataTypeEquals().
6478  */
operator ==(const GDALExtendedDataType & other) const6479 bool GDALExtendedDataType::operator==(const GDALExtendedDataType& other) const
6480 {
6481     if( m_eClass != other.m_eClass ||
6482         m_nSize != other.m_nSize ||
6483         m_osName != other.m_osName )
6484     {
6485         return false;
6486     }
6487     if( m_eClass == GEDTC_NUMERIC )
6488     {
6489         return m_eNumericDT == other.m_eNumericDT;
6490     }
6491     if( m_eClass == GEDTC_STRING )
6492     {
6493         return true;
6494     }
6495     CPLAssert( m_eClass == GEDTC_COMPOUND );
6496     if( m_aoComponents.size() != other.m_aoComponents.size() )
6497     {
6498         return false;
6499     }
6500     for( size_t i = 0; i < m_aoComponents.size(); i++ )
6501     {
6502         if( !(*m_aoComponents[i] == *other.m_aoComponents[i]) )
6503         {
6504             return false;
6505         }
6506     }
6507     return true;
6508 }
6509 
6510 /************************************************************************/
6511 /*                        CanConvertTo()                                */
6512 /************************************************************************/
6513 
6514 /** Return whether this data type can be converted to the other one.
6515  *
6516  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
6517  *
6518  * @param other Target data type for the conversion being considered.
6519  */
CanConvertTo(const GDALExtendedDataType & other) const6520 bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType& other) const
6521 {
6522     if( m_eClass == GEDTC_NUMERIC )
6523     {
6524         if( m_eNumericDT == GDT_Unknown )
6525             return false;
6526         if( other.m_eClass == GEDTC_NUMERIC && other.m_eNumericDT == GDT_Unknown )
6527             return false;
6528         return other.m_eClass == GEDTC_NUMERIC || other.m_eClass == GEDTC_STRING;
6529     }
6530     if( m_eClass == GEDTC_STRING )
6531     {
6532         return other.m_eClass == m_eClass;
6533     }
6534     CPLAssert( m_eClass == GEDTC_COMPOUND );
6535     if( other.m_eClass != GEDTC_COMPOUND )
6536         return false;
6537     std::map<std::string, const std::unique_ptr<GDALEDTComponent>*> srcComponents;
6538     for( const auto& srcComp: m_aoComponents )
6539     {
6540         srcComponents[srcComp->GetName()] = &srcComp;
6541     }
6542     for( const auto& dstComp: other.m_aoComponents )
6543     {
6544         auto oIter = srcComponents.find(dstComp->GetName());
6545         if( oIter == srcComponents.end() )
6546             return false;
6547         if( !(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()) )
6548             return false;
6549     }
6550     return true;
6551 }
6552 
6553 /************************************************************************/
6554 /*                     NeedsFreeDynamicMemory()                         */
6555 /************************************************************************/
6556 
6557 /** Return whether the data type holds dynamically allocated memory, that
6558  * needs to be freed with FreeDynamicMemory().
6559  *
6560  */
NeedsFreeDynamicMemory() const6561 bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
6562 {
6563     switch( m_eClass )
6564     {
6565         case GEDTC_STRING:
6566             return true;
6567 
6568         case GEDTC_NUMERIC:
6569             return false;
6570 
6571         case GEDTC_COMPOUND:
6572         {
6573             for( const auto& comp: m_aoComponents )
6574             {
6575                 if( comp->GetType().NeedsFreeDynamicMemory() )
6576                     return true;
6577             }
6578         }
6579     }
6580     return false;
6581 }
6582 
6583 /************************************************************************/
6584 /*                        FreeDynamicMemory()                           */
6585 /************************************************************************/
6586 
6587 /** Release the dynamic memory (strings typically) from a raw value.
6588  *
6589  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
6590  *
6591  * @param pBuffer Raw buffer of a single element of an attribute or array value.
6592  */
FreeDynamicMemory(void * pBuffer) const6593 void GDALExtendedDataType::FreeDynamicMemory(void* pBuffer) const
6594 {
6595     switch( m_eClass )
6596     {
6597         case GEDTC_STRING:
6598         {
6599             char* pszStr;
6600             memcpy(&pszStr, pBuffer, sizeof(char*));
6601             if( pszStr )
6602             {
6603                 VSIFree(pszStr);
6604             }
6605             break;
6606         }
6607 
6608         case GEDTC_NUMERIC:
6609         {
6610             break;
6611         }
6612 
6613         case GEDTC_COMPOUND:
6614         {
6615             GByte* pabyBuffer = static_cast<GByte*>(pBuffer);
6616             for( const auto& comp: m_aoComponents )
6617             {
6618                 comp->GetType().FreeDynamicMemory(pabyBuffer + comp->GetOffset());
6619             }
6620             break;
6621         }
6622     }
6623 }
6624 
6625 
6626 /************************************************************************/
6627 /*                      ~GDALEDTComponent()                             */
6628 /************************************************************************/
6629 
6630 GDALEDTComponent::~GDALEDTComponent() = default;
6631 
6632 /************************************************************************/
6633 /*                      GDALEDTComponent()                              */
6634 /************************************************************************/
6635 
6636 /** constructor of a GDALEDTComponent
6637  *
6638  * This is the same as the C function GDALEDTComponendCreate()
6639  *
6640  * @param name Component name
6641  * @param offset Offset in byte of the component in the compound data type.
6642  *               In case of nesting of compound data type, this should be
6643  *               the offset to the immediate belonging data type, not to the
6644  *               higher level one.
6645  * @param type   Component data type.
6646  */
GDALEDTComponent(const std::string & name,size_t offset,const GDALExtendedDataType & type)6647 GDALEDTComponent::GDALEDTComponent(const std::string& name,
6648                                    size_t offset,
6649                                    const GDALExtendedDataType& type):
6650     m_osName(name),
6651     m_nOffset(offset),
6652     m_oType(type)
6653 {}
6654 
6655 /************************************************************************/
6656 /*                      GDALEDTComponent()                              */
6657 /************************************************************************/
6658 
6659 /** Copy constructor. */
6660 GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent&) = default;
6661 
6662 /************************************************************************/
6663 /*                           operator==()                               */
6664 /************************************************************************/
6665 
6666 /** Equality operator.
6667  */
operator ==(const GDALEDTComponent & other) const6668 bool GDALEDTComponent::operator==(const GDALEDTComponent& other) const
6669 {
6670     return m_osName == other.m_osName &&
6671            m_nOffset == other.m_nOffset &&
6672            m_oType == other.m_oType;
6673 }
6674 
6675 /************************************************************************/
6676 /*                        ~GDALDimension()                              */
6677 /************************************************************************/
6678 
6679 GDALDimension::~GDALDimension() = default;
6680 
6681 /************************************************************************/
6682 /*                         GDALDimension()                              */
6683 /************************************************************************/
6684 
6685 //! @cond Doxygen_Suppress
6686 /** Constructor.
6687  *
6688  * @param osParentName Parent name
6689  * @param osName name
6690  * @param osType type. See GetType().
6691  * @param osDirection direction. See GetDirection().
6692  * @param nSize size.
6693  */
GDALDimension(const std::string & osParentName,const std::string & osName,const std::string & osType,const std::string & osDirection,GUInt64 nSize)6694 GDALDimension::GDALDimension(const std::string& osParentName,
6695                              const std::string& osName,
6696                              const std::string& osType,
6697                              const std::string& osDirection,
6698                              GUInt64 nSize):
6699     m_osName(osName),
6700     m_osFullName(!osParentName.empty() ? ((osParentName == "/" ? "/" : osParentName + "/") + osName) : osName),
6701     m_osType(osType),
6702     m_osDirection(osDirection),
6703     m_nSize(nSize)
6704 {
6705 }
6706 //! @endcond
6707 
6708 /************************************************************************/
6709 /*                         GetIndexingVariable()                        */
6710 /************************************************************************/
6711 
6712 /** Return the variable that is used to index the dimension (if there is one).
6713  *
6714  * This is the array, typically one-dimensional, describing the values taken
6715  * by the dimension.
6716  */
GetIndexingVariable() const6717 std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
6718 {
6719     return nullptr;
6720 }
6721 
6722 /************************************************************************/
6723 /*                         SetIndexingVariable()                        */
6724 /************************************************************************/
6725 
6726 /** Set the variable that is used to index the dimension.
6727  *
6728  * This is the array, typically one-dimensional, describing the values taken
6729  * by the dimension.
6730  *
6731  * Optionally implemented by drivers.
6732  *
6733  * Drivers known to implement it: MEM.
6734  *
6735  * @param poArray Variable to use to index the dimension.
6736  * @return true in case of success.
6737  */
SetIndexingVariable(CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)6738 bool GDALDimension::SetIndexingVariable(CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
6739 {
6740     CPLError(CE_Failure, CPLE_NotSupported, "SetIndexingVariable() not implemented");
6741     return false;
6742 }
6743 
6744 /************************************************************************/
6745 /************************************************************************/
6746 /************************************************************************/
6747 /*                              C API                                   */
6748 /************************************************************************/
6749 /************************************************************************/
6750 /************************************************************************/
6751 
6752 
6753 struct GDALExtendedDataTypeHS
6754 {
6755     std::unique_ptr<GDALExtendedDataType> m_poImpl;
6756 
GDALExtendedDataTypeHSGDALExtendedDataTypeHS6757     explicit GDALExtendedDataTypeHS(GDALExtendedDataType* dt): m_poImpl(dt) {}
6758 };
6759 
6760 struct GDALEDTComponentHS
6761 {
6762     std::unique_ptr<GDALEDTComponent> m_poImpl;
6763 
GDALEDTComponentHSGDALEDTComponentHS6764     explicit GDALEDTComponentHS(const GDALEDTComponent& component):
6765         m_poImpl(new GDALEDTComponent(component)) {}
6766 };
6767 
6768 struct GDALGroupHS
6769 {
6770     std::shared_ptr<GDALGroup> m_poImpl;
6771 
GDALGroupHSGDALGroupHS6772     explicit GDALGroupHS(const std::shared_ptr<GDALGroup>& poGroup): m_poImpl(poGroup) {}
6773 };
6774 
6775 struct GDALMDArrayHS
6776 {
6777     std::shared_ptr<GDALMDArray> m_poImpl;
6778 
GDALMDArrayHSGDALMDArrayHS6779     explicit GDALMDArrayHS(const std::shared_ptr<GDALMDArray>& poArray): m_poImpl(poArray) {}
6780 };
6781 
6782 struct GDALAttributeHS
6783 {
6784     std::shared_ptr<GDALAttribute> m_poImpl;
6785 
GDALAttributeHSGDALAttributeHS6786     explicit GDALAttributeHS(const std::shared_ptr<GDALAttribute>& poAttr): m_poImpl(poAttr) {}
6787 };
6788 
6789 struct GDALDimensionHS
6790 {
6791     std::shared_ptr<GDALDimension> m_poImpl;
6792 
GDALDimensionHSGDALDimensionHS6793     explicit GDALDimensionHS(const std::shared_ptr<GDALDimension>& poDim): m_poImpl(poDim) {}
6794 };
6795 
6796 /************************************************************************/
6797 /*                      GDALExtendedDataTypeCreate()                    */
6798 /************************************************************************/
6799 
6800 /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
6801  *
6802  * This is the same as the C++ method GDALExtendedDataType::Create()
6803  *
6804  * The returned handle should be freed with GDALExtendedDataTypeRelease().
6805  *
6806  * @param eType Numeric data type.
6807  *
6808  * @return a new GDALExtendedDataTypeH handle, or nullptr.
6809  */
GDALExtendedDataTypeCreate(GDALDataType eType)6810 GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
6811 {
6812     return new GDALExtendedDataTypeHS(
6813         new GDALExtendedDataType(
6814             GDALExtendedDataType::Create(eType)));
6815 }
6816 
6817 /************************************************************************/
6818 /*                    GDALExtendedDataTypeCreateString()                */
6819 /************************************************************************/
6820 
6821 /** Return a new GDALExtendedDataType of class GEDTC_STRING.
6822  *
6823  * This is the same as the C++ method GDALExtendedDataType::CreateString()
6824  *
6825  * The returned handle should be freed with GDALExtendedDataTypeRelease().
6826  *
6827  * @return a new GDALExtendedDataTypeH handle, or nullptr.
6828  */
GDALExtendedDataTypeCreateString(size_t nMaxStringLength)6829 GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
6830 {
6831     return new GDALExtendedDataTypeHS(
6832         new GDALExtendedDataType(
6833             GDALExtendedDataType::CreateString(nMaxStringLength)));
6834 }
6835 
6836 /************************************************************************/
6837 /*                   GDALExtendedDataTypeCreateCompound()               */
6838 /************************************************************************/
6839 
6840 /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
6841  *
6842  * This is the same as the C++ method GDALExtendedDataType::Create(const std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
6843  *
6844  * The returned handle should be freed with GDALExtendedDataTypeRelease().
6845  *
6846  * @param pszName Type name.
6847  * @param nTotalSize Total size of the type in bytes.
6848  *                   Should be large enough to store all components.
6849  * @param nComponents Number of components in comps array.
6850  * @param comps Components.
6851  * @return a new GDALExtendedDataTypeH handle, or nullptr.
6852  */
GDALExtendedDataTypeCreateCompound(const char * pszName,size_t nTotalSize,size_t nComponents,const GDALEDTComponentH * comps)6853 GDALExtendedDataTypeH GDALExtendedDataTypeCreateCompound(
6854     const char* pszName, size_t nTotalSize, size_t nComponents,
6855     const GDALEDTComponentH* comps)
6856 {
6857     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
6858     for( size_t i = 0; i < nComponents; i++ )
6859     {
6860         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
6861             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
6862     }
6863     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "",
6864                                            nTotalSize, std::move(compsCpp));
6865     if( dt.GetClass() != GEDTC_COMPOUND )
6866         return nullptr;
6867     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
6868 }
6869 
6870 /************************************************************************/
6871 /*                     GDALExtendedDataTypeRelease()                    */
6872 /************************************************************************/
6873 
6874 /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
6875  *
6876  * Note: when applied on a object coming from a driver, this does not
6877  * destroy the object in the file, database, etc...
6878  */
GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)6879 void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
6880 {
6881     delete hEDT;
6882 }
6883 
6884 /************************************************************************/
6885 /*                     GDALExtendedDataTypeGetName()                    */
6886 /************************************************************************/
6887 
6888 /** Return type name.
6889  *
6890  * This is the same as the C++ method GDALExtendedDataType::GetName()
6891  */
GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)6892 const char* GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
6893 {
6894     VALIDATE_POINTER1( hEDT, __func__, "" );
6895     return hEDT->m_poImpl->GetName().c_str();
6896 }
6897 
6898 /************************************************************************/
6899 /*                     GDALExtendedDataTypeGetClass()                    */
6900 /************************************************************************/
6901 
6902 /** Return type class.
6903  *
6904  * This is the same as the C++ method GDALExtendedDataType::GetClass()
6905  */
GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)6906 GDALExtendedDataTypeClass GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
6907 {
6908     VALIDATE_POINTER1( hEDT, __func__, GEDTC_NUMERIC );
6909     return hEDT->m_poImpl->GetClass();
6910 }
6911 
6912 /************************************************************************/
6913 /*               GDALExtendedDataTypeGetNumericDataType()               */
6914 /************************************************************************/
6915 
6916 /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
6917  *
6918  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
6919  */
GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)6920 GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
6921 {
6922     VALIDATE_POINTER1( hEDT, __func__, GDT_Unknown );
6923     return hEDT->m_poImpl->GetNumericDataType();
6924 }
6925 
6926 /************************************************************************/
6927 /*                   GDALExtendedDataTypeGetSize()                      */
6928 /************************************************************************/
6929 
6930 /** Return data type size in bytes.
6931  *
6932  * This is the same as the C++ method GDALExtendedDataType::GetSize()
6933  */
GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)6934 size_t  GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
6935 {
6936     VALIDATE_POINTER1( hEDT, __func__, 0 );
6937     return hEDT->m_poImpl->GetSize();
6938 }
6939 
6940 /************************************************************************/
6941 /*              GDALExtendedDataTypeGetMaxStringLength()                */
6942 /************************************************************************/
6943 
6944 /** Return the maximum length of a string in bytes.
6945  *
6946  * 0 indicates unknown/unlimited string.
6947  *
6948  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
6949  */
GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)6950 size_t  GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
6951 {
6952     VALIDATE_POINTER1( hEDT, __func__, 0 );
6953     return hEDT->m_poImpl->GetMaxStringLength();
6954 }
6955 
6956 /************************************************************************/
6957 /*                    GDALExtendedDataTypeCanConvertTo()                */
6958 /************************************************************************/
6959 
6960 /** Return whether this data type can be converted to the other one.
6961  *
6962  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
6963  *
6964  * @param hSourceEDT Source data type for the conversion being considered.
6965  * @param hTargetEDT Target data type for the conversion being considered.
6966  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
6967  */
GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,GDALExtendedDataTypeH hTargetEDT)6968 int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
6969                                      GDALExtendedDataTypeH hTargetEDT)
6970 {
6971     VALIDATE_POINTER1( hSourceEDT, __func__, FALSE );
6972     VALIDATE_POINTER1( hTargetEDT, __func__, FALSE );
6973     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
6974 }
6975 
6976 /************************************************************************/
6977 /*                        GDALExtendedDataTypeEquals()                  */
6978 /************************************************************************/
6979 
6980 /** Return whether this data type is equal to another one.
6981  *
6982  * This is the same as the C++ method GDALExtendedDataType::operator==()
6983  *
6984  * @param hFirstEDT First data type.
6985  * @param hSecondEDT Second data type.
6986  * @return TRUE if they are equal. FALSE otherwise.
6987  */
GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,GDALExtendedDataTypeH hSecondEDT)6988 int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
6989                                GDALExtendedDataTypeH hSecondEDT)
6990 {
6991     VALIDATE_POINTER1( hFirstEDT, __func__, FALSE );
6992     VALIDATE_POINTER1( hSecondEDT, __func__, FALSE );
6993     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
6994 }
6995 
6996 /************************************************************************/
6997 /*                     GDALExtendedDataTypeGetComponents()              */
6998 /************************************************************************/
6999 
7000 /** Return the components of the data type (only valid when GetClass() == GEDTC_COMPOUND)
7001  *
7002  * The returned array and its content must be freed with GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
7003  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
7004  * individual array members).
7005  *
7006  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
7007  *
7008  * @param hEDT Data type
7009  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
7010  * @return an array of *pnCount components.
7011  */
GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,size_t * pnCount)7012 GDALEDTComponentH *GDALExtendedDataTypeGetComponents(
7013                             GDALExtendedDataTypeH hEDT, size_t* pnCount)
7014 {
7015     VALIDATE_POINTER1( hEDT, __func__, nullptr );
7016     VALIDATE_POINTER1( pnCount, __func__, nullptr );
7017     const auto& components = hEDT->m_poImpl->GetComponents();
7018     auto ret = static_cast<GDALEDTComponentH*>(
7019         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
7020     for( size_t i = 0; i < components.size(); i++ )
7021     {
7022         ret[i] = new GDALEDTComponentHS(*components[i].get());
7023     }
7024     *pnCount = components.size();
7025     return ret;
7026 }
7027 
7028 /************************************************************************/
7029 /*                     GDALExtendedDataTypeFreeComponents()             */
7030 /************************************************************************/
7031 
7032 /** Free the return of GDALExtendedDataTypeGetComponents().
7033  *
7034  * @param components return value of GDALExtendedDataTypeGetComponents()
7035  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
7036  */
GDALExtendedDataTypeFreeComponents(GDALEDTComponentH * components,size_t nCount)7037 void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH* components, size_t nCount)
7038 {
7039     for( size_t i = 0; i < nCount; i++ )
7040     {
7041         delete components[i];
7042     }
7043     CPLFree(components);
7044 }
7045 
7046 /************************************************************************/
7047 /*                         GDALEDTComponentCreate()                     */
7048 /************************************************************************/
7049 
7050 /** Create a new GDALEDTComponent.
7051  *
7052  * The returned value must be freed with GDALEDTComponentRelease().
7053  *
7054  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
7055  */
GDALEDTComponentCreate(const char * pszName,size_t nOffset,GDALExtendedDataTypeH hType)7056 GDALEDTComponentH GDALEDTComponentCreate(const char* pszName, size_t nOffset, GDALExtendedDataTypeH hType)
7057 {
7058     VALIDATE_POINTER1( pszName, __func__, nullptr );
7059     VALIDATE_POINTER1( hType, __func__, nullptr );
7060     return new GDALEDTComponentHS(GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
7061 }
7062 
7063 /************************************************************************/
7064 /*                         GDALEDTComponentRelease()                    */
7065 /************************************************************************/
7066 
7067 /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
7068  *
7069  * Note: when applied on a object coming from a driver, this does not
7070  * destroy the object in the file, database, etc...
7071  */
GDALEDTComponentRelease(GDALEDTComponentH hComp)7072 void GDALEDTComponentRelease(GDALEDTComponentH hComp)
7073 {
7074     delete hComp;
7075 }
7076 
7077 /************************************************************************/
7078 /*                         GDALEDTComponentGetName()                    */
7079 /************************************************************************/
7080 
7081 /** Return the name.
7082  *
7083  * The returned pointer is valid until hComp is released.
7084  *
7085  * This is the same as the C++ method GDALEDTComponent::GetName().
7086  */
GDALEDTComponentGetName(GDALEDTComponentH hComp)7087 const char * GDALEDTComponentGetName(GDALEDTComponentH hComp)
7088 {
7089     VALIDATE_POINTER1( hComp, __func__, nullptr );
7090     return hComp->m_poImpl->GetName().c_str();
7091 }
7092 
7093 /************************************************************************/
7094 /*                       GDALEDTComponentGetOffset()                    */
7095 /************************************************************************/
7096 
7097 /** Return the offset (in bytes) of the component in the compound data type.
7098  *
7099  * This is the same as the C++ method GDALEDTComponent::GetOffset().
7100  */
GDALEDTComponentGetOffset(GDALEDTComponentH hComp)7101 size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
7102 {
7103     VALIDATE_POINTER1( hComp, __func__, 0 );
7104     return hComp->m_poImpl->GetOffset();
7105 }
7106 
7107 /************************************************************************/
7108 /*                       GDALEDTComponentGetType()                      */
7109 /************************************************************************/
7110 
7111 /** Return the data type of the component.
7112  *
7113  * This is the same as the C++ method GDALEDTComponent::GetType().
7114  */
GDALEDTComponentGetType(GDALEDTComponentH hComp)7115 GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
7116 {
7117     VALIDATE_POINTER1( hComp, __func__, nullptr );
7118     return new GDALExtendedDataTypeHS(
7119         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
7120 }
7121 
7122 /************************************************************************/
7123 /*                           GDALGroupRelease()                         */
7124 /************************************************************************/
7125 
7126 /** Release the GDAL in-memory object associated with a GDALGroupH.
7127  *
7128  * Note: when applied on a object coming from a driver, this does not
7129  * destroy the object in the file, database, etc...
7130  */
GDALGroupRelease(GDALGroupH hGroup)7131 void GDALGroupRelease(GDALGroupH hGroup)
7132 {
7133     delete hGroup;
7134 }
7135 
7136 /************************************************************************/
7137 /*                           GDALGroupGetName()                         */
7138 /************************************************************************/
7139 
7140 /** Return the name of the group.
7141  *
7142  * The returned pointer is valid until hGroup is released.
7143  *
7144  * This is the same as the C++ method GDALGroup::GetName().
7145  */
GDALGroupGetName(GDALGroupH hGroup)7146 const char *GDALGroupGetName(GDALGroupH hGroup)
7147 {
7148     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7149     return hGroup->m_poImpl->GetName().c_str();
7150 }
7151 
7152 /************************************************************************/
7153 /*                         GDALGroupGetFullName()                       */
7154 /************************************************************************/
7155 
7156 /** Return the full name of the group.
7157  *
7158  * The returned pointer is valid until hGroup is released.
7159  *
7160  * This is the same as the C++ method GDALGroup::GetFullName().
7161  */
GDALGroupGetFullName(GDALGroupH hGroup)7162 const char *GDALGroupGetFullName(GDALGroupH hGroup)
7163 {
7164     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7165     return hGroup->m_poImpl->GetFullName().c_str();
7166 }
7167 
7168 /************************************************************************/
7169 /*                          GDALGroupGetMDArrayNames()                  */
7170 /************************************************************************/
7171 
7172 /** Return the list of multidimensional array names contained in this group.
7173  *
7174  * This is the same as the C++ method GDALGroup::GetGroupNames().
7175  *
7176  * @return the array names, to be freed with CSLDestroy()
7177  */
GDALGroupGetMDArrayNames(GDALGroupH hGroup,CSLConstList papszOptions)7178 char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
7179 {
7180     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7181     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
7182     CPLStringList res;
7183     for( const auto& name: names )
7184     {
7185         res.AddString(name.c_str());
7186     }
7187     return res.StealList();
7188 }
7189 
7190 /************************************************************************/
7191 /*                          GDALGroupOpenMDArray()                      */
7192 /************************************************************************/
7193 
7194 /** Open and return a multidimensional array.
7195  *
7196  * This is the same as the C++ method GDALGroup::OpenMDArray().
7197  *
7198  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
7199  */
GDALGroupOpenMDArray(GDALGroupH hGroup,const char * pszMDArrayName,CSLConstList papszOptions)7200 GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char* pszMDArrayName,
7201                                   CSLConstList papszOptions)
7202 {
7203     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7204     VALIDATE_POINTER1( pszMDArrayName, __func__, nullptr );
7205     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName), papszOptions);
7206     if( !array )
7207         return nullptr;
7208     return new GDALMDArrayHS(array);
7209 }
7210 
7211 /************************************************************************/
7212 /*                  GDALGroupOpenMDArrayFromFullname()                  */
7213 /************************************************************************/
7214 
7215 /** Open and return a multidimensional array from its fully qualified name.
7216  *
7217  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
7218  *
7219  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
7220  *
7221  * @since GDAL 3.2
7222  */
GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,const char * pszFullname,CSLConstList papszOptions)7223 GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup, const char* pszFullname,
7224                                   CSLConstList papszOptions)
7225 {
7226     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7227     VALIDATE_POINTER1( pszFullname, __func__, nullptr );
7228     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(std::string(pszFullname), papszOptions);
7229     if( !array )
7230         return nullptr;
7231     return new GDALMDArrayHS(array);
7232 }
7233 
7234 
7235 
7236 /************************************************************************/
7237 /*                      GDALGroupResolveMDArray()                       */
7238 /************************************************************************/
7239 
7240 /** Locate an array in a group and its subgroups by name.
7241  *
7242  * See GDALGroup::ResolveMDArray() for description of the behavior.
7243  * @since GDAL 3.2
7244  */
GDALGroupResolveMDArray(GDALGroupH hGroup,const char * pszName,const char * pszStartingPoint,CSLConstList papszOptions)7245 GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup,
7246                                      const char* pszName,
7247                                      const char* pszStartingPoint,
7248                                      CSLConstList papszOptions)
7249 {
7250     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7251     VALIDATE_POINTER1( pszName, __func__, nullptr );
7252     VALIDATE_POINTER1( pszStartingPoint, __func__, nullptr );
7253     auto array = hGroup->m_poImpl->ResolveMDArray(std::string(pszName),
7254                                                   std::string(pszStartingPoint),
7255                                                   papszOptions);
7256     if( !array )
7257         return nullptr;
7258     return new GDALMDArrayHS(array);
7259 }
7260 
7261 /************************************************************************/
7262 /*                        GDALGroupGetGroupNames()                      */
7263 /************************************************************************/
7264 
7265 /** Return the list of sub-groups contained in this group.
7266  *
7267  * This is the same as the C++ method GDALGroup::GetGroupNames().
7268  *
7269  * @return the group names, to be freed with CSLDestroy()
7270  */
GDALGroupGetGroupNames(GDALGroupH hGroup,CSLConstList papszOptions)7271 char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
7272 {
7273     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7274     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
7275     CPLStringList res;
7276     for( const auto& name: names )
7277     {
7278         res.AddString(name.c_str());
7279     }
7280     return res.StealList();
7281 }
7282 
7283 /************************************************************************/
7284 /*                           GDALGroupOpenGroup()                       */
7285 /************************************************************************/
7286 
7287 /** Open and return a sub-group.
7288  *
7289  * This is the same as the C++ method GDALGroup::OpenGroup().
7290  *
7291  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
7292  */
GDALGroupOpenGroup(GDALGroupH hGroup,const char * pszSubGroupName,CSLConstList papszOptions)7293 GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char* pszSubGroupName,
7294                               CSLConstList papszOptions)
7295 {
7296     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7297     VALIDATE_POINTER1( pszSubGroupName, __func__, nullptr );
7298     auto subGroup = hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
7299     if( !subGroup )
7300         return nullptr;
7301     return new GDALGroupHS(subGroup);
7302 }
7303 
7304 /************************************************************************/
7305 /*                       GDALGroupOpenMDArrayFromFullname()             */
7306 /************************************************************************/
7307 
7308 /** Open and return a sub-group from its fully qualified name.
7309  *
7310  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
7311  *
7312  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
7313  *
7314  * @since GDAL 3.2
7315  */
GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,const char * pszFullname,CSLConstList papszOptions)7316 GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup, const char* pszFullname,
7317                                           CSLConstList papszOptions)
7318 {
7319     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7320     VALIDATE_POINTER1( pszFullname, __func__, nullptr );
7321     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(std::string(pszFullname), papszOptions);
7322     if( !subGroup )
7323         return nullptr;
7324     return new GDALGroupHS(subGroup);
7325 }
7326 
7327 /************************************************************************/
7328 /*                         GDALGroupGetDimensions()                     */
7329 /************************************************************************/
7330 
7331 /** Return the list of dimensions contained in this group and used by its
7332  * arrays.
7333  *
7334  * The returned array must be freed with GDALReleaseDimensions().  If only the array itself needs to be
7335  * freed, CPLFree() should be called (and GDALDimensionRelease() on
7336  * individual array members).
7337  *
7338  * This is the same as the C++ method GDALGroup::GetDimensions().
7339  *
7340  * @param hGroup Group.
7341  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
7342  * @param papszOptions Driver specific options determining how dimensions
7343  * should be retrieved. Pass nullptr for default behavior.
7344  *
7345  * @return an array of *pnCount dimensions.
7346  */
GDALGroupGetDimensions(GDALGroupH hGroup,size_t * pnCount,CSLConstList papszOptions)7347 GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t* pnCount,
7348                                        CSLConstList papszOptions)
7349 {
7350     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7351     VALIDATE_POINTER1( pnCount, __func__, nullptr );
7352     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
7353     auto ret = static_cast<GDALDimensionH*>(
7354         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
7355     for( size_t i = 0; i < dims.size(); i++ )
7356     {
7357         ret[i] = new GDALDimensionHS(dims[i]);
7358     }
7359     *pnCount = dims.size();
7360     return ret;
7361 }
7362 
7363 /************************************************************************/
7364 /*                          GDALGroupGetAttribute()                     */
7365 /************************************************************************/
7366 
7367 /** Return an attribute by its name.
7368  *
7369  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
7370  *
7371  * The returned attribute must be freed with GDALAttributeRelease().
7372  */
GDALGroupGetAttribute(GDALGroupH hGroup,const char * pszName)7373 GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char* pszName)
7374 {
7375     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7376     VALIDATE_POINTER1( pszName, __func__, nullptr );
7377     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
7378     if( attr )
7379         return new GDALAttributeHS(attr);
7380     return nullptr;
7381 }
7382 
7383 /************************************************************************/
7384 /*                         GDALGroupGetAttributes()                     */
7385 /************************************************************************/
7386 
7387 /** Return the list of attributes contained in this group.
7388  *
7389  * The returned array must be freed with GDALReleaseAttributes(). If only the array itself needs to be
7390  * freed, CPLFree() should be called (and GDALAttributeRelease() on
7391  * individual array members).
7392  *
7393  * This is the same as the C++ method GDALGroup::GetAttributes().
7394  *
7395  * @param hGroup Group.
7396  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
7397  * @param papszOptions Driver specific options determining how attributes
7398  * should be retrieved. Pass nullptr for default behavior.
7399  *
7400  * @return an array of *pnCount attributes.
7401  */
GDALGroupGetAttributes(GDALGroupH hGroup,size_t * pnCount,CSLConstList papszOptions)7402 GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t* pnCount,
7403                                        CSLConstList papszOptions)
7404 {
7405     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7406     VALIDATE_POINTER1( pnCount, __func__, nullptr );
7407     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
7408     auto ret = static_cast<GDALAttributeH*>(
7409         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
7410     for( size_t i = 0; i < attrs.size(); i++ )
7411     {
7412         ret[i] = new GDALAttributeHS(attrs[i]);
7413     }
7414     *pnCount = attrs.size();
7415     return ret;
7416 }
7417 
7418 /************************************************************************/
7419 /*                     GDALGroupGetStructuralInfo()                     */
7420 /************************************************************************/
7421 
7422 /** Return structural information on the group.
7423  *
7424  * This may be the compression, etc..
7425  *
7426  * The return value should not be freed and is valid until GDALGroup is
7427  * released or this function called again.
7428  *
7429  * This is the same as the C++ method GDALGroup::GetStruturalInfo().
7430  */
GDALGroupGetStructuralInfo(GDALGroupH hGroup)7431 CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
7432 {
7433     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7434     return hGroup->m_poImpl->GetStructuralInfo();
7435 }
7436 
7437 /************************************************************************/
7438 /*                         GDALReleaseAttributes()                      */
7439 /************************************************************************/
7440 
7441 /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
7442  *
7443  * @param attributes return pointer of above methods
7444  * @param nCount *pnCount value returned by above methods
7445  */
GDALReleaseAttributes(GDALAttributeH * attributes,size_t nCount)7446 void GDALReleaseAttributes(GDALAttributeH* attributes, size_t nCount)
7447 {
7448     for( size_t i = 0; i < nCount; i++ )
7449     {
7450         delete attributes[i];
7451     }
7452     CPLFree(attributes);
7453 }
7454 
7455 /************************************************************************/
7456 /*                         GDALGroupCreateGroup()                       */
7457 /************************************************************************/
7458 
7459 /** Create a sub-group within a group.
7460  *
7461  * This is the same as the C++ method GDALGroup::CreateGroup().
7462  *
7463  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
7464  */
GDALGroupCreateGroup(GDALGroupH hGroup,const char * pszSubGroupName,CSLConstList papszOptions)7465 GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup,
7466                                         const char* pszSubGroupName,
7467                                         CSLConstList papszOptions)
7468 {
7469     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7470     VALIDATE_POINTER1( pszSubGroupName, __func__, nullptr );
7471     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
7472                                              papszOptions);
7473     if( !ret )
7474         return nullptr;
7475     return new GDALGroupHS(ret);
7476 }
7477 
7478 /************************************************************************/
7479 /*                      GDALGroupCreateDimension()                      */
7480 /************************************************************************/
7481 
7482 /** Create a dimension within a group.
7483  *
7484  * This is the same as the C++ method GDALGroup::CreateDimension().
7485  *
7486  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
7487  */
GDALGroupCreateDimension(GDALGroupH hGroup,const char * pszName,const char * pszType,const char * pszDirection,GUInt64 nSize,CSLConstList papszOptions)7488 GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup,
7489                                                 const char* pszName,
7490                                                 const char* pszType,
7491                                                 const char* pszDirection,
7492                                                 GUInt64 nSize,
7493                                                 CSLConstList papszOptions)
7494 {
7495     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7496     VALIDATE_POINTER1( pszName, __func__, nullptr );
7497     auto ret = hGroup->m_poImpl->CreateDimension(std::string(pszName),
7498                                                  std::string(pszType ? pszType : ""),
7499                                                  std::string(pszDirection ? pszDirection: ""),
7500                                                  nSize,
7501                                                  papszOptions);
7502     if( !ret )
7503         return nullptr;
7504     return new GDALDimensionHS(ret);
7505 }
7506 
7507 /************************************************************************/
7508 /*                      GDALGroupCreateMDArray()                        */
7509 /************************************************************************/
7510 
7511 /** Create a multidimensional array within a group.
7512  *
7513  * This is the same as the C++ method GDALGroup::CreateMDArray().
7514  *
7515  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
7516  */
GDALGroupCreateMDArray(GDALGroupH hGroup,const char * pszName,size_t nDimensions,GDALDimensionH * pahDimensions,GDALExtendedDataTypeH hEDT,CSLConstList papszOptions)7517 GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup,
7518                                     const char* pszName,
7519                                     size_t nDimensions,
7520                                     GDALDimensionH* pahDimensions,
7521                                     GDALExtendedDataTypeH hEDT,
7522                                     CSLConstList papszOptions)
7523 {
7524     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7525     VALIDATE_POINTER1( pszName, __func__, nullptr );
7526     VALIDATE_POINTER1( hEDT, __func__, nullptr );
7527     std::vector<std::shared_ptr<GDALDimension>> dims;
7528     dims.reserve(nDimensions);
7529     for(size_t i = 0; i < nDimensions; i++)
7530         dims.push_back(pahDimensions[i]->m_poImpl);
7531     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName),
7532                                                dims,
7533                                                *(hEDT->m_poImpl),
7534                                                papszOptions);
7535     if( !ret )
7536         return nullptr;
7537     return new GDALMDArrayHS(ret);
7538 }
7539 
7540 /************************************************************************/
7541 /*                      GDALGroupCreateAttribute()                      */
7542 /************************************************************************/
7543 
7544 /** Create a attribute within a group.
7545  *
7546  * This is the same as the C++ method GDALGroup::CreateAttribute().
7547  *
7548  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
7549  */
GDALGroupCreateAttribute(GDALGroupH hGroup,const char * pszName,size_t nDimensions,const GUInt64 * panDimensions,GDALExtendedDataTypeH hEDT,CSLConstList papszOptions)7550 GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup,
7551                                                 const char* pszName,
7552                                                 size_t nDimensions,
7553                                                 const GUInt64* panDimensions,
7554                                                 GDALExtendedDataTypeH hEDT,
7555                                                 CSLConstList papszOptions)
7556 {
7557     VALIDATE_POINTER1( hGroup, __func__, nullptr );
7558     VALIDATE_POINTER1( hEDT, __func__, nullptr );
7559     std::vector<GUInt64> dims;
7560     dims.reserve(nDimensions);
7561     for(size_t i = 0; i < nDimensions; i++)
7562         dims.push_back(panDimensions[i]);
7563     auto ret = hGroup->m_poImpl->CreateAttribute(std::string(pszName),
7564                                                dims,
7565                                                *(hEDT->m_poImpl),
7566                                                papszOptions);
7567     if( !ret )
7568         return nullptr;
7569     return new GDALAttributeHS(ret);
7570 }
7571 
7572 /************************************************************************/
7573 /*                        GDALMDArrayRelease()                          */
7574 /************************************************************************/
7575 
7576 /** Release the GDAL in-memory object associated with a GDALMDArray.
7577  *
7578  * Note: when applied on a object coming from a driver, this does not
7579  * destroy the object in the file, database, etc...
7580  */
GDALMDArrayRelease(GDALMDArrayH hMDArray)7581 void GDALMDArrayRelease(GDALMDArrayH hMDArray)
7582 {
7583     delete hMDArray;
7584 }
7585 
7586 /************************************************************************/
7587 /*                        GDALMDArrayGetName()                          */
7588 /************************************************************************/
7589 
7590 /** Return array name.
7591  *
7592  * This is the same as the C++ method GDALMDArray::GetName()
7593  */
GDALMDArrayGetName(GDALMDArrayH hArray)7594 const char* GDALMDArrayGetName(GDALMDArrayH hArray)
7595 {
7596     VALIDATE_POINTER1( hArray, __func__, nullptr );
7597     return hArray->m_poImpl->GetName().c_str();
7598 }
7599 
7600 /************************************************************************/
7601 /*                    GDALMDArrayGetFullName()                          */
7602 /************************************************************************/
7603 
7604 /** Return array full name.
7605  *
7606  * This is the same as the C++ method GDALMDArray::GetFullName()
7607  */
GDALMDArrayGetFullName(GDALMDArrayH hArray)7608 const char* GDALMDArrayGetFullName(GDALMDArrayH hArray)
7609 {
7610     VALIDATE_POINTER1( hArray, __func__, nullptr );
7611     return hArray->m_poImpl->GetFullName().c_str();
7612 }
7613 
7614 /************************************************************************/
7615 /*                        GDALMDArrayGetName()                          */
7616 /************************************************************************/
7617 
7618 /** Return the total number of values in the array.
7619  *
7620  * This is the same as the C++ method GDALAbstractMDArray::GetTotalElementsCount()
7621  */
GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)7622 GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
7623 {
7624     VALIDATE_POINTER1( hArray, __func__, 0 );
7625     return hArray->m_poImpl->GetTotalElementsCount();
7626 }
7627 
7628 /************************************************************************/
7629 /*                        GDALMDArrayGetDimensionCount()                */
7630 /************************************************************************/
7631 
7632 /** Return the number of dimensions.
7633  *
7634  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
7635  */
GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)7636 size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
7637 {
7638     VALIDATE_POINTER1( hArray, __func__, 0 );
7639     return hArray->m_poImpl->GetDimensionCount();
7640 }
7641 
7642 /************************************************************************/
7643 /*                        GDALMDArrayGetDimensions()                    */
7644 /************************************************************************/
7645 
7646 /** Return the dimensions of the array
7647  *
7648  * The returned array must be freed with GDALReleaseDimensions(). If only the array itself needs to be
7649  * freed, CPLFree() should be called (and GDALDimensionRelease() on
7650  * individual array members).
7651  *
7652  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
7653  *
7654  * @param hArray Array.
7655  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
7656  *
7657  * @return an array of *pnCount dimensions.
7658  */
GDALMDArrayGetDimensions(GDALMDArrayH hArray,size_t * pnCount)7659 GDALDimensionH* GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
7660 {
7661     VALIDATE_POINTER1( hArray, __func__, nullptr );
7662     VALIDATE_POINTER1( pnCount, __func__, nullptr );
7663     const auto& dims(hArray->m_poImpl->GetDimensions());
7664     auto ret = static_cast<GDALDimensionH*>(
7665         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
7666     for( size_t i = 0; i < dims.size(); i++ )
7667     {
7668         ret[i] = new GDALDimensionHS(dims[i]);
7669     }
7670     *pnCount = dims.size();
7671     return ret;
7672 }
7673 
7674 /************************************************************************/
7675 /*                        GDALReleaseDimensions()                       */
7676 /************************************************************************/
7677 
7678 /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
7679  *
7680  * @param dims return pointer of above methods
7681  * @param nCount *pnCount value returned by above methods
7682  */
GDALReleaseDimensions(GDALDimensionH * dims,size_t nCount)7683 void GDALReleaseDimensions(GDALDimensionH* dims, size_t nCount)
7684 {
7685     for( size_t i = 0; i < nCount; i++ )
7686     {
7687         delete dims[i];
7688     }
7689     CPLFree(dims);
7690 }
7691 
7692 /************************************************************************/
7693 /*                        GDALMDArrayGetDataType()                     */
7694 /************************************************************************/
7695 
7696 /** Return the data type
7697  *
7698  * The return must be freed with GDALExtendedDataTypeRelease().
7699  */
GDALMDArrayGetDataType(GDALMDArrayH hArray)7700 GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
7701 {
7702     VALIDATE_POINTER1( hArray, __func__, nullptr );
7703     return new GDALExtendedDataTypeHS(
7704         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
7705 }
7706 
7707 /************************************************************************/
7708 /*                          GDALMDArrayRead()                           */
7709 /************************************************************************/
7710 
7711 /** Read part or totality of a multidimensional array.
7712  *
7713  * This is the same as the C++ method GDALAbstractMDArray::Read()
7714  *
7715  * @return TRUE in case of success.
7716  */
GDALMDArrayRead(GDALMDArrayH hArray,const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,GDALExtendedDataTypeH bufferDataType,void * pDstBuffer,const void * pDstBufferAllocStart,size_t nDstBufferAllocSize)7717 int GDALMDArrayRead(GDALMDArrayH hArray,
7718                             const GUInt64* arrayStartIdx,
7719                             const size_t* count,
7720                             const GInt64* arrayStep,
7721                             const GPtrDiff_t* bufferStride,
7722                             GDALExtendedDataTypeH bufferDataType,
7723                             void* pDstBuffer,
7724                             const void* pDstBufferAllocStart,
7725                             size_t nDstBufferAllocSize)
7726 {
7727     VALIDATE_POINTER1( hArray, __func__, FALSE );
7728     if( (arrayStartIdx == nullptr || count == nullptr) &&
7729          hArray->m_poImpl->GetDimensionCount() > 0 )
7730     {
7731         VALIDATE_POINTER1( arrayStartIdx, __func__, FALSE );
7732         VALIDATE_POINTER1( count, __func__, FALSE );
7733     }
7734     VALIDATE_POINTER1( bufferDataType, __func__, FALSE );
7735     VALIDATE_POINTER1( pDstBuffer, __func__, FALSE );
7736     // coverity[var_deref_model]
7737     return hArray->m_poImpl->Read(arrayStartIdx,
7738                                   count,
7739                                   arrayStep,
7740                                   bufferStride,
7741                                   *(bufferDataType->m_poImpl),
7742                                   pDstBuffer,
7743                                   pDstBufferAllocStart,
7744                                   nDstBufferAllocSize);
7745 }
7746 
7747 /************************************************************************/
7748 /*                          GDALMDArrayWrite()                           */
7749 /************************************************************************/
7750 
7751 /** Write part or totality of a multidimensional array.
7752  *
7753  * This is the same as the C++ method GDALAbstractMDArray::Write()
7754  *
7755  * @return TRUE in case of success.
7756  */
GDALMDArrayWrite(GDALMDArrayH hArray,const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,GDALExtendedDataTypeH bufferDataType,const void * pSrcBuffer,const void * pSrcBufferAllocStart,size_t nSrcBufferAllocSize)7757 int GDALMDArrayWrite(GDALMDArrayH hArray,
7758                             const GUInt64* arrayStartIdx,
7759                             const size_t* count,
7760                             const GInt64* arrayStep,
7761                             const GPtrDiff_t* bufferStride,
7762                             GDALExtendedDataTypeH bufferDataType,
7763                             const void* pSrcBuffer,
7764                             const void* pSrcBufferAllocStart,
7765                             size_t nSrcBufferAllocSize)
7766 {
7767     VALIDATE_POINTER1( hArray, __func__, FALSE );
7768     if( (arrayStartIdx == nullptr || count == nullptr) &&
7769          hArray->m_poImpl->GetDimensionCount() > 0 )
7770     {
7771         VALIDATE_POINTER1( arrayStartIdx, __func__, FALSE );
7772         VALIDATE_POINTER1( count, __func__, FALSE );
7773     }
7774     VALIDATE_POINTER1( bufferDataType, __func__, FALSE );
7775     VALIDATE_POINTER1( pSrcBuffer, __func__, FALSE );
7776     // coverity[var_deref_model]
7777     return hArray->m_poImpl->Write(arrayStartIdx,
7778                                   count,
7779                                   arrayStep,
7780                                   bufferStride,
7781                                   *(bufferDataType->m_poImpl),
7782                                   pSrcBuffer,
7783                                   pSrcBufferAllocStart,
7784                                   nSrcBufferAllocSize);
7785 }
7786 
7787 /************************************************************************/
7788 /*                       GDALMDArrayAdviseRead()                        */
7789 /************************************************************************/
7790 
7791 /** Advise driver of upcoming read requests.
7792  *
7793  * This is the same as the C++ method GDALMDArray::AdviseRead()
7794  *
7795  * @return TRUE in case of success.
7796  *
7797  * @since GDAL 3.2
7798  */
GDALMDArrayAdviseRead(GDALMDArrayH hArray,const GUInt64 * arrayStartIdx,const size_t * count)7799 int GDALMDArrayAdviseRead(GDALMDArrayH hArray,
7800                           const GUInt64* arrayStartIdx,
7801                           const size_t* count)
7802 {
7803     VALIDATE_POINTER1( hArray, __func__, FALSE );
7804     // coverity[var_deref_model]
7805     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count);
7806 }
7807 
7808 /************************************************************************/
7809 /*                         GDALMDArrayGetAttribute()                    */
7810 /************************************************************************/
7811 
7812 /** Return an attribute by its name.
7813  *
7814  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
7815  *
7816  * The returned attribute must be freed with GDALAttributeRelease().
7817  */
GDALMDArrayGetAttribute(GDALMDArrayH hArray,const char * pszName)7818 GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char* pszName)
7819 {
7820     VALIDATE_POINTER1( hArray, __func__, nullptr );
7821     VALIDATE_POINTER1( pszName, __func__, nullptr );
7822     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
7823     if( attr )
7824         return new GDALAttributeHS(attr);
7825     return nullptr;
7826 }
7827 
7828 /************************************************************************/
7829 /*                        GDALMDArrayGetAttributes()                    */
7830 /************************************************************************/
7831 
7832 /** Return the list of attributes contained in this array.
7833  *
7834  * The returned array must be freed with GDALReleaseAttributes(). If only the array itself needs to be
7835  * freed, CPLFree() should be called (and GDALAttributeRelease() on
7836  * individual array members).
7837  *
7838  * This is the same as the C++ method GDALMDArray::GetAttributes().
7839  *
7840  * @param hArray Array.
7841  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
7842  * @param papszOptions Driver specific options determining how attributes
7843  * should be retrieved. Pass nullptr for default behavior.
7844  *
7845  * @return an array of *pnCount attributes.
7846  */
GDALMDArrayGetAttributes(GDALMDArrayH hArray,size_t * pnCount,CSLConstList papszOptions)7847 GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t* pnCount,
7848                                          CSLConstList papszOptions)
7849 {
7850     VALIDATE_POINTER1( hArray, __func__, nullptr );
7851     VALIDATE_POINTER1( pnCount, __func__, nullptr );
7852     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
7853     auto ret = static_cast<GDALAttributeH*>(
7854         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
7855     for( size_t i = 0; i < attrs.size(); i++ )
7856     {
7857         ret[i] = new GDALAttributeHS(attrs[i]);
7858     }
7859     *pnCount = attrs.size();
7860     return ret;
7861 }
7862 
7863 /************************************************************************/
7864 /*                       GDALMDArrayCreateAttribute()                   */
7865 /************************************************************************/
7866 
7867 /** Create a attribute within an array.
7868  *
7869  * This is the same as the C++ method GDALMDArray::CreateAttribute().
7870  *
7871  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
7872  */
GDALMDArrayCreateAttribute(GDALMDArrayH hArray,const char * pszName,size_t nDimensions,const GUInt64 * panDimensions,GDALExtendedDataTypeH hEDT,CSLConstList papszOptions)7873 GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
7874                                                 const char* pszName,
7875                                                 size_t nDimensions,
7876                                                 const GUInt64* panDimensions,
7877                                                 GDALExtendedDataTypeH hEDT,
7878                                                 CSLConstList papszOptions)
7879 {
7880     VALIDATE_POINTER1( hArray, __func__, nullptr );
7881     VALIDATE_POINTER1( pszName, __func__, nullptr );
7882     VALIDATE_POINTER1( hEDT, __func__, nullptr );
7883     std::vector<GUInt64> dims;
7884     dims.reserve(nDimensions);
7885     for(size_t i = 0; i < nDimensions; i++)
7886         dims.push_back(panDimensions[i]);
7887     auto ret = hArray->m_poImpl->CreateAttribute(std::string(pszName),
7888                                                dims,
7889                                                *(hEDT->m_poImpl),
7890                                                papszOptions);
7891     if( !ret )
7892         return nullptr;
7893     return new GDALAttributeHS(ret);
7894 }
7895 
7896 /************************************************************************/
7897 /*                       GDALMDArrayGetRawNoDataValue()                 */
7898 /************************************************************************/
7899 
7900 /** Return the nodata value as a "raw" value.
7901  *
7902  * The value returned might be nullptr in case of no nodata value. When
7903  * a nodata value is registered, a non-nullptr will be returned whose size in
7904  * bytes is GetDataType().GetSize().
7905  *
7906  * The returned value should not be modified or freed.
7907  *
7908  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
7909  *
7910  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
7911  */
GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)7912 const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
7913 {
7914     VALIDATE_POINTER1( hArray, __func__, nullptr );
7915     return hArray->m_poImpl->GetRawNoDataValue();
7916 }
7917 
7918 /************************************************************************/
7919 /*                      GDALMDArrayGetNoDataValueAsDouble()             */
7920 /************************************************************************/
7921 
7922 /** Return the nodata value as a double.
7923  *
7924  * The value returned might be nullptr in case of no nodata value. When
7925  * a nodata value is registered, a non-nullptr will be returned whose size in
7926  * bytes is GetDataType().GetSize().
7927  *
7928  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
7929  *
7930  * @param hArray Array handle.
7931  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true if
7932  * a nodata value exists and can be converted to double. Might be nullptr.
7933  *
7934  * @return the nodata value as a double. A 0.0 value might also indicate the
7935  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue will be
7936  * set to false then).
7937  */
GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,int * pbHasNoDataValue)7938 double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
7939                                          int* pbHasNoDataValue)
7940 {
7941     VALIDATE_POINTER1( hArray, __func__, 0 );
7942     bool bHasNodataValue = false;
7943     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
7944     if( pbHasNoDataValue )
7945         *pbHasNoDataValue = bHasNodataValue;
7946     return ret;
7947 }
7948 
7949 /************************************************************************/
7950 /*                     GDALMDArraySetRawNoDataValue()                   */
7951 /************************************************************************/
7952 
7953 /** Set the nodata value as a "raw" value.
7954  *
7955  * The value passed might be nullptr in case of no nodata value. When
7956  * a nodata value is registered, a non-nullptr whose size in
7957  * bytes is GetDataType().GetSize() must be passed.
7958  *
7959  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const void*).
7960  *
7961  * @return TRUE in case of success.
7962  */
GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray,const void * pNoData)7963 int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void* pNoData)
7964 {
7965     VALIDATE_POINTER1( hArray, __func__, FALSE );
7966     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
7967 }
7968 
7969 /************************************************************************/
7970 /*                   GDALMDArraySetNoDataValueAsDouble()                */
7971 /************************************************************************/
7972 
7973 /** Set the nodata value as a double.
7974  *
7975  * If the natural data type of the attribute/array is not double, type conversion
7976  * will occur to the type returned by GetDataType().
7977  *
7978  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
7979  *
7980  * @return TRUE in case of success.
7981  */
GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray,double dfNoDataValue)7982 int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray,
7983                                       double dfNoDataValue)
7984 {
7985     VALIDATE_POINTER1( hArray, __func__, FALSE );
7986     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
7987 }
7988 
7989 /************************************************************************/
7990 /*                          GDALMDArraySetScale()                       */
7991 /************************************************************************/
7992 
7993 /** Set the scale value to apply to raw values.
7994  *
7995  * unscaled_value = raw_value * GetScale() + GetOffset()
7996  *
7997  * This is the same as the C++ method GDALMDArray::SetScale().
7998  *
7999  * @return TRUE in case of success.
8000  */
GDALMDArraySetScale(GDALMDArrayH hArray,double dfScale)8001 int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
8002 {
8003     VALIDATE_POINTER1( hArray, __func__, FALSE );
8004     return hArray->m_poImpl->SetScale(dfScale);
8005 }
8006 
8007 /************************************************************************/
8008 /*                        GDALMDArraySetScaleEx()                       */
8009 /************************************************************************/
8010 
8011 /** Set the scale value to apply to raw values.
8012  *
8013  * unscaled_value = raw_value * GetScale() + GetOffset()
8014  *
8015  * This is the same as the C++ method GDALMDArray::SetScale().
8016  *
8017  * @return TRUE in case of success.
8018  * @since GDAL 3.3
8019  */
GDALMDArraySetScaleEx(GDALMDArrayH hArray,double dfScale,GDALDataType eStorageType)8020 int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
8021                           GDALDataType eStorageType)
8022 {
8023     VALIDATE_POINTER1( hArray, __func__, FALSE );
8024     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
8025 }
8026 
8027 /************************************************************************/
8028 /*                          GDALMDArraySetOffset()                       */
8029 /************************************************************************/
8030 
8031 /** Set the scale value to apply to raw values.
8032  *
8033  * unscaled_value = raw_value * GetScale() + GetOffset()
8034  *
8035  * This is the same as the C++ method GDALMDArray::SetOffset().
8036  *
8037  * @return TRUE in case of success.
8038  */
GDALMDArraySetOffset(GDALMDArrayH hArray,double dfOffset)8039 int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
8040 {
8041     VALIDATE_POINTER1( hArray, __func__, FALSE );
8042     return hArray->m_poImpl->SetOffset(dfOffset);
8043 }
8044 
8045 /************************************************************************/
8046 /*                       GDALMDArraySetOffsetEx()                       */
8047 /************************************************************************/
8048 
8049 /** Set the scale value to apply to raw values.
8050  *
8051  * unscaled_value = raw_value * GetOffset() + GetOffset()
8052  *
8053  * This is the same as the C++ method GDALMDArray::SetOffset().
8054  *
8055  * @return TRUE in case of success.
8056  * @since GDAL 3.3
8057  */
GDALMDArraySetOffsetEx(GDALMDArrayH hArray,double dfOffset,GDALDataType eStorageType)8058 int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
8059                            GDALDataType eStorageType)
8060 {
8061     VALIDATE_POINTER1( hArray, __func__, FALSE );
8062     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
8063 }
8064 
8065 /************************************************************************/
8066 /*                          GDALMDArrayGetScale()                       */
8067 /************************************************************************/
8068 
8069 /** Get the scale value to apply to raw values.
8070  *
8071  * unscaled_value = raw_value * GetScale() + GetOffset()
8072  *
8073  * This is the same as the C++ method GDALMDArray::GetScale().
8074  *
8075  * @return the scale value
8076  */
GDALMDArrayGetScale(GDALMDArrayH hArray,int * pbHasValue)8077 double GDALMDArrayGetScale(GDALMDArrayH hArray, int* pbHasValue)
8078 {
8079     VALIDATE_POINTER1( hArray, __func__, 0.0 );
8080     bool bHasValue = false;
8081     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
8082     if( pbHasValue )
8083         *pbHasValue = bHasValue;
8084     return dfRet;
8085 }
8086 
8087 /************************************************************************/
8088 /*                        GDALMDArrayGetScaleEx()                       */
8089 /************************************************************************/
8090 
8091 /** Get the scale value to apply to raw values.
8092  *
8093  * unscaled_value = raw_value * GetScale() + GetScale()
8094  *
8095  * This is the same as the C++ method GDALMDArray::GetScale().
8096  *
8097  * @return the scale value
8098  * @since GDAL 3.3
8099  */
GDALMDArrayGetScaleEx(GDALMDArrayH hArray,int * pbHasValue,GDALDataType * peStorageType)8100 double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int* pbHasValue,
8101                              GDALDataType* peStorageType)
8102 {
8103     VALIDATE_POINTER1( hArray, __func__, 0.0 );
8104     bool bHasValue = false;
8105     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
8106     if( pbHasValue )
8107         *pbHasValue = bHasValue;
8108     return dfRet;
8109 }
8110 
8111 /************************************************************************/
8112 /*                          GDALMDArrayGetOffset()                      */
8113 /************************************************************************/
8114 
8115 /** Get the scale value to apply to raw values.
8116  *
8117  * unscaled_value = raw_value * GetScale() + GetOffset()
8118  *
8119  * This is the same as the C++ method GDALMDArray::GetOffset().
8120  *
8121  * @return the scale value
8122  */
GDALMDArrayGetOffset(GDALMDArrayH hArray,int * pbHasValue)8123 double GDALMDArrayGetOffset(GDALMDArrayH hArray, int* pbHasValue)
8124 {
8125     VALIDATE_POINTER1( hArray, __func__, 0.0 );
8126     bool bHasValue = false;
8127     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
8128     if( pbHasValue )
8129         *pbHasValue = bHasValue;
8130     return dfRet;
8131 }
8132 
8133 /************************************************************************/
8134 /*                        GDALMDArrayGetOffsetEx()                      */
8135 /************************************************************************/
8136 
8137 /** Get the scale value to apply to raw values.
8138  *
8139  * unscaled_value = raw_value * GetScale() + GetOffset()
8140  *
8141  * This is the same as the C++ method GDALMDArray::GetOffset().
8142  *
8143  * @return the scale value
8144  * @since GDAL 3.3
8145  */
GDALMDArrayGetOffsetEx(GDALMDArrayH hArray,int * pbHasValue,GDALDataType * peStorageType)8146 double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int* pbHasValue,
8147                               GDALDataType* peStorageType)
8148 {
8149     VALIDATE_POINTER1( hArray, __func__, 0.0 );
8150     bool bHasValue = false;
8151     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
8152     if( pbHasValue )
8153         *pbHasValue = bHasValue;
8154     return dfRet;
8155 }
8156 
8157 /************************************************************************/
8158 /*                      GDALMDArrayGetBlockSize()                       */
8159 /************************************************************************/
8160 
8161 /** Return the "natural" block size of the array along all dimensions.
8162  *
8163  * Some drivers might organize the array in tiles/blocks and reading/writing
8164  * aligned on those tile/block boundaries will be more efficient.
8165  *
8166  * The returned number of elements in the vector is the same as
8167  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
8168  * the natural block size along the considered dimension.
8169  * "Flat" arrays will typically return a vector of values set to 0.
8170  *
8171  * The default implementation will return a vector of values set to 0.
8172  *
8173  * This method is used by GetProcessingChunkSize().
8174  *
8175  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley theoretical
8176  * case of a 32-bit platform, this might exceed its size_t allocation capabilities.
8177  *
8178  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
8179  *
8180  * @return the block size, in number of elements along each dimension.
8181  */
GDALMDArrayGetBlockSize(GDALMDArrayH hArray,size_t * pnCount)8182 GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
8183 {
8184     VALIDATE_POINTER1( hArray, __func__, nullptr );
8185     VALIDATE_POINTER1( pnCount, __func__, nullptr );
8186     auto res = hArray->m_poImpl->GetBlockSize();
8187     auto ret = static_cast<GUInt64*>(
8188         CPLMalloc(sizeof(GUInt64) * res.size()));
8189     for( size_t i = 0; i < res.size(); i++ )
8190     {
8191         ret[i] = res[i];
8192     }
8193     *pnCount = res.size();
8194     return ret;
8195 }
8196 
8197 /***********************************************************************/
8198 /*                   GDALMDArrayGetProcessingChunkSize()               */
8199 /************************************************************************/
8200 
8201 /** \brief Return an optimal chunk size for read/write oerations, given the natural
8202  * block size and memory constraints specified.
8203  *
8204  * This method will use GetBlockSize() to define a chunk whose dimensions are
8205  * multiple of those returned by GetBlockSize() (unless the block define by
8206  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
8207  * returned by this method).
8208  *
8209  * This is the same as the C++ method GDALAbstractMDArray::GetProcessingChunkSize().
8210  *
8211  * @param hArray Array.
8212  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
8213  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the chunk.
8214  *
8215  * @return the chunk size, in number of elements along each dimension.
8216  */
8217 
GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray,size_t * pnCount,size_t nMaxChunkMemory)8218 size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
8219                                 size_t nMaxChunkMemory)
8220 {
8221     VALIDATE_POINTER1( hArray, __func__, nullptr );
8222     VALIDATE_POINTER1( pnCount, __func__, nullptr );
8223     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
8224     auto ret = static_cast<size_t*>(
8225         CPLMalloc(sizeof(size_t) * res.size()));
8226     for( size_t i = 0; i < res.size(); i++ )
8227     {
8228         ret[i] = res[i];
8229     }
8230     *pnCount = res.size();
8231     return ret;
8232 }
8233 
8234 /************************************************************************/
8235 /*                     GDALMDArrayGetStructuralInfo()                   */
8236 /************************************************************************/
8237 
8238 /** Return structural information on the array.
8239  *
8240  * This may be the compression, etc..
8241  *
8242  * The return value should not be freed and is valid until GDALMDArray is
8243  * released or this function called again.
8244  *
8245  * This is the same as the C++ method GDALMDArray::GetStruturalInfo().
8246  */
GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)8247 CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
8248 {
8249     VALIDATE_POINTER1( hArray, __func__, nullptr );
8250     return hArray->m_poImpl->GetStructuralInfo();
8251 }
8252 
8253 /************************************************************************/
8254 /*                        GDALMDArrayGetView()                          */
8255 /************************************************************************/
8256 
8257 /** Return a view of the array using slicing or field access.
8258  *
8259  * The returned object should be released with GDALMDArrayRelease().
8260  *
8261  * This is the same as the C++ method GDALMDArray::GetView().
8262  */
GDALMDArrayGetView(GDALMDArrayH hArray,const char * pszViewExpr)8263 GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char* pszViewExpr)
8264 {
8265     VALIDATE_POINTER1( hArray, __func__, nullptr );
8266     VALIDATE_POINTER1( pszViewExpr, __func__, nullptr );
8267     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
8268     if( !sliced )
8269         return nullptr;
8270     return new GDALMDArrayHS(sliced);
8271 }
8272 
8273 /************************************************************************/
8274 /*                       GDALMDArrayTranspose()                         */
8275 /************************************************************************/
8276 
8277 /** Return a view of the array whose axis have been reordered.
8278  *
8279  * The returned object should be released with GDALMDArrayRelease().
8280  *
8281  * This is the same as the C++ method GDALMDArray::Transpose().
8282  */
GDALMDArrayTranspose(GDALMDArrayH hArray,size_t nNewAxisCount,const int * panMapNewAxisToOldAxis)8283 GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray,
8284                                             size_t nNewAxisCount,
8285                                             const int *panMapNewAxisToOldAxis)
8286 {
8287     VALIDATE_POINTER1( hArray, __func__, nullptr );
8288     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
8289     if( nNewAxisCount )
8290     {
8291         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
8292                nNewAxisCount * sizeof(int));
8293     }
8294     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
8295     if( !reordered )
8296         return nullptr;
8297     return new GDALMDArrayHS(reordered);
8298 }
8299 
8300 /************************************************************************/
8301 /*                      GDALMDArrayGetUnscaled()                        */
8302 /************************************************************************/
8303 
8304 /** Return an array that is the unscaled version of the current one.
8305  *
8306  * That is each value of the unscaled array will be
8307  * unscaled_value = raw_value * GetScale() + GetOffset()
8308  *
8309  * Starting with GDAL 3.3, the Write() method is implemented and will convert
8310  * from unscaled values to raw values.
8311  *
8312  * The returned object should be released with GDALMDArrayRelease().
8313  *
8314  * This is the same as the C++ method GDALMDArray::GetUnscaled().
8315  */
GDALMDArrayGetUnscaled(GDALMDArrayH hArray)8316 GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
8317 {
8318     VALIDATE_POINTER1( hArray, __func__, nullptr );
8319     auto unscaled = hArray->m_poImpl->GetUnscaled();
8320     if( !unscaled )
8321         return nullptr;
8322     return new GDALMDArrayHS(unscaled);
8323 }
8324 
8325 /************************************************************************/
8326 /*                          GDALMDArrayGetMask()                         */
8327 /************************************************************************/
8328 
8329 /** Return an array that is a mask for the current array
8330  *
8331  * This array will be of type Byte, with values set to 0 to indicate invalid
8332  * pixels of the current array, and values set to 1 to indicate valid pixels.
8333  *
8334  * The returned object should be released with GDALMDArrayRelease().
8335  *
8336  * This is the same as the C++ method GDALMDArray::GetMask().
8337  */
GDALMDArrayGetMask(GDALMDArrayH hArray,CSLConstList papszOptions)8338 GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
8339 {
8340     VALIDATE_POINTER1( hArray, __func__, nullptr );
8341     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
8342     if( !unscaled )
8343         return nullptr;
8344     return new GDALMDArrayHS(unscaled);
8345 }
8346 
8347 /************************************************************************/
8348 /*                      GDALMDArraySetUnit()                            */
8349 /************************************************************************/
8350 
8351 /** Set the variable unit.
8352  *
8353  * Values should conform as much as possible with those allowed by
8354  * the NetCDF CF conventions:
8355  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
8356  * but others might be returned.
8357  *
8358  * Few examples are "meter", "degrees", "second", ...
8359  * Empty value means unknown.
8360  *
8361  * This is the same as the C function GDALMDArraySetUnit()
8362  *
8363  * @param hArray array.
8364  * @param pszUnit unit name.
8365  * @return TRUE in case of success.
8366  */
GDALMDArraySetUnit(GDALMDArrayH hArray,const char * pszUnit)8367 int GDALMDArraySetUnit(GDALMDArrayH hArray, const char* pszUnit)
8368 {
8369     VALIDATE_POINTER1( hArray, __func__, FALSE );
8370     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
8371 }
8372 
8373 /************************************************************************/
8374 /*                      GDALMDArrayGetUnit()                            */
8375 /************************************************************************/
8376 
8377 /** Return the array unit.
8378  *
8379  * Values should conform as much as possible with those allowed by
8380  * the NetCDF CF conventions:
8381  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
8382  * but others might be returned.
8383  *
8384  * Few examples are "meter", "degrees", "second", ...
8385  * Empty value means unknown.
8386  *
8387  * The return value should not be freed and is valid until GDALMDArray is
8388  * released or this function called again.
8389  *
8390  * This is the same as the C++ method GDALMDArray::GetUnit().
8391  */
GDALMDArrayGetUnit(GDALMDArrayH hArray)8392 const char* GDALMDArrayGetUnit(GDALMDArrayH hArray)
8393 {
8394     VALIDATE_POINTER1( hArray, __func__, nullptr );
8395     return hArray->m_poImpl->GetUnit().c_str();
8396 }
8397 
8398 
8399 /************************************************************************/
8400 /*                      GDALMDArrayGetSpatialRef()                      */
8401 /************************************************************************/
8402 
8403 /** Assign a spatial reference system object to the the array.
8404  *
8405  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
8406  * @return TRUE in case of success.
8407  */
GDALMDArraySetSpatialRef(GDALMDArrayH hArray,OGRSpatialReferenceH hSRS)8408 int GDALMDArraySetSpatialRef(GDALMDArrayH hArray,
8409                              OGRSpatialReferenceH hSRS)
8410 {
8411     VALIDATE_POINTER1( hArray, __func__, FALSE );
8412     return hArray->m_poImpl->SetSpatialRef(OGRSpatialReference::FromHandle(hSRS));
8413 }
8414 
8415 /************************************************************************/
8416 /*                      GDALMDArrayGetSpatialRef()                      */
8417 /************************************************************************/
8418 
8419 /** Return the spatial reference system object associated with the array.
8420  *
8421  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
8422  *
8423  * The returned object must be freed with OSRDestroySpatialReference().
8424  */
GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)8425 OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
8426 {
8427     VALIDATE_POINTER1( hArray, __func__, nullptr );
8428     auto poSRS = hArray->m_poImpl->GetSpatialRef();
8429     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
8430 }
8431 
8432 /************************************************************************/
8433 /*                      GDALMDArrayGetStatistics()                      */
8434 /************************************************************************/
8435 
8436 /**
8437  * \brief Fetch statistics.
8438  *
8439  * This is the same as the C++ method GDALMDArray::GetStatistics().
8440  *
8441  * @since GDAL 3.2
8442  */
8443 
GDALMDArrayGetStatistics(GDALMDArrayH hArray,GDALDatasetH hDS,int bApproxOK,int bForce,double * pdfMin,double * pdfMax,double * pdfMean,double * pdfStdDev,GUInt64 * pnValidCount,GDALProgressFunc pfnProgress,void * pProgressData)8444 CPLErr GDALMDArrayGetStatistics(
8445                         GDALMDArrayH hArray,
8446                         GDALDatasetH hDS, int bApproxOK, int bForce,
8447                         double *pdfMin, double *pdfMax,
8448                         double *pdfMean, double *pdfStdDev,
8449                         GUInt64* pnValidCount,
8450                         GDALProgressFunc pfnProgress, void *pProgressData )
8451 {
8452     VALIDATE_POINTER1( hArray, __func__, CE_Failure );
8453     return hArray->m_poImpl->GetStatistics(
8454         GDALDataset::FromHandle(hDS),
8455         CPL_TO_BOOL(bApproxOK),
8456         CPL_TO_BOOL(bForce),
8457         pdfMin, pdfMax, pdfMean, pdfStdDev, pnValidCount,
8458         pfnProgress, pProgressData);
8459 }
8460 
8461 /************************************************************************/
8462 /*                      GDALMDArrayComputeStatistics()                  */
8463 /************************************************************************/
8464 
8465 /**
8466  * \brief Compute statistics.
8467  *
8468  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
8469  *
8470  * @since GDAL 3.2
8471  */
8472 
GDALMDArrayComputeStatistics(GDALMDArrayH hArray,GDALDatasetH hDS,int bApproxOK,double * pdfMin,double * pdfMax,double * pdfMean,double * pdfStdDev,GUInt64 * pnValidCount,GDALProgressFunc pfnProgress,void * pProgressData)8473 int GDALMDArrayComputeStatistics( GDALMDArrayH hArray,
8474                                   GDALDatasetH hDS,
8475                                     int bApproxOK,
8476                                     double *pdfMin, double *pdfMax,
8477                                     double *pdfMean, double *pdfStdDev,
8478                                     GUInt64* pnValidCount,
8479                                     GDALProgressFunc pfnProgress,
8480                                   void *pProgressData )
8481 {
8482     VALIDATE_POINTER1( hArray, __func__, FALSE );
8483     return hArray->m_poImpl->ComputeStatistics(
8484         GDALDataset::FromHandle(hDS),
8485         CPL_TO_BOOL(bApproxOK),
8486         pdfMin, pdfMax, pdfMean, pdfStdDev, pnValidCount,
8487         pfnProgress, pProgressData);
8488 }
8489 
8490 /************************************************************************/
8491 /*                        GDALAttributeRelease()                        */
8492 /************************************************************************/
8493 
8494 /** Release the GDAL in-memory object associated with a GDALAttribute.
8495  *
8496  * Note: when applied on a object coming from a driver, this does not
8497  * destroy the object in the file, database, etc...
8498  */
GDALAttributeRelease(GDALAttributeH hAttr)8499 void GDALAttributeRelease(GDALAttributeH hAttr)
8500 {
8501     delete hAttr;
8502 }
8503 
8504 /************************************************************************/
8505 /*                        GDALAttributeGetName()                        */
8506 /************************************************************************/
8507 
8508 /** Return the name of the attribute.
8509  *
8510  * The returned pointer is valid until hAttr is released.
8511  *
8512  * This is the same as the C++ method GDALAttribute::GetName().
8513  */
GDALAttributeGetName(GDALAttributeH hAttr)8514 const char* GDALAttributeGetName(GDALAttributeH hAttr)
8515 {
8516     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8517     return hAttr->m_poImpl->GetName().c_str();
8518 }
8519 
8520 /************************************************************************/
8521 /*                      GDALAttributeGetFullName()                      */
8522 /************************************************************************/
8523 
8524 /** Return the full name of the attribute.
8525  *
8526  * The returned pointer is valid until hAttr is released.
8527  *
8528  * This is the same as the C++ method GDALAttribute::GetFullName().
8529  */
GDALAttributeGetFullName(GDALAttributeH hAttr)8530 const char* GDALAttributeGetFullName(GDALAttributeH hAttr)
8531 {
8532     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8533     return hAttr->m_poImpl->GetFullName().c_str();
8534 }
8535 
8536 /************************************************************************/
8537 /*                   GDALAttributeGetTotalElementsCount()               */
8538 /************************************************************************/
8539 
8540 /** Return the total number of values in the attribute.
8541  *
8542  * This is the same as the C++ method GDALAbstractMDArray::GetTotalElementsCount()
8543  */
GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)8544 GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
8545 {
8546     VALIDATE_POINTER1( hAttr, __func__, 0 );
8547     return hAttr->m_poImpl->GetTotalElementsCount();
8548 }
8549 
8550 /************************************************************************/
8551 /*                    GDALAttributeGetDimensionCount()                */
8552 /************************************************************************/
8553 
8554 /** Return the number of dimensions.
8555  *
8556  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
8557  */
GDALAttributeGetDimensionCount(GDALAttributeH hAttr)8558 size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
8559 {
8560     VALIDATE_POINTER1( hAttr, __func__, 0 );
8561     return hAttr->m_poImpl->GetDimensionCount();
8562 }
8563 
8564 /************************************************************************/
8565 /*                       GDALAttributeGetDimensionsSize()                */
8566 /************************************************************************/
8567 
8568 /** Return the dimension sizes of the attribute.
8569  *
8570  * The returned array must be freed with CPLFree()
8571  *
8572  * @param hAttr Attribute.
8573  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
8574  *
8575  * @return an array of *pnCount values.
8576  */
GDALAttributeGetDimensionsSize(GDALAttributeH hAttr,size_t * pnCount)8577 GUInt64* GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
8578 {
8579     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8580     VALIDATE_POINTER1( pnCount, __func__, nullptr );
8581     const auto& dims = hAttr->m_poImpl->GetDimensions();
8582     auto ret = static_cast<GUInt64*>(
8583         CPLMalloc(sizeof(GUInt64) * dims.size()));
8584     for( size_t i = 0; i < dims.size(); i++ )
8585     {
8586         ret[i] = dims[i]->GetSize();
8587     }
8588     *pnCount = dims.size();
8589     return ret;
8590 }
8591 
8592 /************************************************************************/
8593 /*                       GDALAttributeGetDataType()                     */
8594 /************************************************************************/
8595 
8596 /** Return the data type
8597  *
8598  * The return must be freed with GDALExtendedDataTypeRelease().
8599  */
GDALAttributeGetDataType(GDALAttributeH hAttr)8600 GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
8601 {
8602     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8603     return new GDALExtendedDataTypeHS(
8604         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
8605 }
8606 
8607 /************************************************************************/
8608 /*                       GDALAttributeReadAsRaw()                       */
8609 /************************************************************************/
8610 
8611 /** Return the raw value of an attribute.
8612  *
8613  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
8614  *
8615  * The returned buffer must be freed with GDALAttributeFreeRawResult()
8616  *
8617  * @param hAttr Attribute.
8618  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
8619  *
8620  * @return a buffer of *pnSize bytes.
8621  */
GDALAttributeReadAsRaw(GDALAttributeH hAttr,size_t * pnSize)8622 GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
8623 {
8624     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8625     VALIDATE_POINTER1( pnSize, __func__, nullptr );
8626     auto res(hAttr->m_poImpl->ReadAsRaw());
8627     *pnSize = res.size();
8628     auto ret = res.StealData();
8629     if( !ret )
8630     {
8631         *pnSize = 0;
8632         return nullptr;
8633     }
8634     return ret;
8635 }
8636 
8637 /************************************************************************/
8638 /*                       GDALAttributeFreeRawResult()                   */
8639 /************************************************************************/
8640 
8641 /** Free the return of GDALAttributeAsRaw()
8642  */
GDALAttributeFreeRawResult(GDALAttributeH hAttr,GByte * raw,CPL_UNUSED size_t nSize)8643 void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte* raw,
8644                                 CPL_UNUSED size_t nSize)
8645 {
8646     VALIDATE_POINTER0( hAttr, __func__ );
8647     if( raw )
8648     {
8649         const auto dt(hAttr->m_poImpl->GetDataType());
8650         const auto nDTSize(dt.GetSize());
8651         GByte* pabyPtr = raw;
8652         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
8653         CPLAssert( nSize == nDTSize * nEltCount );
8654         for( size_t i = 0; i < nEltCount; ++i )
8655         {
8656             dt.FreeDynamicMemory(pabyPtr);
8657             pabyPtr += nDTSize;
8658         }
8659         CPLFree(raw);
8660     }
8661 }
8662 
8663 /************************************************************************/
8664 /*                       GDALAttributeReadAsString()                    */
8665 /************************************************************************/
8666 
8667 /** Return the value of an attribute as a string.
8668  *
8669  * The returned string should not be freed, and its lifetime does not
8670  * excess a next call to ReadAsString() on the same object, or the deletion
8671  * of the object itself.
8672  *
8673  * This function will only return the first element if there are several.
8674  *
8675  * This is the same as the C++ method GDALAttribute::ReadAsString()
8676  *
8677  * @return a string, or nullptr.
8678  */
GDALAttributeReadAsString(GDALAttributeH hAttr)8679 const char* GDALAttributeReadAsString(GDALAttributeH hAttr)
8680 {
8681     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8682     return hAttr->m_poImpl->ReadAsString();
8683 }
8684 
8685 /************************************************************************/
8686 /*                      GDALAttributeReadAsInt()                        */
8687 /************************************************************************/
8688 
8689 /** Return the value of an attribute as a integer.
8690  *
8691  * This function will only return the first element if there are several.
8692  *
8693  * It can fail if its value can be converted to integer.
8694  *
8695  * This is the same as the C++ method GDALAttribute::ReadAsInt()
8696  *
8697  * @return a integer, or INT_MIN in case of error.
8698  */
GDALAttributeReadAsInt(GDALAttributeH hAttr)8699 int GDALAttributeReadAsInt(GDALAttributeH hAttr)
8700 {
8701     VALIDATE_POINTER1( hAttr, __func__, 0 );
8702     return hAttr->m_poImpl->ReadAsInt();
8703 }
8704 
8705 /************************************************************************/
8706 /*                       GDALAttributeReadAsDouble()                    */
8707 /************************************************************************/
8708 
8709 /** Return the value of an attribute as a double.
8710  *
8711  * This function will only return the first element if there are several.
8712  *
8713  * It can fail if its value can be converted to double.
8714  *
8715  * This is the same as the C++ method GDALAttribute::ReadAsDoubl()
8716  *
8717  * @return a double value.
8718  */
GDALAttributeReadAsDouble(GDALAttributeH hAttr)8719 double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
8720 {
8721     VALIDATE_POINTER1( hAttr, __func__, 0 );
8722     return hAttr->m_poImpl->ReadAsDouble();
8723 }
8724 
8725 /************************************************************************/
8726 /*                     GDALAttributeReadAsStringArray()                 */
8727 /************************************************************************/
8728 
8729 /** Return the value of an attribute as an array of strings.
8730  *
8731  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
8732  *
8733  * The return value must be freed with CSLDestroy().
8734  */
GDALAttributeReadAsStringArray(GDALAttributeH hAttr)8735 char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
8736 {
8737     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8738     return hAttr->m_poImpl->ReadAsStringArray().StealList();
8739 }
8740 
8741 /************************************************************************/
8742 /*                     GDALAttributeReadAsIntArray()                    */
8743 /************************************************************************/
8744 
8745 /** Return the value of an attribute as an array of integers.
8746  *
8747  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
8748  *
8749  * @param hAttr Attribute
8750  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
8751  * @return array to be freed with CPLFree(), or nullptr.
8752  */
GDALAttributeReadAsIntArray(GDALAttributeH hAttr,size_t * pnCount)8753 int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t* pnCount)
8754 {
8755     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8756     VALIDATE_POINTER1( pnCount, __func__, nullptr );
8757     *pnCount = 0;
8758     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
8759     if( tmp.empty() )
8760         return nullptr;
8761     auto ret = static_cast<int*>(VSI_MALLOC2_VERBOSE(tmp.size(),
8762                                                         sizeof(int)));
8763     if( !ret )
8764         return nullptr;
8765     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
8766     *pnCount = tmp.size();
8767     return ret;
8768 }
8769 
8770 /************************************************************************/
8771 /*                     GDALAttributeReadAsDoubleArray()                 */
8772 /************************************************************************/
8773 
8774 /** Return the value of an attribute as an array of doubles.
8775  *
8776  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
8777  *
8778  * @param hAttr Attribute
8779  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
8780  * @return array to be freed with CPLFree(), or nullptr.
8781  */
GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr,size_t * pnCount)8782 double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t* pnCount)
8783 {
8784     VALIDATE_POINTER1( hAttr, __func__, nullptr );
8785     VALIDATE_POINTER1( pnCount, __func__, nullptr );
8786     *pnCount = 0;
8787     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
8788     if( tmp.empty() )
8789         return nullptr;
8790     auto ret = static_cast<double*>(VSI_MALLOC2_VERBOSE(tmp.size(),
8791                                                         sizeof(double)));
8792     if( !ret )
8793         return nullptr;
8794     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
8795     *pnCount = tmp.size();
8796     return ret;
8797 }
8798 
8799 /************************************************************************/
8800 /*                     GDALAttributeWriteRaw()                          */
8801 /************************************************************************/
8802 
8803 /** Write an attribute from raw values expressed in GetDataType()
8804  *
8805  * The values should be provided in the type of GetDataType() and there should
8806  * be exactly GetTotalElementsCount() of them.
8807  * If GetDataType() is a string, each value should be a char* pointer.
8808  *
8809  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
8810  *
8811  * @param hAttr Attribute
8812  * @param pabyValue Buffer of nLen bytes.
8813  * @param nLength Size of pabyValue in bytes. Should be equal to
8814  *             GetTotalElementsCount() * GetDataType().GetSize()
8815  * @return TRUE in case of success.
8816  */
GDALAttributeWriteRaw(GDALAttributeH hAttr,const void * pabyValue,size_t nLength)8817 int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void* pabyValue, size_t nLength)
8818 {
8819     VALIDATE_POINTER1( hAttr, __func__, FALSE );
8820     return hAttr->m_poImpl->Write(pabyValue, nLength);
8821 }
8822 
8823 /************************************************************************/
8824 /*                     GDALAttributeWriteString()                       */
8825 /************************************************************************/
8826 
8827 /** Write an attribute from a string value.
8828  *
8829  * Type conversion will be performed if needed. If the attribute contains
8830  * multiple values, only the first one will be updated.
8831  *
8832  * This is the same as the C++ method GDALAttribute::Write(const char*)
8833  *
8834  * @param hAttr Attribute
8835  * @param pszVal Pointer to a string.
8836  * @return TRUE in case of success.
8837  */
GDALAttributeWriteString(GDALAttributeH hAttr,const char * pszVal)8838 int GDALAttributeWriteString(GDALAttributeH hAttr, const char* pszVal)
8839 {
8840     VALIDATE_POINTER1( hAttr, __func__, FALSE );
8841     return hAttr->m_poImpl->Write(pszVal);
8842 }
8843 
8844 /************************************************************************/
8845 /*                        GDALAttributeWriteInt()                       */
8846 /************************************************************************/
8847 
8848 /** Write an attribute from a integer value.
8849  *
8850  * Type conversion will be performed if needed. If the attribute contains
8851  * multiple values, only the first one will be updated.
8852  *
8853  * This is the same as the C++ method GDALAttribute::WriteInt()
8854  *
8855  * @param hAttr Attribute
8856  * @param nVal Value.
8857  * @return TRUE in case of success.
8858  */
GDALAttributeWriteInt(GDALAttributeH hAttr,int nVal)8859 int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
8860 {
8861     VALIDATE_POINTER1( hAttr, __func__, FALSE );
8862     return hAttr->m_poImpl->WriteInt(nVal);
8863 }
8864 
8865 /************************************************************************/
8866 /*                        GDALAttributeWriteDouble()                    */
8867 /************************************************************************/
8868 
8869 /** Write an attribute from a double value.
8870  *
8871  * Type conversion will be performed if needed. If the attribute contains
8872  * multiple values, only the first one will be updated.
8873  *
8874  * This is the same as the C++ method GDALAttribute::Write(double);
8875  *
8876  * @param hAttr Attribute
8877  * @param dfVal Value.
8878  *
8879  * @return TRUE in case of success.
8880  */
GDALAttributeWriteDouble(GDALAttributeH hAttr,double dfVal)8881 int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
8882 {
8883     VALIDATE_POINTER1( hAttr, __func__, FALSE );
8884     return hAttr->m_poImpl->Write(dfVal);
8885 }
8886 
8887 /************************************************************************/
8888 /*                       GDALAttributeWriteStringArray()                */
8889 /************************************************************************/
8890 
8891 /** Write an attribute from an array of strings.
8892  *
8893  * Type conversion will be performed if needed.
8894  *
8895  * Exactly GetTotalElementsCount() strings must be provided
8896  *
8897  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
8898  *
8899  * @param hAttr Attribute
8900  * @param papszValues Array of strings.
8901  * @return TRUE in case of success.
8902  */
GDALAttributeWriteStringArray(GDALAttributeH hAttr,CSLConstList papszValues)8903 int GDALAttributeWriteStringArray(GDALAttributeH hAttr, CSLConstList papszValues)
8904 {
8905     VALIDATE_POINTER1( hAttr, __func__, FALSE );
8906     return hAttr->m_poImpl->Write(papszValues);
8907 }
8908 
8909 
8910 /************************************************************************/
8911 /*                       GDALAttributeWriteDoubleArray()                */
8912 /************************************************************************/
8913 
8914 /** Write an attribute from an array of double.
8915  *
8916  * Type conversion will be performed if needed.
8917  *
8918  * Exactly GetTotalElementsCount() strings must be provided
8919  *
8920  * This is the same as the C++ method GDALAttribute::Write(const double *, size_t)
8921  *
8922  * @param hAttr Attribute
8923  * @param padfValues Array of double.
8924  * @param nCount Should be equal to GetTotalElementsCount().
8925  * @return TRUE in case of success.
8926  */
GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,const double * padfValues,size_t nCount)8927 int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
8928                                   const double* padfValues, size_t nCount)
8929 {
8930     VALIDATE_POINTER1( hAttr, __func__, FALSE );
8931     return hAttr->m_poImpl->Write(padfValues, nCount);
8932 }
8933 
8934 /************************************************************************/
8935 /*                        GDALDimensionRelease()                        */
8936 /************************************************************************/
8937 
8938 /** Release the GDAL in-memory object associated with a GDALDimension.
8939  *
8940  * Note: when applied on a object coming from a driver, this does not
8941  * destroy the object in the file, database, etc...
8942  */
GDALDimensionRelease(GDALDimensionH hDim)8943 void GDALDimensionRelease(GDALDimensionH hDim)
8944 {
8945     delete hDim;
8946 }
8947 
8948 /************************************************************************/
8949 /*                        GDALDimensionGetName()                        */
8950 /************************************************************************/
8951 
8952 /** Return dimension name.
8953  *
8954  * This is the same as the C++ method GDALDimension::GetName()
8955  */
GDALDimensionGetName(GDALDimensionH hDim)8956 const char *GDALDimensionGetName(GDALDimensionH hDim)
8957 {
8958     VALIDATE_POINTER1( hDim, __func__, nullptr );
8959     return hDim->m_poImpl->GetName().c_str();
8960 }
8961 
8962 /************************************************************************/
8963 /*                      GDALDimensionGetFullName()                      */
8964 /************************************************************************/
8965 
8966 /** Return dimension full name.
8967  *
8968  * This is the same as the C++ method GDALDimension::GetFullName()
8969  */
GDALDimensionGetFullName(GDALDimensionH hDim)8970 const char *GDALDimensionGetFullName(GDALDimensionH hDim)
8971 {
8972     VALIDATE_POINTER1( hDim, __func__, nullptr );
8973     return hDim->m_poImpl->GetFullName().c_str();
8974 }
8975 
8976 /************************************************************************/
8977 /*                        GDALDimensionGetType()                        */
8978 /************************************************************************/
8979 
8980 /** Return dimension type.
8981  *
8982  * This is the same as the C++ method GDALDimension::GetType()
8983  */
GDALDimensionGetType(GDALDimensionH hDim)8984 const char *GDALDimensionGetType(GDALDimensionH hDim)
8985 {
8986     VALIDATE_POINTER1( hDim, __func__, nullptr );
8987     return hDim->m_poImpl->GetType().c_str();
8988 }
8989 
8990 /************************************************************************/
8991 /*                     GDALDimensionGetDirection()                      */
8992 /************************************************************************/
8993 
8994 /** Return dimension direction.
8995  *
8996  * This is the same as the C++ method GDALDimension::GetDirection()
8997  */
GDALDimensionGetDirection(GDALDimensionH hDim)8998 const char *GDALDimensionGetDirection(GDALDimensionH hDim)
8999 {
9000     VALIDATE_POINTER1( hDim, __func__, nullptr );
9001     return hDim->m_poImpl->GetDirection().c_str();
9002 }
9003 
9004 /************************************************************************/
9005 /*                        GDALDimensionGetSize()                        */
9006 /************************************************************************/
9007 
9008 /** Return the size, that is the number of values along the dimension.
9009  *
9010  * This is the same as the C++ method GDALDimension::GetSize()
9011  */
GDALDimensionGetSize(GDALDimensionH hDim)9012 GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
9013 {
9014     VALIDATE_POINTER1( hDim, __func__, 0 );
9015     return hDim->m_poImpl->GetSize();
9016 }
9017 
9018 /************************************************************************/
9019 /*                     GDALDimensionGetIndexingVariable()               */
9020 /************************************************************************/
9021 
9022 /** Return the variable that is used to index the dimension (if there is one).
9023  *
9024  * This is the array, typically one-dimensional, describing the values taken
9025  * by the dimension.
9026  *
9027  * The returned value should be freed with GDALMDArrayRelease().
9028  *
9029  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
9030  */
GDALDimensionGetIndexingVariable(GDALDimensionH hDim)9031 GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
9032 {
9033     VALIDATE_POINTER1( hDim, __func__, nullptr );
9034     auto var(hDim->m_poImpl->GetIndexingVariable());
9035     if( !var )
9036         return nullptr;
9037     return new GDALMDArrayHS(var);
9038 }
9039 
9040 /************************************************************************/
9041 /*                      GDALDimensionSetIndexingVariable()              */
9042 /************************************************************************/
9043 
9044 /** Set the variable that is used to index the dimension.
9045  *
9046  * This is the array, typically one-dimensional, describing the values taken
9047  * by the dimension.
9048  *
9049  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
9050  *
9051  * @return TRUE in case of success.
9052  */
GDALDimensionSetIndexingVariable(GDALDimensionH hDim,GDALMDArrayH hArray)9053 int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
9054 {
9055     VALIDATE_POINTER1( hDim, __func__, FALSE );
9056     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl : nullptr);
9057 }
9058 
9059 /************************************************************************/
9060 /*                       GDALDatasetGetRootGroup()                      */
9061 /************************************************************************/
9062 
9063 /** Return the root GDALGroup of this dataset.
9064  *
9065  * Only valid for multidimensional datasets.
9066  *
9067  * The returned value must be freed with GDALGroupRelease().
9068  *
9069  * This is the same as the C++ method GDALDataset::GetRootGroup().
9070  *
9071  * @since GDAL 3.1
9072  */
GDALDatasetGetRootGroup(GDALDatasetH hDS)9073 GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
9074 {
9075     VALIDATE_POINTER1( hDS, __func__, nullptr );
9076     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
9077     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
9078 }
9079 
9080 
9081 /************************************************************************/
9082 /*                      GDALRasterBandAsMDArray()                        */
9083 /************************************************************************/
9084 
9085 /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
9086  *
9087  * The band must be linked to a GDALDataset. If this dataset is not already
9088  * marked as shared, it will be, so that the returned array holds a reference
9089  * to it.
9090  *
9091  * If the dataset has a geotransform attached, the X and Y dimensions of the
9092  * returned array will have an associated indexing variable.
9093  *
9094  * The returned pointer must be released with GDALMDArrayRelease().
9095  *
9096  * This is the same as the C++ method GDALRasterBand::AsMDArray().
9097  *
9098  * @return a new array, or NULL.
9099  *
9100  * @since GDAL 3.1
9101  */
GDALRasterBandAsMDArray(GDALRasterBandH hBand)9102 GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
9103 {
9104     VALIDATE_POINTER1( hBand, __func__, nullptr );
9105     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
9106     if( !poArray )
9107         return nullptr;
9108     return new GDALMDArrayHS(poArray);
9109 }
9110 
9111 
9112 /************************************************************************/
9113 /*                       GDALMDArrayAsClassicDataset()                  */
9114 /************************************************************************/
9115 
9116 /** Return a view of this array as a "classic" GDALDataset (ie 2D)
9117  *
9118  * Only 2D or more arrays are supported.
9119  *
9120  * In the case of > 2D arrays, additional dimensions will be represented as
9121  * raster bands.
9122  *
9123  * The "reverse" method is GDALRasterBand::AsMDArray().
9124  *
9125  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
9126  *
9127  * @param hArray Array.
9128  * @param iXDim Index of the dimension that will be used as the X/width axis.
9129  * @param iYDim Index of the dimension that will be used as the Y/height axis.
9130  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
9131  */
GDALMDArrayAsClassicDataset(GDALMDArrayH hArray,size_t iXDim,size_t iYDim)9132 GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray,
9133                                          size_t iXDim, size_t iYDim)
9134 {
9135     VALIDATE_POINTER1( hArray, __func__, nullptr );
9136     return GDALDataset::ToHandle(
9137         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
9138 }
9139 
9140 
9141 //! @cond Doxygen_Suppress
9142 
GDALAttributeString(const std::string & osParentName,const std::string & osName,const std::string & osValue)9143 GDALAttributeString::GDALAttributeString(const std::string& osParentName,
9144                   const std::string& osName,
9145                   const std::string& osValue):
9146         GDALAbstractMDArray(osParentName, osName),
9147         GDALAttribute(osParentName, osName),
9148         m_osValue(osValue)
9149 {}
9150 
GetDimensions() const9151 const std::vector<std::shared_ptr<GDALDimension>>& GDALAttributeString::GetDimensions() const
9152 {
9153     return m_dims;
9154 }
9155 
GetDataType() const9156 const GDALExtendedDataType &GDALAttributeString::GetDataType() const
9157 {
9158     return m_dt;
9159 }
9160 
IRead(const GUInt64 *,const size_t *,const GInt64 *,const GPtrDiff_t *,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const9161 bool GDALAttributeString::IRead(const GUInt64* ,
9162             const size_t* ,
9163             const GInt64* ,
9164             const GPtrDiff_t* ,
9165             const GDALExtendedDataType& bufferDataType,
9166             void* pDstBuffer) const
9167 {
9168     if( bufferDataType.GetClass() != GEDTC_STRING )
9169         return false;
9170     char* pszStr = static_cast<char*>(VSIMalloc(m_osValue.size() + 1));
9171     if( !pszStr )
9172         return false;
9173     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
9174     *static_cast<char**>(pDstBuffer) = pszStr;
9175     return true;
9176 }
9177 
GDALAttributeNumeric(const std::string & osParentName,const std::string & osName,double dfValue)9178 GDALAttributeNumeric::GDALAttributeNumeric(const std::string& osParentName,
9179                   const std::string& osName,
9180                   double dfValue):
9181         GDALAbstractMDArray(osParentName, osName),
9182         GDALAttribute(osParentName, osName),
9183         m_dt(GDALExtendedDataType::Create(GDT_Float64)),
9184         m_dfValue(dfValue)
9185 {}
9186 
GDALAttributeNumeric(const std::string & osParentName,const std::string & osName,int nValue)9187 GDALAttributeNumeric::GDALAttributeNumeric(const std::string& osParentName,
9188                   const std::string& osName,
9189                   int nValue):
9190         GDALAbstractMDArray(osParentName, osName),
9191         GDALAttribute(osParentName, osName),
9192         m_dt(GDALExtendedDataType::Create(GDT_Int32)),
9193         m_nValue(nValue)
9194 {}
9195 
GDALAttributeNumeric(const std::string & osParentName,const std::string & osName,const std::vector<GUInt32> & anValues)9196 GDALAttributeNumeric::GDALAttributeNumeric(const std::string& osParentName,
9197                   const std::string& osName,
9198                   const std::vector<GUInt32>& anValues):
9199         GDALAbstractMDArray(osParentName, osName),
9200         GDALAttribute(osParentName, osName),
9201         m_dt(GDALExtendedDataType::Create(GDT_UInt32)),
9202         m_anValuesUInt32(anValues)
9203 {
9204     m_dims.push_back(std::make_shared<GDALDimension>(
9205         std::string(), "dim0", std::string(), std::string(), m_anValuesUInt32.size()));
9206 }
9207 
GetDimensions() const9208 const std::vector<std::shared_ptr<GDALDimension>>& GDALAttributeNumeric::GetDimensions() const
9209 {
9210     return m_dims;
9211 }
9212 
GetDataType() const9213 const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
9214 {
9215     return m_dt;
9216 }
9217 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const9218 bool GDALAttributeNumeric::IRead(const GUInt64* arrayStartIdx,
9219                                  const size_t* count,
9220                                  const GInt64* arrayStep,
9221                                  const GPtrDiff_t* bufferStride,
9222                                  const GDALExtendedDataType& bufferDataType,
9223                                  void* pDstBuffer) const
9224 {
9225     if( m_dims.empty() )
9226     {
9227         if( m_dt.GetNumericDataType() == GDT_Float64 )
9228             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer, bufferDataType);
9229         else
9230         {
9231             CPLAssert( m_dt.GetNumericDataType() == GDT_Int32 );
9232             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer, bufferDataType);
9233         }
9234     }
9235     else
9236     {
9237         CPLAssert( m_dt.GetNumericDataType() == GDT_UInt32 );
9238         GByte* pabyDstBuffer = static_cast<GByte*>(pDstBuffer);
9239         for(size_t i = 0; i < count[0]; ++i )
9240         {
9241             GDALExtendedDataType::CopyValue(
9242                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] + i * arrayStep[0])],
9243                 m_dt,
9244                 pabyDstBuffer,
9245                 bufferDataType);
9246             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
9247         }
9248     }
9249     return true;
9250 }
9251 
GDALMDArrayRegularlySpaced(const std::string & osParentName,const std::string & osName,const std::shared_ptr<GDALDimension> & poDim,double dfStart,double dfIncrement,double dfOffsetInIncrement)9252 GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
9253                 const std::string& osParentName,
9254                 const std::string& osName,
9255                 const std::shared_ptr<GDALDimension>& poDim,
9256                 double dfStart, double dfIncrement,
9257                 double dfOffsetInIncrement):
9258     GDALAbstractMDArray(osParentName, osName),
9259     GDALMDArray(osParentName, osName),
9260     m_dfStart(dfStart),
9261     m_dfIncrement(dfIncrement),
9262     m_dfOffsetInIncrement(dfOffsetInIncrement),
9263     m_dims{poDim}
9264 {}
9265 
GetDimensions() const9266 const std::vector<std::shared_ptr<GDALDimension>>& GDALMDArrayRegularlySpaced::GetDimensions() const
9267 {
9268     return m_dims;
9269 }
9270 
GetDataType() const9271 const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
9272 {
9273     return m_dt;
9274 }
9275 
GetAttributes(CSLConstList) const9276 std::vector<std::shared_ptr<GDALAttribute>> GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
9277 {
9278     return m_attributes;
9279 }
9280 
AddAttribute(const std::shared_ptr<GDALAttribute> & poAttr)9281 void GDALMDArrayRegularlySpaced::AddAttribute(const std::shared_ptr<GDALAttribute>& poAttr)
9282 {
9283     m_attributes.emplace_back(poAttr);
9284 }
9285 
IRead(const GUInt64 * arrayStartIdx,const size_t * count,const GInt64 * arrayStep,const GPtrDiff_t * bufferStride,const GDALExtendedDataType & bufferDataType,void * pDstBuffer) const9286 bool GDALMDArrayRegularlySpaced::IRead(const GUInt64* arrayStartIdx,
9287                                        const size_t* count,
9288                                        const GInt64* arrayStep,
9289                                        const GPtrDiff_t* bufferStride,
9290                                        const GDALExtendedDataType& bufferDataType,
9291                                        void* pDstBuffer) const
9292 {
9293     GByte* pabyDstBuffer = static_cast<GByte*>(pDstBuffer);
9294     for( size_t i = 0; i < count[0]; i++ )
9295     {
9296         const double dfVal = m_dfStart +
9297             (arrayStartIdx[0] + i * arrayStep[0] + m_dfOffsetInIncrement) * m_dfIncrement;
9298         GDALExtendedDataType::CopyValue(&dfVal, m_dt,
9299                                         pabyDstBuffer, bufferDataType);
9300         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
9301     }
9302     return true;
9303 }
9304 
GDALDimensionWeakIndexingVar(const std::string & osParentName,const std::string & osName,const std::string & osType,const std::string & osDirection,GUInt64 nSize)9305 GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(const std::string& osParentName,
9306                   const std::string& osName,
9307                   const std::string& osType,
9308                   const std::string& osDirection,
9309                   GUInt64 nSize) :
9310         GDALDimension(osParentName, osName, osType, osDirection, nSize)
9311 {}
9312 
GetIndexingVariable() const9313 std::shared_ptr<GDALMDArray> GDALDimensionWeakIndexingVar::GetIndexingVariable() const
9314 {
9315     return m_poIndexingVariable.lock();
9316 }
9317 
9318 // cppcheck-suppress passedByValue
SetIndexingVariable(std::shared_ptr<GDALMDArray> poIndexingVariable)9319 bool GDALDimensionWeakIndexingVar::SetIndexingVariable(std::shared_ptr<GDALMDArray> poIndexingVariable)
9320 {
9321     m_poIndexingVariable = poIndexingVariable;
9322     return true;
9323 }
9324 
9325 //! @endcond
9326