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