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