Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Zarr driver
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "zarr.h"
14 : #include "ucs4_utf8.hpp"
15 :
16 : #include "cpl_float.h"
17 :
18 : #include "netcdf_cf_constants.h" // for CF_UNITS, etc
19 :
20 : #include <algorithm>
21 : #include <cassert>
22 : #include <cmath>
23 : #include <cstdlib>
24 : #include <limits>
25 : #include <map>
26 : #include <set>
27 :
28 : #if defined(__clang__) || defined(_MSC_VER)
29 : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
30 : #endif
31 :
32 : namespace
33 : {
34 :
35 4 : inline std::vector<GByte> UTF8ToUCS4(const char *pszStr, bool needByteSwap)
36 : {
37 4 : const size_t nLen = strlen(pszStr);
38 : // Worst case if that we need 4 more bytes than the UTF-8 one
39 : // (when the content is pure ASCII)
40 4 : if (nLen > std::numeric_limits<size_t>::max() / sizeof(uint32_t))
41 0 : throw std::bad_alloc();
42 4 : std::vector<GByte> ret(nLen * sizeof(uint32_t));
43 4 : size_t outPos = 0;
44 27 : for (size_t i = 0; i < nLen; outPos += sizeof(uint32_t))
45 : {
46 23 : uint32_t ucs4 = 0;
47 23 : int consumed = FcUtf8ToUcs4(
48 23 : reinterpret_cast<const uint8_t *>(pszStr + i), &ucs4, nLen - i);
49 23 : if (consumed <= 0)
50 : {
51 0 : ret.resize(outPos);
52 : }
53 23 : if (needByteSwap)
54 : {
55 1 : CPL_SWAP32PTR(&ucs4);
56 : }
57 23 : memcpy(&ret[outPos], &ucs4, sizeof(uint32_t));
58 23 : i += consumed;
59 : }
60 4 : ret.resize(outPos);
61 4 : return ret;
62 : }
63 :
64 6 : inline char *UCS4ToUTF8(const uint8_t *ucs4Ptr, size_t nSize, bool needByteSwap)
65 : {
66 : // A UCS4 char can require up to 6 bytes in UTF8.
67 6 : if (nSize > (std::numeric_limits<size_t>::max() - 1) / 6 * 4)
68 0 : return nullptr;
69 6 : const size_t nOutSize = nSize / 4 * 6 + 1;
70 6 : char *ret = static_cast<char *>(VSI_MALLOC_VERBOSE(nOutSize));
71 6 : if (ret == nullptr)
72 0 : return nullptr;
73 6 : size_t outPos = 0;
74 30 : for (size_t i = 0; i + sizeof(uint32_t) - 1 < nSize; i += sizeof(uint32_t))
75 : {
76 : uint32_t ucs4;
77 24 : memcpy(&ucs4, ucs4Ptr + i, sizeof(uint32_t));
78 24 : if (needByteSwap)
79 : {
80 2 : CPL_SWAP32PTR(&ucs4);
81 : }
82 : int written =
83 24 : FcUcs4ToUtf8(ucs4, reinterpret_cast<uint8_t *>(ret + outPos));
84 24 : outPos += written;
85 : }
86 6 : ret[outPos] = 0;
87 6 : return ret;
88 : }
89 :
90 : } // namespace
91 :
92 : /************************************************************************/
93 : /* ZarrArray::ParseChunkSize() */
94 : /************************************************************************/
95 :
96 776 : /* static */ bool ZarrArray::ParseChunkSize(const CPLJSONArray &oChunks,
97 : const GDALExtendedDataType &oType,
98 : std::vector<GUInt64> &anBlockSize)
99 : {
100 776 : size_t nBlockSize = oType.GetSize();
101 1949 : for (const auto &item : oChunks)
102 : {
103 1176 : const auto nSize = static_cast<GUInt64>(item.ToLong());
104 1176 : if (nSize == 0)
105 : {
106 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid content for chunks");
107 3 : return false;
108 : }
109 1175 : if (nBlockSize > std::numeric_limits<size_t>::max() / nSize)
110 : {
111 2 : CPLError(CE_Failure, CPLE_AppDefined, "Too large chunks");
112 2 : return false;
113 : }
114 1173 : nBlockSize *= static_cast<size_t>(nSize);
115 1173 : anBlockSize.emplace_back(nSize);
116 : }
117 :
118 773 : return true;
119 : }
120 :
121 : /************************************************************************/
122 : /* ZarrArray::ComputeTileCount() */
123 : /************************************************************************/
124 :
125 1163 : /* static */ uint64_t ZarrArray::ComputeTileCount(
126 : const std::string &osName,
127 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
128 : const std::vector<GUInt64> &anBlockSize)
129 : {
130 1163 : uint64_t nTotalTileCount = 1;
131 2863 : for (size_t i = 0; i < aoDims.size(); ++i)
132 : {
133 : uint64_t nTileThisDim =
134 1702 : (aoDims[i]->GetSize() / anBlockSize[i]) +
135 1702 : (((aoDims[i]->GetSize() % anBlockSize[i]) != 0) ? 1 : 0);
136 3404 : if (nTileThisDim != 0 &&
137 : nTotalTileCount >
138 1702 : std::numeric_limits<uint64_t>::max() / nTileThisDim)
139 : {
140 2 : CPLError(
141 : CE_Failure, CPLE_NotSupported,
142 : "Array %s has more than 2^64 tiles. This is not supported.",
143 : osName.c_str());
144 2 : return 0;
145 : }
146 1700 : nTotalTileCount *= nTileThisDim;
147 : }
148 1161 : return nTotalTileCount;
149 : }
150 :
151 : /************************************************************************/
152 : /* ZarrArray::ZarrArray() */
153 : /************************************************************************/
154 :
155 1163 : ZarrArray::ZarrArray(
156 : const std::shared_ptr<ZarrSharedResource> &poSharedResource,
157 : const std::string &osParentName, const std::string &osName,
158 : const std::vector<std::shared_ptr<GDALDimension>> &aoDims,
159 : const GDALExtendedDataType &oType, const std::vector<DtypeElt> &aoDtypeElts,
160 0 : const std::vector<GUInt64> &anBlockSize)
161 : :
162 : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
163 : GDALAbstractMDArray(osParentName, osName),
164 : #endif
165 : GDALPamMDArray(osParentName, osName, poSharedResource->GetPAM()),
166 : m_poSharedResource(poSharedResource), m_aoDims(aoDims), m_oType(oType),
167 : m_aoDtypeElts(aoDtypeElts), m_anBlockSize(anBlockSize),
168 1163 : m_oAttrGroup(m_osFullName, /*bContainerIsGroup=*/false)
169 : {
170 1163 : m_nTotalTileCount = ComputeTileCount(osName, aoDims, anBlockSize);
171 1163 : if (m_nTotalTileCount == 0)
172 2 : return;
173 :
174 : // Compute individual tile size
175 : const size_t nSourceSize =
176 1161 : m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
177 1161 : m_nTileSize = nSourceSize;
178 2857 : for (const auto &nBlockSize : m_anBlockSize)
179 : {
180 1696 : m_nTileSize *= static_cast<size_t>(nBlockSize);
181 : }
182 :
183 1161 : m_bUseOptimizedCodePaths = CPLTestBool(
184 : CPLGetConfigOption("GDAL_ZARR_USE_OPTIMIZED_CODE_PATHS", "YES"));
185 : }
186 :
187 : /************************************************************************/
188 : /* ~ZarrArray() */
189 : /************************************************************************/
190 :
191 1163 : ZarrArray::~ZarrArray()
192 : {
193 1163 : if (m_pabyNoData)
194 : {
195 175 : m_oType.FreeDynamicMemory(&m_pabyNoData[0]);
196 175 : CPLFree(m_pabyNoData);
197 : }
198 :
199 1163 : DeallocateDecodedTileData();
200 1163 : }
201 :
202 : /************************************************************************/
203 : /* ZarrArray::SerializeSpecialAttributes() */
204 : /************************************************************************/
205 :
206 403 : CPLJSONObject ZarrArray::SerializeSpecialAttributes()
207 : {
208 403 : m_bSRSModified = false;
209 403 : m_oAttrGroup.UnsetModified();
210 :
211 403 : auto oAttrs = m_oAttrGroup.Serialize();
212 :
213 403 : if (m_poSRS)
214 : {
215 36 : CPLJSONObject oCRS;
216 36 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
217 36 : char *pszWKT = nullptr;
218 36 : if (m_poSRS->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE)
219 : {
220 36 : oCRS.Add("wkt", pszWKT);
221 : }
222 36 : CPLFree(pszWKT);
223 :
224 : {
225 72 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
226 36 : char *projjson = nullptr;
227 72 : if (m_poSRS->exportToPROJJSON(&projjson, nullptr) == OGRERR_NONE &&
228 36 : projjson != nullptr)
229 : {
230 72 : CPLJSONDocument oDocProjJSON;
231 36 : if (oDocProjJSON.LoadMemory(std::string(projjson)))
232 : {
233 36 : oCRS.Add("projjson", oDocProjJSON.GetRoot());
234 : }
235 : }
236 36 : CPLFree(projjson);
237 : }
238 :
239 36 : const char *pszAuthorityCode = m_poSRS->GetAuthorityCode(nullptr);
240 36 : const char *pszAuthorityName = m_poSRS->GetAuthorityName(nullptr);
241 36 : if (pszAuthorityCode && pszAuthorityName &&
242 4 : EQUAL(pszAuthorityName, "EPSG"))
243 : {
244 4 : oCRS.Add("url",
245 8 : std::string("http://www.opengis.net/def/crs/EPSG/0/") +
246 : pszAuthorityCode);
247 : }
248 :
249 36 : oAttrs.Add(CRS_ATTRIBUTE_NAME, oCRS);
250 : }
251 :
252 403 : if (m_osUnit.empty())
253 : {
254 394 : if (m_bUnitModified)
255 0 : oAttrs.Delete(CF_UNITS);
256 : }
257 : else
258 : {
259 9 : oAttrs.Set(CF_UNITS, m_osUnit);
260 : }
261 403 : m_bUnitModified = false;
262 :
263 403 : if (!m_bHasOffset)
264 : {
265 400 : oAttrs.Delete(CF_ADD_OFFSET);
266 : }
267 : else
268 : {
269 3 : oAttrs.Set(CF_ADD_OFFSET, m_dfOffset);
270 : }
271 403 : m_bOffsetModified = false;
272 :
273 403 : if (!m_bHasScale)
274 : {
275 400 : oAttrs.Delete(CF_SCALE_FACTOR);
276 : }
277 : else
278 : {
279 3 : oAttrs.Set(CF_SCALE_FACTOR, m_dfScale);
280 : }
281 403 : m_bScaleModified = false;
282 :
283 403 : return oAttrs;
284 : }
285 :
286 : /************************************************************************/
287 : /* FillBlockSize() */
288 : /************************************************************************/
289 :
290 : /* static */
291 428 : bool ZarrArray::FillBlockSize(
292 : const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
293 : const GDALExtendedDataType &oDataType, std::vector<GUInt64> &anBlockSize,
294 : CSLConstList papszOptions)
295 : {
296 428 : const auto nDims = aoDimensions.size();
297 428 : anBlockSize.resize(nDims);
298 1017 : for (size_t i = 0; i < nDims; ++i)
299 589 : anBlockSize[i] = 1;
300 428 : if (nDims >= 2)
301 : {
302 324 : anBlockSize[nDims - 2] =
303 324 : std::min(std::max<GUInt64>(1, aoDimensions[nDims - 2]->GetSize()),
304 324 : static_cast<GUInt64>(256));
305 324 : anBlockSize[nDims - 1] =
306 324 : std::min(std::max<GUInt64>(1, aoDimensions[nDims - 1]->GetSize()),
307 486 : static_cast<GUInt64>(256));
308 : }
309 266 : else if (nDims == 1)
310 : {
311 219 : anBlockSize[0] = std::max<GUInt64>(1, aoDimensions[0]->GetSize());
312 : }
313 :
314 428 : const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
315 428 : if (pszBlockSize)
316 : {
317 : const auto aszTokens(
318 19 : CPLStringList(CSLTokenizeString2(pszBlockSize, ",", 0)));
319 19 : if (static_cast<size_t>(aszTokens.size()) != nDims)
320 : {
321 5 : CPLError(CE_Failure, CPLE_AppDefined,
322 : "Invalid number of values in BLOCKSIZE");
323 5 : return false;
324 : }
325 14 : size_t nBlockSize = oDataType.GetSize();
326 43 : for (size_t i = 0; i < nDims; ++i)
327 : {
328 31 : anBlockSize[i] = static_cast<GUInt64>(CPLAtoGIntBig(aszTokens[i]));
329 31 : if (anBlockSize[i] == 0)
330 : {
331 1 : CPLError(CE_Failure, CPLE_AppDefined,
332 : "Values in BLOCKSIZE should be > 0");
333 1 : return false;
334 : }
335 30 : if (anBlockSize[i] >
336 30 : std::numeric_limits<size_t>::max() / nBlockSize)
337 : {
338 1 : CPLError(CE_Failure, CPLE_AppDefined,
339 : "Too large values in BLOCKSIZE");
340 1 : return false;
341 : }
342 29 : nBlockSize *= static_cast<size_t>(anBlockSize[i]);
343 : }
344 : }
345 421 : return true;
346 : }
347 :
348 : /************************************************************************/
349 : /* DeallocateDecodedTileData() */
350 : /************************************************************************/
351 :
352 2652 : void ZarrArray::DeallocateDecodedTileData()
353 : {
354 2652 : if (!m_abyDecodedTileData.empty())
355 : {
356 224 : const size_t nDTSize = m_oType.GetSize();
357 224 : GByte *pDst = &m_abyDecodedTileData[0];
358 224 : const size_t nValues = m_abyDecodedTileData.size() / nDTSize;
359 456 : for (const auto &elt : m_aoDtypeElts)
360 : {
361 232 : if (elt.nativeType == DtypeElt::NativeType::STRING_ASCII ||
362 231 : elt.nativeType == DtypeElt::NativeType::STRING_UNICODE)
363 : {
364 2 : for (size_t i = 0; i < nValues; i++, pDst += nDTSize)
365 : {
366 : char *ptr;
367 1 : char **pptr =
368 1 : reinterpret_cast<char **>(pDst + elt.gdalOffset);
369 1 : memcpy(&ptr, pptr, sizeof(ptr));
370 1 : VSIFree(ptr);
371 : }
372 : }
373 : }
374 : }
375 2652 : }
376 :
377 : /************************************************************************/
378 : /* EncodeElt() */
379 : /************************************************************************/
380 :
381 : /* Encode from GDAL raw type to Zarr native type */
382 : /*static*/
383 121 : void ZarrArray::EncodeElt(const std::vector<DtypeElt> &elts, const GByte *pSrc,
384 : GByte *pDst)
385 : {
386 243 : for (const auto &elt : elts)
387 : {
388 122 : if (elt.nativeType == DtypeElt::NativeType::STRING_UNICODE)
389 : {
390 0 : const char *pStr =
391 0 : *reinterpret_cast<const char *const *>(pSrc + elt.gdalOffset);
392 0 : if (pStr)
393 : {
394 : try
395 : {
396 0 : const auto ucs4 = UTF8ToUCS4(pStr, elt.needByteSwapping);
397 0 : const auto ucs4Len = ucs4.size();
398 0 : memcpy(pDst + elt.nativeOffset, ucs4.data(),
399 0 : std::min(ucs4Len, elt.nativeSize));
400 0 : if (ucs4Len > elt.nativeSize)
401 : {
402 0 : CPLError(CE_Warning, CPLE_AppDefined,
403 : "Too long string truncated");
404 : }
405 0 : else if (ucs4Len < elt.nativeSize)
406 : {
407 0 : memset(pDst + elt.nativeOffset + ucs4Len, 0,
408 0 : elt.nativeSize - ucs4Len);
409 : }
410 : }
411 0 : catch (const std::exception &)
412 : {
413 0 : memset(pDst + elt.nativeOffset, 0, elt.nativeSize);
414 : }
415 : }
416 : else
417 : {
418 0 : memset(pDst + elt.nativeOffset, 0, elt.nativeSize);
419 : }
420 : }
421 122 : else if (elt.needByteSwapping)
422 : {
423 120 : if (elt.nativeSize == 2)
424 : {
425 24 : if (elt.gdalTypeIsApproxOfNative)
426 : {
427 0 : CPLAssert(elt.nativeType == DtypeElt::NativeType::IEEEFP);
428 0 : CPLAssert(elt.gdalType.GetNumericDataType() == GDT_Float32);
429 0 : const uint32_t uint32Val =
430 0 : *reinterpret_cast<const uint32_t *>(pSrc +
431 0 : elt.gdalOffset);
432 0 : bool bHasWarned = false;
433 : uint16_t uint16Val =
434 0 : CPL_SWAP16(CPLFloatToHalf(uint32Val, bHasWarned));
435 0 : memcpy(pDst + elt.nativeOffset, &uint16Val,
436 : sizeof(uint16Val));
437 : }
438 : else
439 : {
440 24 : const uint16_t val =
441 24 : CPL_SWAP16(*reinterpret_cast<const uint16_t *>(
442 : pSrc + elt.gdalOffset));
443 24 : memcpy(pDst + elt.nativeOffset, &val, sizeof(val));
444 : }
445 : }
446 96 : else if (elt.nativeSize == 4)
447 : {
448 36 : const uint32_t val = CPL_SWAP32(
449 : *reinterpret_cast<const uint32_t *>(pSrc + elt.gdalOffset));
450 36 : memcpy(pDst + elt.nativeOffset, &val, sizeof(val));
451 : }
452 60 : else if (elt.nativeSize == 8)
453 : {
454 48 : if (elt.nativeType == DtypeElt::NativeType::COMPLEX_IEEEFP)
455 : {
456 12 : uint32_t val =
457 12 : CPL_SWAP32(*reinterpret_cast<const uint32_t *>(
458 : pSrc + elt.gdalOffset));
459 12 : memcpy(pDst + elt.nativeOffset, &val, sizeof(val));
460 12 : val = CPL_SWAP32(*reinterpret_cast<const uint32_t *>(
461 : pSrc + elt.gdalOffset + 4));
462 12 : memcpy(pDst + elt.nativeOffset + 4, &val, sizeof(val));
463 : }
464 : else
465 : {
466 36 : const uint64_t val =
467 36 : CPL_SWAP64(*reinterpret_cast<const uint64_t *>(
468 : pSrc + elt.gdalOffset));
469 36 : memcpy(pDst + elt.nativeOffset, &val, sizeof(val));
470 : }
471 : }
472 12 : else if (elt.nativeSize == 16)
473 : {
474 12 : uint64_t val = CPL_SWAP64(
475 : *reinterpret_cast<const uint64_t *>(pSrc + elt.gdalOffset));
476 12 : memcpy(pDst + elt.nativeOffset, &val, sizeof(val));
477 12 : val = CPL_SWAP64(*reinterpret_cast<const uint64_t *>(
478 : pSrc + elt.gdalOffset + 8));
479 12 : memcpy(pDst + elt.nativeOffset + 8, &val, sizeof(val));
480 : }
481 : else
482 : {
483 0 : CPLAssert(false);
484 : }
485 : }
486 2 : else if (elt.gdalTypeIsApproxOfNative)
487 : {
488 0 : if (elt.nativeType == DtypeElt::NativeType::IEEEFP &&
489 0 : elt.nativeSize == 2)
490 : {
491 0 : CPLAssert(elt.gdalType.GetNumericDataType() == GDT_Float32);
492 0 : const uint32_t uint32Val =
493 0 : *reinterpret_cast<const uint32_t *>(pSrc + elt.gdalOffset);
494 0 : bool bHasWarned = false;
495 : const uint16_t uint16Val =
496 0 : CPLFloatToHalf(uint32Val, bHasWarned);
497 0 : memcpy(pDst + elt.nativeOffset, &uint16Val, sizeof(uint16Val));
498 : }
499 : else
500 : {
501 0 : CPLAssert(false);
502 : }
503 : }
504 2 : else if (elt.nativeType == DtypeElt::NativeType::STRING_ASCII)
505 : {
506 0 : const char *pStr =
507 0 : *reinterpret_cast<const char *const *>(pSrc + elt.gdalOffset);
508 0 : if (pStr)
509 : {
510 0 : const size_t nLen = strlen(pStr);
511 0 : memcpy(pDst + elt.nativeOffset, pStr,
512 0 : std::min(nLen, elt.nativeSize));
513 0 : if (nLen < elt.nativeSize)
514 0 : memset(pDst + elt.nativeOffset + nLen, 0,
515 0 : elt.nativeSize - nLen);
516 : }
517 : else
518 : {
519 0 : memset(pDst + elt.nativeOffset, 0, elt.nativeSize);
520 : }
521 : }
522 : else
523 : {
524 2 : CPLAssert(elt.nativeSize == elt.gdalSize);
525 2 : memcpy(pDst + elt.nativeOffset, pSrc + elt.gdalOffset,
526 2 : elt.nativeSize);
527 : }
528 : }
529 121 : }
530 :
531 : /************************************************************************/
532 : /* ZarrArray::SerializeNumericNoData() */
533 : /************************************************************************/
534 :
535 16 : void ZarrArray::SerializeNumericNoData(CPLJSONObject &oRoot) const
536 : {
537 16 : if (m_oType.GetNumericDataType() == GDT_Int64)
538 : {
539 0 : const auto nVal = GetNoDataValueAsInt64();
540 0 : oRoot.Add("fill_value", static_cast<GInt64>(nVal));
541 : }
542 16 : else if (m_oType.GetNumericDataType() == GDT_UInt64)
543 : {
544 0 : const auto nVal = GetNoDataValueAsUInt64();
545 0 : oRoot.Add("fill_value", static_cast<uint64_t>(nVal));
546 : }
547 : else
548 : {
549 16 : const double dfVal = GetNoDataValueAsDouble();
550 16 : if (std::isnan(dfVal))
551 2 : oRoot.Add("fill_value", "NaN");
552 14 : else if (dfVal == std::numeric_limits<double>::infinity())
553 2 : oRoot.Add("fill_value", "Infinity");
554 12 : else if (dfVal == -std::numeric_limits<double>::infinity())
555 2 : oRoot.Add("fill_value", "-Infinity");
556 10 : else if (GDALDataTypeIsInteger(m_oType.GetNumericDataType()))
557 8 : oRoot.Add("fill_value", static_cast<GInt64>(dfVal));
558 : else
559 2 : oRoot.Add("fill_value", dfVal);
560 : }
561 16 : }
562 :
563 : /************************************************************************/
564 : /* ZarrArray::GetSpatialRef() */
565 : /************************************************************************/
566 :
567 16 : std::shared_ptr<OGRSpatialReference> ZarrArray::GetSpatialRef() const
568 : {
569 16 : if (!CheckValidAndErrorOutIfNot())
570 0 : return nullptr;
571 :
572 16 : if (m_poSRS)
573 6 : return m_poSRS;
574 10 : return GDALPamMDArray::GetSpatialRef();
575 : }
576 :
577 : /************************************************************************/
578 : /* SetRawNoDataValue() */
579 : /************************************************************************/
580 :
581 18 : bool ZarrArray::SetRawNoDataValue(const void *pRawNoData)
582 : {
583 18 : if (!CheckValidAndErrorOutIfNot())
584 0 : return false;
585 :
586 18 : if (!m_bUpdatable)
587 : {
588 0 : CPLError(CE_Failure, CPLE_AppDefined, "Array opened in read-only mode");
589 0 : return false;
590 : }
591 18 : m_bDefinitionModified = true;
592 18 : RegisterNoDataValue(pRawNoData);
593 18 : return true;
594 : }
595 :
596 : /************************************************************************/
597 : /* RegisterNoDataValue() */
598 : /************************************************************************/
599 :
600 175 : void ZarrArray::RegisterNoDataValue(const void *pNoData)
601 : {
602 175 : if (m_pabyNoData)
603 : {
604 0 : m_oType.FreeDynamicMemory(&m_pabyNoData[0]);
605 : }
606 :
607 175 : if (pNoData == nullptr)
608 : {
609 0 : CPLFree(m_pabyNoData);
610 0 : m_pabyNoData = nullptr;
611 : }
612 : else
613 : {
614 175 : const auto nSize = m_oType.GetSize();
615 175 : if (m_pabyNoData == nullptr)
616 : {
617 175 : m_pabyNoData = static_cast<GByte *>(CPLMalloc(nSize));
618 : }
619 175 : memset(m_pabyNoData, 0, nSize);
620 175 : GDALExtendedDataType::CopyValue(pNoData, m_oType, m_pabyNoData,
621 175 : m_oType);
622 : }
623 175 : }
624 :
625 : /************************************************************************/
626 : /* ZarrArray::BlockTranspose() */
627 : /************************************************************************/
628 :
629 66 : void ZarrArray::BlockTranspose(const ZarrByteVectorQuickResize &abySrc,
630 : ZarrByteVectorQuickResize &abyDst,
631 : bool bDecode) const
632 : {
633 : // Perform transposition
634 66 : const size_t nDims = m_anBlockSize.size();
635 : const size_t nSourceSize =
636 66 : m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
637 :
638 : struct Stack
639 : {
640 : size_t nIters = 0;
641 : const GByte *src_ptr = nullptr;
642 : GByte *dst_ptr = nullptr;
643 : size_t src_inc_offset = 0;
644 : size_t dst_inc_offset = 0;
645 : };
646 :
647 132 : std::vector<Stack> stack(nDims);
648 : stack.emplace_back(
649 66 : Stack()); // to make gcc 9.3 -O2 -Wnull-dereference happy
650 :
651 66 : if (bDecode)
652 : {
653 46 : stack[0].src_inc_offset = nSourceSize;
654 118 : for (size_t i = 1; i < nDims; ++i)
655 : {
656 144 : stack[i].src_inc_offset = stack[i - 1].src_inc_offset *
657 72 : static_cast<size_t>(m_anBlockSize[i - 1]);
658 : }
659 :
660 46 : stack[nDims - 1].dst_inc_offset = nSourceSize;
661 118 : for (size_t i = nDims - 1; i > 0;)
662 : {
663 72 : --i;
664 144 : stack[i].dst_inc_offset = stack[i + 1].dst_inc_offset *
665 72 : static_cast<size_t>(m_anBlockSize[i + 1]);
666 : }
667 : }
668 : else
669 : {
670 20 : stack[0].dst_inc_offset = nSourceSize;
671 60 : for (size_t i = 1; i < nDims; ++i)
672 : {
673 80 : stack[i].dst_inc_offset = stack[i - 1].dst_inc_offset *
674 40 : static_cast<size_t>(m_anBlockSize[i - 1]);
675 : }
676 :
677 20 : stack[nDims - 1].src_inc_offset = nSourceSize;
678 60 : for (size_t i = nDims - 1; i > 0;)
679 : {
680 40 : --i;
681 80 : stack[i].src_inc_offset = stack[i + 1].src_inc_offset *
682 40 : static_cast<size_t>(m_anBlockSize[i + 1]);
683 : }
684 : }
685 :
686 66 : stack[0].src_ptr = abySrc.data();
687 66 : stack[0].dst_ptr = &abyDst[0];
688 :
689 66 : size_t dimIdx = 0;
690 1058 : lbl_next_depth:
691 1058 : if (dimIdx == nDims)
692 : {
693 744 : void *dst_ptr = stack[nDims].dst_ptr;
694 744 : const void *src_ptr = stack[nDims].src_ptr;
695 744 : if (nSourceSize == 1)
696 264 : *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
697 480 : else if (nSourceSize == 2)
698 120 : *static_cast<uint16_t *>(dst_ptr) =
699 120 : *static_cast<const uint16_t *>(src_ptr);
700 360 : else if (nSourceSize == 4)
701 168 : *static_cast<uint32_t *>(dst_ptr) =
702 168 : *static_cast<const uint32_t *>(src_ptr);
703 192 : else if (nSourceSize == 8)
704 168 : *static_cast<uint64_t *>(dst_ptr) =
705 168 : *static_cast<const uint64_t *>(src_ptr);
706 : else
707 24 : memcpy(dst_ptr, src_ptr, nSourceSize);
708 : }
709 : else
710 : {
711 314 : stack[dimIdx].nIters = static_cast<size_t>(m_anBlockSize[dimIdx]);
712 : while (true)
713 : {
714 992 : dimIdx++;
715 992 : stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
716 992 : stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
717 992 : goto lbl_next_depth;
718 992 : lbl_return_to_caller:
719 992 : dimIdx--;
720 992 : if ((--stack[dimIdx].nIters) == 0)
721 314 : break;
722 678 : stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
723 678 : stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
724 : }
725 : }
726 1058 : if (dimIdx > 0)
727 992 : goto lbl_return_to_caller;
728 66 : }
729 :
730 : /************************************************************************/
731 : /* DecodeSourceElt() */
732 : /************************************************************************/
733 :
734 : /* static */
735 1938 : void ZarrArray::DecodeSourceElt(const std::vector<DtypeElt> &elts,
736 : const GByte *pSrc, GByte *pDst)
737 : {
738 3908 : for (const auto &elt : elts)
739 : {
740 1970 : if (elt.nativeType == DtypeElt::NativeType::STRING_UNICODE)
741 : {
742 : char *ptr;
743 0 : char **pDstPtr = reinterpret_cast<char **>(pDst + elt.gdalOffset);
744 0 : memcpy(&ptr, pDstPtr, sizeof(ptr));
745 0 : VSIFree(ptr);
746 :
747 0 : char *pDstStr = UCS4ToUTF8(pSrc + elt.nativeOffset, elt.nativeSize,
748 0 : elt.needByteSwapping);
749 0 : memcpy(pDstPtr, &pDstStr, sizeof(pDstStr));
750 : }
751 1970 : else if (elt.needByteSwapping)
752 : {
753 1922 : if (elt.nativeSize == 2)
754 : {
755 : uint16_t val;
756 386 : memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
757 386 : if (elt.gdalTypeIsApproxOfNative)
758 : {
759 2 : CPLAssert(elt.nativeType == DtypeElt::NativeType::IEEEFP);
760 2 : CPLAssert(elt.gdalType.GetNumericDataType() == GDT_Float32);
761 2 : uint32_t uint32Val = CPLHalfToFloat(CPL_SWAP16(val));
762 2 : memcpy(pDst + elt.gdalOffset, &uint32Val,
763 : sizeof(uint32Val));
764 : }
765 : else
766 : {
767 384 : *reinterpret_cast<uint16_t *>(pDst + elt.gdalOffset) =
768 384 : CPL_SWAP16(val);
769 : }
770 : }
771 1536 : else if (elt.nativeSize == 4)
772 : {
773 : uint32_t val;
774 576 : memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
775 576 : *reinterpret_cast<uint32_t *>(pDst + elt.gdalOffset) =
776 576 : CPL_SWAP32(val);
777 : }
778 960 : else if (elt.nativeSize == 8)
779 : {
780 768 : if (elt.nativeType == DtypeElt::NativeType::COMPLEX_IEEEFP)
781 : {
782 : uint32_t val;
783 192 : memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
784 192 : *reinterpret_cast<uint32_t *>(pDst + elt.gdalOffset) =
785 192 : CPL_SWAP32(val);
786 192 : memcpy(&val, pSrc + elt.nativeOffset + 4, sizeof(val));
787 192 : *reinterpret_cast<uint32_t *>(pDst + elt.gdalOffset + 4) =
788 192 : CPL_SWAP32(val);
789 : }
790 : else
791 : {
792 : uint64_t val;
793 576 : memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
794 576 : *reinterpret_cast<uint64_t *>(pDst + elt.gdalOffset) =
795 576 : CPL_SWAP64(val);
796 : }
797 : }
798 192 : else if (elt.nativeSize == 16)
799 : {
800 : uint64_t val;
801 192 : memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
802 192 : *reinterpret_cast<uint64_t *>(pDst + elt.gdalOffset) =
803 192 : CPL_SWAP64(val);
804 192 : memcpy(&val, pSrc + elt.nativeOffset + 8, sizeof(val));
805 192 : *reinterpret_cast<uint64_t *>(pDst + elt.gdalOffset + 8) =
806 192 : CPL_SWAP64(val);
807 : }
808 : else
809 : {
810 0 : CPLAssert(false);
811 : }
812 : }
813 48 : else if (elt.gdalTypeIsApproxOfNative)
814 : {
815 2 : if (elt.nativeType == DtypeElt::NativeType::IEEEFP &&
816 2 : elt.nativeSize == 2)
817 : {
818 2 : CPLAssert(elt.gdalType.GetNumericDataType() == GDT_Float32);
819 : uint16_t uint16Val;
820 2 : memcpy(&uint16Val, pSrc + elt.nativeOffset, sizeof(uint16Val));
821 2 : uint32_t uint32Val = CPLHalfToFloat(uint16Val);
822 2 : memcpy(pDst + elt.gdalOffset, &uint32Val, sizeof(uint32Val));
823 : }
824 : else
825 : {
826 0 : CPLAssert(false);
827 : }
828 : }
829 46 : else if (elt.nativeType == DtypeElt::NativeType::STRING_ASCII)
830 : {
831 : char *ptr;
832 3 : char **pDstPtr = reinterpret_cast<char **>(pDst + elt.gdalOffset);
833 3 : memcpy(&ptr, pDstPtr, sizeof(ptr));
834 3 : VSIFree(ptr);
835 :
836 3 : char *pDstStr = static_cast<char *>(CPLMalloc(elt.nativeSize + 1));
837 3 : memcpy(pDstStr, pSrc + elt.nativeOffset, elt.nativeSize);
838 3 : pDstStr[elt.nativeSize] = 0;
839 3 : memcpy(pDstPtr, &pDstStr, sizeof(pDstStr));
840 : }
841 : else
842 : {
843 43 : CPLAssert(elt.nativeSize == elt.gdalSize);
844 43 : memcpy(pDst + elt.gdalOffset, pSrc + elt.nativeOffset,
845 43 : elt.nativeSize);
846 : }
847 : }
848 1938 : }
849 :
850 : /************************************************************************/
851 : /* ZarrArray::IAdviseReadCommon() */
852 : /************************************************************************/
853 :
854 12 : bool ZarrArray::IAdviseReadCommon(const GUInt64 *arrayStartIdx,
855 : const size_t *count,
856 : CSLConstList papszOptions,
857 : std::vector<uint64_t> &anIndicesCur,
858 : int &nThreadsMax,
859 : std::vector<uint64_t> &anReqTilesIndices,
860 : size_t &nReqTiles) const
861 : {
862 12 : if (!CheckValidAndErrorOutIfNot())
863 0 : return false;
864 :
865 12 : const size_t nDims = m_aoDims.size();
866 12 : anIndicesCur.resize(nDims);
867 24 : std::vector<uint64_t> anIndicesMin(nDims);
868 24 : std::vector<uint64_t> anIndicesMax(nDims);
869 :
870 : // Compute min and max tile indices in each dimension, and the total
871 : // nomber of tiles this represents.
872 12 : nReqTiles = 1;
873 36 : for (size_t i = 0; i < nDims; ++i)
874 : {
875 24 : anIndicesMin[i] = arrayStartIdx[i] / m_anBlockSize[i];
876 24 : anIndicesMax[i] = (arrayStartIdx[i] + count[i] - 1) / m_anBlockSize[i];
877 : // Overflow on number of tiles already checked in Create()
878 24 : nReqTiles *= static_cast<size_t>(anIndicesMax[i] - anIndicesMin[i] + 1);
879 : }
880 :
881 : // Find available cache size
882 12 : const size_t nCacheSize = [papszOptions]()
883 : {
884 : size_t nCacheSizeTmp;
885 : const char *pszCacheSize =
886 12 : CSLFetchNameValue(papszOptions, "CACHE_SIZE");
887 12 : if (pszCacheSize)
888 : {
889 4 : const auto nCacheSizeBig = CPLAtoGIntBig(pszCacheSize);
890 8 : if (nCacheSizeBig < 0 || static_cast<uint64_t>(nCacheSizeBig) >
891 4 : std::numeric_limits<size_t>::max() / 2)
892 : {
893 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Too big CACHE_SIZE");
894 0 : return std::numeric_limits<size_t>::max();
895 : }
896 4 : nCacheSizeTmp = static_cast<size_t>(nCacheSizeBig);
897 : }
898 : else
899 : {
900 : // Arbitrarily take half of remaining cache size
901 8 : nCacheSizeTmp = static_cast<size_t>(std::min(
902 16 : static_cast<uint64_t>(
903 8 : (GDALGetCacheMax64() - GDALGetCacheUsed64()) / 2),
904 16 : static_cast<uint64_t>(std::numeric_limits<size_t>::max() / 2)));
905 8 : CPLDebug(ZARR_DEBUG_KEY, "Using implicit CACHE_SIZE=" CPL_FRMT_GUIB,
906 : static_cast<GUIntBig>(nCacheSizeTmp));
907 : }
908 12 : return nCacheSizeTmp;
909 12 : }();
910 12 : if (nCacheSize == std::numeric_limits<size_t>::max())
911 0 : return false;
912 :
913 : // Check that cache size is sufficient to hold all needed tiles.
914 : // Also check that anReqTilesIndices size computation won't overflow.
915 12 : if (nReqTiles > nCacheSize / std::max(m_nTileSize, nDims))
916 : {
917 4 : CPLError(
918 : CE_Failure, CPLE_OutOfMemory,
919 : "CACHE_SIZE=" CPL_FRMT_GUIB " is not big enough to cache "
920 : "all needed tiles. "
921 : "At least " CPL_FRMT_GUIB " bytes would be needed",
922 : static_cast<GUIntBig>(nCacheSize),
923 4 : static_cast<GUIntBig>(nReqTiles * std::max(m_nTileSize, nDims)));
924 4 : return false;
925 : }
926 :
927 8 : const char *pszNumThreads = CSLFetchNameValueDef(
928 : papszOptions, "NUM_THREADS",
929 : CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"));
930 8 : if (EQUAL(pszNumThreads, "ALL_CPUS"))
931 8 : nThreadsMax = CPLGetNumCPUs();
932 : else
933 0 : nThreadsMax = std::max(1, atoi(pszNumThreads));
934 8 : if (nThreadsMax > 1024)
935 0 : nThreadsMax = 1024;
936 8 : if (nThreadsMax <= 1)
937 0 : return true;
938 8 : CPLDebug(ZARR_DEBUG_KEY, "IAdviseRead(): Using up to %d threads",
939 : nThreadsMax);
940 :
941 8 : m_oMapTileIndexToCachedTile.clear();
942 :
943 : // Overflow checked above
944 : try
945 : {
946 8 : anReqTilesIndices.resize(nDims * nReqTiles);
947 : }
948 0 : catch (const std::bad_alloc &e)
949 : {
950 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
951 0 : "Cannot allocate anReqTilesIndices: %s", e.what());
952 0 : return false;
953 : }
954 :
955 8 : size_t dimIdx = 0;
956 8 : size_t nTileIter = 0;
957 21608 : lbl_next_depth:
958 21608 : if (dimIdx == nDims)
959 : {
960 21344 : if (nDims == 2)
961 : {
962 : // optimize in common case
963 21344 : memcpy(&anReqTilesIndices[nTileIter * nDims], anIndicesCur.data(),
964 : sizeof(uint64_t) * 2);
965 : }
966 0 : else if (nDims == 3)
967 : {
968 : // optimize in common case
969 0 : memcpy(&anReqTilesIndices[nTileIter * nDims], anIndicesCur.data(),
970 : sizeof(uint64_t) * 3);
971 : }
972 : else
973 : {
974 0 : memcpy(&anReqTilesIndices[nTileIter * nDims], anIndicesCur.data(),
975 0 : sizeof(uint64_t) * nDims);
976 : }
977 21344 : nTileIter++;
978 : }
979 : else
980 : {
981 : // This level of loop loops over blocks
982 264 : anIndicesCur[dimIdx] = anIndicesMin[dimIdx];
983 : while (true)
984 : {
985 21600 : dimIdx++;
986 21600 : goto lbl_next_depth;
987 21600 : lbl_return_to_caller:
988 21600 : dimIdx--;
989 21600 : if (anIndicesCur[dimIdx] == anIndicesMax[dimIdx])
990 264 : break;
991 21336 : ++anIndicesCur[dimIdx];
992 : }
993 : }
994 21608 : if (dimIdx > 0)
995 21600 : goto lbl_return_to_caller;
996 8 : assert(nTileIter == nReqTiles);
997 :
998 8 : return true;
999 : }
1000 :
1001 : /************************************************************************/
1002 : /* ZarrArray::IRead() */
1003 : /************************************************************************/
1004 :
1005 1572 : bool ZarrArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
1006 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1007 : const GDALExtendedDataType &bufferDataType,
1008 : void *pDstBuffer) const
1009 : {
1010 1572 : if (!CheckValidAndErrorOutIfNot())
1011 0 : return false;
1012 :
1013 1572 : if (!AllocateWorkingBuffers())
1014 3 : return false;
1015 :
1016 : // Need to be kept in top-level scope
1017 3138 : std::vector<GUInt64> arrayStartIdxMod;
1018 3138 : std::vector<GInt64> arrayStepMod;
1019 3138 : std::vector<GPtrDiff_t> bufferStrideMod;
1020 :
1021 1569 : const size_t nDims = m_aoDims.size();
1022 1569 : bool negativeStep = false;
1023 4322 : for (size_t i = 0; i < nDims; ++i)
1024 : {
1025 2955 : if (arrayStep[i] < 0)
1026 : {
1027 202 : negativeStep = true;
1028 202 : break;
1029 : }
1030 : }
1031 :
1032 : // const auto eBufferDT = bufferDataType.GetNumericDataType();
1033 1569 : const auto nBufferDTSize = static_cast<int>(bufferDataType.GetSize());
1034 :
1035 : // Make sure that arrayStep[i] are positive for sake of simplicity
1036 1569 : if (negativeStep)
1037 : {
1038 : #if defined(__GNUC__)
1039 : #pragma GCC diagnostic push
1040 : #pragma GCC diagnostic ignored "-Wnull-dereference"
1041 : #endif
1042 202 : arrayStartIdxMod.resize(nDims);
1043 202 : arrayStepMod.resize(nDims);
1044 202 : bufferStrideMod.resize(nDims);
1045 : #if defined(__GNUC__)
1046 : #pragma GCC diagnostic pop
1047 : #endif
1048 606 : for (size_t i = 0; i < nDims; ++i)
1049 : {
1050 404 : if (arrayStep[i] < 0)
1051 : {
1052 808 : arrayStartIdxMod[i] =
1053 404 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
1054 404 : arrayStepMod[i] = -arrayStep[i];
1055 404 : bufferStrideMod[i] = -bufferStride[i];
1056 404 : pDstBuffer =
1057 : static_cast<GByte *>(pDstBuffer) +
1058 404 : bufferStride[i] *
1059 404 : static_cast<GPtrDiff_t>(nBufferDTSize * (count[i] - 1));
1060 : }
1061 : else
1062 : {
1063 0 : arrayStartIdxMod[i] = arrayStartIdx[i];
1064 0 : arrayStepMod[i] = arrayStep[i];
1065 0 : bufferStrideMod[i] = bufferStride[i];
1066 : }
1067 : }
1068 202 : arrayStartIdx = arrayStartIdxMod.data();
1069 202 : arrayStep = arrayStepMod.data();
1070 202 : bufferStride = bufferStrideMod.data();
1071 : }
1072 :
1073 3138 : std::vector<uint64_t> indicesOuterLoop(nDims + 1);
1074 3138 : std::vector<GByte *> dstPtrStackOuterLoop(nDims + 1);
1075 :
1076 3138 : std::vector<uint64_t> indicesInnerLoop(nDims + 1);
1077 3138 : std::vector<GByte *> dstPtrStackInnerLoop(nDims + 1);
1078 :
1079 3138 : std::vector<GPtrDiff_t> dstBufferStrideBytes;
1080 4726 : for (size_t i = 0; i < nDims; ++i)
1081 : {
1082 3157 : dstBufferStrideBytes.push_back(bufferStride[i] *
1083 3157 : static_cast<GPtrDiff_t>(nBufferDTSize));
1084 : }
1085 1569 : dstBufferStrideBytes.push_back(0);
1086 :
1087 1569 : const auto nDTSize = m_oType.GetSize();
1088 :
1089 3138 : std::vector<uint64_t> tileIndices(nDims);
1090 : const size_t nSourceSize =
1091 1569 : m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
1092 :
1093 3138 : std::vector<size_t> countInnerLoopInit(nDims + 1, 1);
1094 3138 : std::vector<size_t> countInnerLoop(nDims);
1095 :
1096 3121 : const bool bBothAreNumericDT = m_oType.GetClass() == GEDTC_NUMERIC &&
1097 1552 : bufferDataType.GetClass() == GEDTC_NUMERIC;
1098 : const bool bSameNumericDT =
1099 3121 : bBothAreNumericDT &&
1100 1552 : m_oType.GetNumericDataType() == bufferDataType.GetNumericDataType();
1101 1569 : const auto nSameDTSize = bSameNumericDT ? m_oType.GetSize() : 0;
1102 : const bool bSameCompoundAndNoDynamicMem =
1103 1572 : m_oType.GetClass() == GEDTC_COMPOUND && m_oType == bufferDataType &&
1104 3 : !m_oType.NeedsFreeDynamicMemory();
1105 3138 : std::vector<GByte> abyTargetNoData;
1106 1569 : bool bNoDataIsZero = false;
1107 :
1108 1569 : size_t dimIdx = 0;
1109 1569 : dstPtrStackOuterLoop[0] = static_cast<GByte *>(pDstBuffer);
1110 29798 : lbl_next_depth:
1111 29798 : if (dimIdx == nDims)
1112 : {
1113 25292 : size_t dimIdxSubLoop = 0;
1114 25292 : dstPtrStackInnerLoop[0] = dstPtrStackOuterLoop[nDims];
1115 25292 : bool bEmptyTile = false;
1116 :
1117 25292 : const GByte *pabySrcTile = m_abyDecodedTileData.empty()
1118 24300 : ? m_abyRawTileData.data()
1119 25292 : : m_abyDecodedTileData.data();
1120 25292 : bool bMatchFoundInMapTileIndexToCachedTile = false;
1121 :
1122 : // Use cache built by IAdviseRead() if possible
1123 25292 : if (!m_oMapTileIndexToCachedTile.empty())
1124 : {
1125 21352 : uint64_t nTileIdx = 0;
1126 64056 : for (size_t j = 0; j < nDims; ++j)
1127 : {
1128 42704 : if (j > 0)
1129 21352 : nTileIdx *= m_aoDims[j - 1]->GetSize();
1130 42704 : nTileIdx += tileIndices[j];
1131 : }
1132 21352 : const auto oIter = m_oMapTileIndexToCachedTile.find(nTileIdx);
1133 21352 : if (oIter != m_oMapTileIndexToCachedTile.end())
1134 : {
1135 21344 : bMatchFoundInMapTileIndexToCachedTile = true;
1136 21344 : if (oIter->second.abyDecoded.empty())
1137 : {
1138 4 : bEmptyTile = true;
1139 : }
1140 : else
1141 : {
1142 21340 : pabySrcTile = oIter->second.abyDecoded.data();
1143 : }
1144 : }
1145 : else
1146 : {
1147 8 : CPLDebugOnly(ZARR_DEBUG_KEY,
1148 : "Cache miss for tile " CPL_FRMT_GUIB,
1149 : static_cast<GUIntBig>(nTileIdx));
1150 : }
1151 : }
1152 :
1153 25292 : if (!bMatchFoundInMapTileIndexToCachedTile)
1154 : {
1155 3948 : if (!tileIndices.empty() && tileIndices == m_anCachedTiledIndices)
1156 : {
1157 221 : if (!m_bCachedTiledValid)
1158 0 : return false;
1159 221 : bEmptyTile = m_bCachedTiledEmpty;
1160 : }
1161 : else
1162 : {
1163 3727 : if (!FlushDirtyTile())
1164 0 : return false;
1165 :
1166 3727 : m_anCachedTiledIndices = tileIndices;
1167 3727 : m_bCachedTiledValid =
1168 3727 : LoadTileData(tileIndices.data(), bEmptyTile);
1169 3727 : if (!m_bCachedTiledValid)
1170 : {
1171 0 : return false;
1172 : }
1173 3727 : m_bCachedTiledEmpty = bEmptyTile;
1174 : }
1175 :
1176 7896 : pabySrcTile = m_abyDecodedTileData.empty()
1177 2956 : ? m_abyRawTileData.data()
1178 992 : : m_abyDecodedTileData.data();
1179 : }
1180 : const size_t nSrcDTSize =
1181 25292 : m_abyDecodedTileData.empty() ? nSourceSize : nDTSize;
1182 :
1183 75998 : for (size_t i = 0; i < nDims; ++i)
1184 : {
1185 50706 : countInnerLoopInit[i] = 1;
1186 50706 : if (arrayStep[i] != 0)
1187 : {
1188 : const auto nextBlockIdx =
1189 50004 : std::min((1 + indicesOuterLoop[i] / m_anBlockSize[i]) *
1190 50004 : m_anBlockSize[i],
1191 100008 : arrayStartIdx[i] + count[i] * arrayStep[i]);
1192 50004 : countInnerLoopInit[i] = static_cast<size_t>(
1193 50004 : (nextBlockIdx - indicesOuterLoop[i] + arrayStep[i] - 1) /
1194 50004 : arrayStep[i]);
1195 : }
1196 : }
1197 :
1198 25292 : if (bEmptyTile && bBothAreNumericDT && abyTargetNoData.empty())
1199 : {
1200 602 : abyTargetNoData.resize(nBufferDTSize);
1201 602 : if (m_pabyNoData)
1202 : {
1203 156 : GDALExtendedDataType::CopyValue(
1204 156 : m_pabyNoData, m_oType, &abyTargetNoData[0], bufferDataType);
1205 156 : bNoDataIsZero = true;
1206 1308 : for (size_t i = 0; i < abyTargetNoData.size(); ++i)
1207 : {
1208 1152 : if (abyTargetNoData[i] != 0)
1209 334 : bNoDataIsZero = false;
1210 : }
1211 : }
1212 : else
1213 : {
1214 446 : bNoDataIsZero = true;
1215 446 : GByte zero = 0;
1216 446 : GDALCopyWords(&zero, GDT_Byte, 0, &abyTargetNoData[0],
1217 : bufferDataType.GetNumericDataType(), 0, 1);
1218 : }
1219 : }
1220 :
1221 24690 : lbl_next_depth_inner_loop:
1222 467583 : if (nDims == 0 || dimIdxSubLoop == nDims - 1)
1223 : {
1224 442167 : indicesInnerLoop[dimIdxSubLoop] = indicesOuterLoop[dimIdxSubLoop];
1225 442167 : void *dst_ptr = dstPtrStackInnerLoop[dimIdxSubLoop];
1226 :
1227 439680 : if (m_bUseOptimizedCodePaths && bEmptyTile && bBothAreNumericDT &&
1228 881847 : bNoDataIsZero &&
1229 1134 : nBufferDTSize == dstBufferStrideBytes[dimIdxSubLoop])
1230 : {
1231 1068 : memset(dst_ptr, 0,
1232 1068 : nBufferDTSize * countInnerLoopInit[dimIdxSubLoop]);
1233 1068 : goto end_inner_loop;
1234 : }
1235 438612 : else if (m_bUseOptimizedCodePaths && bEmptyTile &&
1236 880086 : !abyTargetNoData.empty() && bBothAreNumericDT &&
1237 375 : dstBufferStrideBytes[dimIdxSubLoop] <
1238 375 : std::numeric_limits<int>::max())
1239 : {
1240 750 : GDALCopyWords64(
1241 375 : abyTargetNoData.data(), bufferDataType.GetNumericDataType(),
1242 : 0, dst_ptr, bufferDataType.GetNumericDataType(),
1243 375 : static_cast<int>(dstBufferStrideBytes[dimIdxSubLoop]),
1244 375 : static_cast<GPtrDiff_t>(countInnerLoopInit[dimIdxSubLoop]));
1245 375 : goto end_inner_loop;
1246 : }
1247 440724 : else if (bEmptyTile)
1248 : {
1249 3875 : for (size_t i = 0; i < countInnerLoopInit[dimIdxSubLoop];
1250 2528 : ++i, dst_ptr = static_cast<uint8_t *>(dst_ptr) +
1251 2528 : dstBufferStrideBytes[dimIdxSubLoop])
1252 : {
1253 2528 : if (bNoDataIsZero)
1254 : {
1255 1930 : if (nBufferDTSize == 1)
1256 : {
1257 36 : *static_cast<uint8_t *>(dst_ptr) = 0;
1258 : }
1259 1894 : else if (nBufferDTSize == 2)
1260 : {
1261 48 : *static_cast<uint16_t *>(dst_ptr) = 0;
1262 : }
1263 1846 : else if (nBufferDTSize == 4)
1264 : {
1265 72 : *static_cast<uint32_t *>(dst_ptr) = 0;
1266 : }
1267 1774 : else if (nBufferDTSize == 8)
1268 : {
1269 1534 : *static_cast<uint64_t *>(dst_ptr) = 0;
1270 : }
1271 240 : else if (nBufferDTSize == 16)
1272 : {
1273 240 : static_cast<uint64_t *>(dst_ptr)[0] = 0;
1274 240 : static_cast<uint64_t *>(dst_ptr)[1] = 0;
1275 : }
1276 : else
1277 : {
1278 0 : CPLAssert(false);
1279 : }
1280 : }
1281 598 : else if (m_pabyNoData)
1282 : {
1283 597 : if (bBothAreNumericDT)
1284 : {
1285 588 : const void *src_ptr_v = abyTargetNoData.data();
1286 588 : if (nBufferDTSize == 1)
1287 24 : *static_cast<uint8_t *>(dst_ptr) =
1288 24 : *static_cast<const uint8_t *>(src_ptr_v);
1289 564 : else if (nBufferDTSize == 2)
1290 0 : *static_cast<uint16_t *>(dst_ptr) =
1291 0 : *static_cast<const uint16_t *>(src_ptr_v);
1292 564 : else if (nBufferDTSize == 4)
1293 60 : *static_cast<uint32_t *>(dst_ptr) =
1294 60 : *static_cast<const uint32_t *>(src_ptr_v);
1295 504 : else if (nBufferDTSize == 8)
1296 504 : *static_cast<uint64_t *>(dst_ptr) =
1297 504 : *static_cast<const uint64_t *>(src_ptr_v);
1298 0 : else if (nBufferDTSize == 16)
1299 : {
1300 0 : static_cast<uint64_t *>(dst_ptr)[0] =
1301 0 : static_cast<const uint64_t *>(src_ptr_v)[0];
1302 0 : static_cast<uint64_t *>(dst_ptr)[1] =
1303 0 : static_cast<const uint64_t *>(src_ptr_v)[1];
1304 : }
1305 : else
1306 : {
1307 0 : CPLAssert(false);
1308 : }
1309 : }
1310 : else
1311 : {
1312 9 : GDALExtendedDataType::CopyValue(
1313 9 : m_pabyNoData, m_oType, dst_ptr, bufferDataType);
1314 : }
1315 : }
1316 : else
1317 : {
1318 1 : memset(dst_ptr, 0, nBufferDTSize);
1319 : }
1320 : }
1321 :
1322 1347 : goto end_inner_loop;
1323 : }
1324 :
1325 439377 : size_t nOffset = 0;
1326 1330560 : for (size_t i = 0; i < nDims; i++)
1327 : {
1328 891188 : nOffset = static_cast<size_t>(
1329 891188 : nOffset * m_anBlockSize[i] +
1330 1782380 : (indicesInnerLoop[i] - tileIndices[i] * m_anBlockSize[i]));
1331 : }
1332 439377 : const GByte *src_ptr = pabySrcTile + nOffset * nSrcDTSize;
1333 439377 : const auto step = nDims == 0 ? 0 : arrayStep[dimIdxSubLoop];
1334 :
1335 438227 : if (m_bUseOptimizedCodePaths && bBothAreNumericDT &&
1336 438203 : step <= static_cast<GIntBig>(std::numeric_limits<int>::max() /
1337 877604 : nDTSize) &&
1338 438203 : dstBufferStrideBytes[dimIdxSubLoop] <=
1339 438203 : std::numeric_limits<int>::max())
1340 : {
1341 438203 : GDALCopyWords64(
1342 : src_ptr, m_oType.GetNumericDataType(),
1343 : static_cast<int>(step * nDTSize), dst_ptr,
1344 : bufferDataType.GetNumericDataType(),
1345 438203 : static_cast<int>(dstBufferStrideBytes[dimIdxSubLoop]),
1346 438203 : static_cast<GPtrDiff_t>(countInnerLoopInit[dimIdxSubLoop]));
1347 :
1348 438203 : goto end_inner_loop;
1349 : }
1350 :
1351 3391 : for (size_t i = 0; i < countInnerLoopInit[dimIdxSubLoop];
1352 2217 : ++i, src_ptr += step * nSrcDTSize,
1353 2217 : dst_ptr = static_cast<uint8_t *>(dst_ptr) +
1354 2217 : dstBufferStrideBytes[dimIdxSubLoop])
1355 : {
1356 2217 : if (bSameNumericDT)
1357 : {
1358 814 : const void *src_ptr_v = src_ptr;
1359 814 : if (nSameDTSize == 1)
1360 70 : *static_cast<uint8_t *>(dst_ptr) =
1361 70 : *static_cast<const uint8_t *>(src_ptr_v);
1362 744 : else if (nSameDTSize == 2)
1363 : {
1364 56 : *static_cast<uint16_t *>(dst_ptr) =
1365 56 : *static_cast<const uint16_t *>(src_ptr_v);
1366 : }
1367 688 : else if (nSameDTSize == 4)
1368 : {
1369 154 : *static_cast<uint32_t *>(dst_ptr) =
1370 154 : *static_cast<const uint32_t *>(src_ptr_v);
1371 : }
1372 534 : else if (nSameDTSize == 8)
1373 : {
1374 482 : *static_cast<uint64_t *>(dst_ptr) =
1375 482 : *static_cast<const uint64_t *>(src_ptr_v);
1376 : }
1377 52 : else if (nSameDTSize == 16)
1378 : {
1379 52 : static_cast<uint64_t *>(dst_ptr)[0] =
1380 52 : static_cast<const uint64_t *>(src_ptr_v)[0];
1381 52 : static_cast<uint64_t *>(dst_ptr)[1] =
1382 52 : static_cast<const uint64_t *>(src_ptr_v)[1];
1383 : }
1384 : else
1385 : {
1386 0 : CPLAssert(false);
1387 : }
1388 : }
1389 1403 : else if (bSameCompoundAndNoDynamicMem)
1390 : {
1391 4 : memcpy(dst_ptr, src_ptr, nDTSize);
1392 : }
1393 1399 : else if (m_oType.GetClass() == GEDTC_STRING)
1394 : {
1395 26 : if (m_aoDtypeElts.back().nativeType ==
1396 : DtypeElt::NativeType::STRING_UNICODE)
1397 : {
1398 : char *pDstStr =
1399 12 : UCS4ToUTF8(src_ptr, nSourceSize,
1400 6 : m_aoDtypeElts.back().needByteSwapping);
1401 6 : char **pDstPtr = static_cast<char **>(dst_ptr);
1402 6 : memcpy(pDstPtr, &pDstStr, sizeof(pDstStr));
1403 : }
1404 : else
1405 : {
1406 : char *pDstStr =
1407 20 : static_cast<char *>(CPLMalloc(nSourceSize + 1));
1408 20 : memcpy(pDstStr, src_ptr, nSourceSize);
1409 20 : pDstStr[nSourceSize] = 0;
1410 20 : char **pDstPtr = static_cast<char **>(dst_ptr);
1411 20 : memcpy(pDstPtr, &pDstStr, sizeof(char *));
1412 : }
1413 : }
1414 : else
1415 : {
1416 1373 : GDALExtendedDataType::CopyValue(src_ptr, m_oType, dst_ptr,
1417 : bufferDataType);
1418 : }
1419 1174 : }
1420 : }
1421 : else
1422 : {
1423 : // This level of loop loops over individual samples, within a
1424 : // block
1425 25416 : indicesInnerLoop[dimIdxSubLoop] = indicesOuterLoop[dimIdxSubLoop];
1426 25416 : countInnerLoop[dimIdxSubLoop] = countInnerLoopInit[dimIdxSubLoop];
1427 : while (true)
1428 : {
1429 442291 : dimIdxSubLoop++;
1430 442291 : dstPtrStackInnerLoop[dimIdxSubLoop] =
1431 442291 : dstPtrStackInnerLoop[dimIdxSubLoop - 1];
1432 442291 : goto lbl_next_depth_inner_loop;
1433 442291 : lbl_return_to_caller_inner_loop:
1434 442291 : dimIdxSubLoop--;
1435 442291 : --countInnerLoop[dimIdxSubLoop];
1436 442291 : if (countInnerLoop[dimIdxSubLoop] == 0)
1437 : {
1438 25416 : break;
1439 : }
1440 416875 : indicesInnerLoop[dimIdxSubLoop] += arrayStep[dimIdxSubLoop];
1441 416875 : dstPtrStackInnerLoop[dimIdxSubLoop] +=
1442 416875 : dstBufferStrideBytes[dimIdxSubLoop];
1443 : }
1444 : }
1445 467583 : end_inner_loop:
1446 467583 : if (dimIdxSubLoop > 0)
1447 442291 : goto lbl_return_to_caller_inner_loop;
1448 : }
1449 : else
1450 : {
1451 : // This level of loop loops over blocks
1452 4506 : indicesOuterLoop[dimIdx] = arrayStartIdx[dimIdx];
1453 4506 : tileIndices[dimIdx] = indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1454 : while (true)
1455 : {
1456 28229 : dimIdx++;
1457 28229 : dstPtrStackOuterLoop[dimIdx] = dstPtrStackOuterLoop[dimIdx - 1];
1458 28229 : goto lbl_next_depth;
1459 28229 : lbl_return_to_caller:
1460 28229 : dimIdx--;
1461 28229 : if (count[dimIdx] == 1 || arrayStep[dimIdx] == 0)
1462 : break;
1463 :
1464 : size_t nIncr;
1465 27208 : if (static_cast<GUInt64>(arrayStep[dimIdx]) < m_anBlockSize[dimIdx])
1466 : {
1467 : // Compute index at next block boundary
1468 : auto newIdx =
1469 26800 : indicesOuterLoop[dimIdx] +
1470 26800 : (m_anBlockSize[dimIdx] -
1471 26800 : (indicesOuterLoop[dimIdx] % m_anBlockSize[dimIdx]));
1472 : // And round up compared to arrayStartIdx, arrayStep
1473 26800 : nIncr = static_cast<size_t>((newIdx - indicesOuterLoop[dimIdx] +
1474 26800 : arrayStep[dimIdx] - 1) /
1475 26800 : arrayStep[dimIdx]);
1476 : }
1477 : else
1478 : {
1479 408 : nIncr = 1;
1480 : }
1481 27208 : indicesOuterLoop[dimIdx] += nIncr * arrayStep[dimIdx];
1482 27208 : if (indicesOuterLoop[dimIdx] >
1483 27208 : arrayStartIdx[dimIdx] + (count[dimIdx] - 1) * arrayStep[dimIdx])
1484 3485 : break;
1485 23723 : dstPtrStackOuterLoop[dimIdx] +=
1486 23723 : bufferStride[dimIdx] *
1487 23723 : static_cast<GPtrDiff_t>(nIncr * nBufferDTSize);
1488 47446 : tileIndices[dimIdx] =
1489 23723 : indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1490 23723 : }
1491 : }
1492 29798 : if (dimIdx > 0)
1493 28229 : goto lbl_return_to_caller;
1494 :
1495 1569 : return true;
1496 : }
1497 :
1498 : /************************************************************************/
1499 : /* ZarrArray::IRead() */
1500 : /************************************************************************/
1501 :
1502 478 : bool ZarrArray::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
1503 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
1504 : const GDALExtendedDataType &bufferDataType,
1505 : const void *pSrcBuffer)
1506 : {
1507 478 : if (!CheckValidAndErrorOutIfNot())
1508 0 : return false;
1509 :
1510 478 : if (!AllocateWorkingBuffers())
1511 0 : return false;
1512 :
1513 478 : m_oMapTileIndexToCachedTile.clear();
1514 :
1515 : // Need to be kept in top-level scope
1516 956 : std::vector<GUInt64> arrayStartIdxMod;
1517 956 : std::vector<GInt64> arrayStepMod;
1518 956 : std::vector<GPtrDiff_t> bufferStrideMod;
1519 :
1520 478 : const size_t nDims = m_aoDims.size();
1521 478 : bool negativeStep = false;
1522 478 : bool bWriteWholeTileInit = true;
1523 1296 : for (size_t i = 0; i < nDims; ++i)
1524 : {
1525 818 : if (arrayStep[i] < 0)
1526 : {
1527 132 : negativeStep = true;
1528 132 : if (arrayStep[i] != -1 && count[i] > 1)
1529 0 : bWriteWholeTileInit = false;
1530 : }
1531 686 : else if (arrayStep[i] != 1 && count[i] > 1)
1532 0 : bWriteWholeTileInit = false;
1533 : }
1534 :
1535 478 : const auto nBufferDTSize = static_cast<int>(bufferDataType.GetSize());
1536 :
1537 : // Make sure that arrayStep[i] are positive for sake of simplicity
1538 478 : if (negativeStep)
1539 : {
1540 : #if defined(__GNUC__)
1541 : #pragma GCC diagnostic push
1542 : #pragma GCC diagnostic ignored "-Wnull-dereference"
1543 : #endif
1544 66 : arrayStartIdxMod.resize(nDims);
1545 66 : arrayStepMod.resize(nDims);
1546 66 : bufferStrideMod.resize(nDims);
1547 : #if defined(__GNUC__)
1548 : #pragma GCC diagnostic pop
1549 : #endif
1550 198 : for (size_t i = 0; i < nDims; ++i)
1551 : {
1552 132 : if (arrayStep[i] < 0)
1553 : {
1554 264 : arrayStartIdxMod[i] =
1555 132 : arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
1556 132 : arrayStepMod[i] = -arrayStep[i];
1557 132 : bufferStrideMod[i] = -bufferStride[i];
1558 132 : pSrcBuffer =
1559 : static_cast<const GByte *>(pSrcBuffer) +
1560 132 : bufferStride[i] *
1561 132 : static_cast<GPtrDiff_t>(nBufferDTSize * (count[i] - 1));
1562 : }
1563 : else
1564 : {
1565 0 : arrayStartIdxMod[i] = arrayStartIdx[i];
1566 0 : arrayStepMod[i] = arrayStep[i];
1567 0 : bufferStrideMod[i] = bufferStride[i];
1568 : }
1569 : }
1570 66 : arrayStartIdx = arrayStartIdxMod.data();
1571 66 : arrayStep = arrayStepMod.data();
1572 66 : bufferStride = bufferStrideMod.data();
1573 : }
1574 :
1575 956 : std::vector<uint64_t> indicesOuterLoop(nDims + 1);
1576 956 : std::vector<const GByte *> srcPtrStackOuterLoop(nDims + 1);
1577 :
1578 956 : std::vector<size_t> offsetDstBuffer(nDims + 1);
1579 956 : std::vector<const GByte *> srcPtrStackInnerLoop(nDims + 1);
1580 :
1581 956 : std::vector<GPtrDiff_t> srcBufferStrideBytes;
1582 1296 : for (size_t i = 0; i < nDims; ++i)
1583 : {
1584 818 : srcBufferStrideBytes.push_back(bufferStride[i] *
1585 818 : static_cast<GPtrDiff_t>(nBufferDTSize));
1586 : }
1587 478 : srcBufferStrideBytes.push_back(0);
1588 :
1589 478 : const auto nDTSize = m_oType.GetSize();
1590 :
1591 956 : std::vector<uint64_t> tileIndices(nDims);
1592 : const size_t nNativeSize =
1593 478 : m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
1594 :
1595 956 : std::vector<size_t> countInnerLoopInit(nDims + 1, 1);
1596 956 : std::vector<size_t> countInnerLoop(nDims);
1597 :
1598 952 : const bool bBothAreNumericDT = m_oType.GetClass() == GEDTC_NUMERIC &&
1599 474 : bufferDataType.GetClass() == GEDTC_NUMERIC;
1600 : const bool bSameNumericDT =
1601 952 : bBothAreNumericDT &&
1602 474 : m_oType.GetNumericDataType() == bufferDataType.GetNumericDataType();
1603 478 : const auto nSameDTSize = bSameNumericDT ? m_oType.GetSize() : 0;
1604 : const bool bSameCompoundAndNoDynamicMem =
1605 478 : m_oType.GetClass() == GEDTC_COMPOUND && m_oType == bufferDataType &&
1606 0 : !m_oType.NeedsFreeDynamicMemory();
1607 :
1608 478 : size_t dimIdx = 0;
1609 478 : size_t dimIdxForCopy = nDims == 0 ? 0 : nDims - 1;
1610 478 : if (nDims)
1611 : {
1612 495 : while (dimIdxForCopy > 0 && count[dimIdxForCopy] == 1)
1613 17 : --dimIdxForCopy;
1614 : }
1615 :
1616 478 : srcPtrStackOuterLoop[0] = static_cast<const GByte *>(pSrcBuffer);
1617 24556 : lbl_next_depth:
1618 24556 : if (dimIdx == nDims)
1619 : {
1620 22804 : bool bWriteWholeTile = bWriteWholeTileInit;
1621 22804 : bool bPartialTile = false;
1622 68535 : for (size_t i = 0; i < nDims; ++i)
1623 : {
1624 45731 : countInnerLoopInit[i] = 1;
1625 45731 : if (arrayStep[i] != 0)
1626 : {
1627 : const auto nextBlockIdx =
1628 45448 : std::min((1 + indicesOuterLoop[i] / m_anBlockSize[i]) *
1629 45448 : m_anBlockSize[i],
1630 90896 : arrayStartIdx[i] + count[i] * arrayStep[i]);
1631 45448 : countInnerLoopInit[i] = static_cast<size_t>(
1632 45448 : (nextBlockIdx - indicesOuterLoop[i] + arrayStep[i] - 1) /
1633 45448 : arrayStep[i]);
1634 : }
1635 45731 : if (bWriteWholeTile)
1636 : {
1637 : const bool bWholePartialTileThisDim =
1638 46547 : indicesOuterLoop[i] == 0 &&
1639 1763 : countInnerLoopInit[i] == m_aoDims[i]->GetSize();
1640 44784 : bWriteWholeTile = (countInnerLoopInit[i] == m_anBlockSize[i] ||
1641 : bWholePartialTileThisDim);
1642 44784 : if (bWholePartialTileThisDim)
1643 : {
1644 517 : bPartialTile = true;
1645 : }
1646 : }
1647 : }
1648 :
1649 22804 : size_t dimIdxSubLoop = 0;
1650 22804 : srcPtrStackInnerLoop[0] = srcPtrStackOuterLoop[nDims];
1651 : const size_t nCacheDTSize =
1652 22804 : m_abyDecodedTileData.empty() ? nNativeSize : nDTSize;
1653 22804 : auto &abyTile = m_abyDecodedTileData.empty() ? m_abyRawTileData
1654 22804 : : m_abyDecodedTileData;
1655 :
1656 22804 : if (!tileIndices.empty() && tileIndices == m_anCachedTiledIndices)
1657 : {
1658 3 : if (!m_bCachedTiledValid)
1659 0 : return false;
1660 : }
1661 : else
1662 : {
1663 22801 : if (!FlushDirtyTile())
1664 0 : return false;
1665 :
1666 22801 : m_anCachedTiledIndices = tileIndices;
1667 22801 : m_bCachedTiledValid = true;
1668 :
1669 22801 : if (bWriteWholeTile)
1670 : {
1671 21278 : if (bPartialTile)
1672 : {
1673 274 : DeallocateDecodedTileData();
1674 274 : memset(&abyTile[0], 0, abyTile.size());
1675 : }
1676 : }
1677 : else
1678 : {
1679 : // If we don't write the whole tile, we need to fetch a
1680 : // potentially existing one.
1681 1523 : bool bEmptyTile = false;
1682 1523 : m_bCachedTiledValid =
1683 1523 : LoadTileData(tileIndices.data(), bEmptyTile);
1684 1523 : if (!m_bCachedTiledValid)
1685 : {
1686 0 : return false;
1687 : }
1688 :
1689 1523 : if (bEmptyTile)
1690 : {
1691 1215 : DeallocateDecodedTileData();
1692 :
1693 1215 : if (m_pabyNoData == nullptr)
1694 : {
1695 495 : memset(&abyTile[0], 0, abyTile.size());
1696 : }
1697 : else
1698 : {
1699 720 : const size_t nElts = abyTile.size() / nCacheDTSize;
1700 720 : GByte *dstPtr = &abyTile[0];
1701 720 : if (m_oType.GetClass() == GEDTC_NUMERIC)
1702 : {
1703 720 : GDALCopyWords64(
1704 720 : m_pabyNoData, m_oType.GetNumericDataType(), 0,
1705 : dstPtr, m_oType.GetNumericDataType(),
1706 720 : static_cast<int>(m_oType.GetSize()),
1707 : static_cast<GPtrDiff_t>(nElts));
1708 : }
1709 : else
1710 : {
1711 0 : for (size_t i = 0; i < nElts; ++i)
1712 : {
1713 0 : GDALExtendedDataType::CopyValue(
1714 0 : m_pabyNoData, m_oType, dstPtr, m_oType);
1715 0 : dstPtr += nCacheDTSize;
1716 : }
1717 : }
1718 : }
1719 : }
1720 : }
1721 : }
1722 22804 : m_bDirtyTile = true;
1723 22804 : m_bCachedTiledEmpty = false;
1724 22804 : if (nDims)
1725 22804 : offsetDstBuffer[0] = static_cast<size_t>(
1726 22804 : indicesOuterLoop[0] - tileIndices[0] * m_anBlockSize[0]);
1727 :
1728 22804 : GByte *pabyTile = &abyTile[0];
1729 :
1730 453210 : lbl_next_depth_inner_loop:
1731 453210 : if (dimIdxSubLoop == dimIdxForCopy)
1732 : {
1733 430457 : size_t nOffset = offsetDstBuffer[dimIdxSubLoop];
1734 430457 : GInt64 step = nDims == 0 ? 0 : arrayStep[dimIdxSubLoop];
1735 430631 : for (size_t i = dimIdxSubLoop + 1; i < nDims; ++i)
1736 : {
1737 174 : nOffset = static_cast<size_t>(
1738 174 : nOffset * m_anBlockSize[i] +
1739 348 : (indicesOuterLoop[i] - tileIndices[i] * m_anBlockSize[i]));
1740 174 : step *= m_anBlockSize[i];
1741 : }
1742 430457 : const void *src_ptr = srcPtrStackInnerLoop[dimIdxSubLoop];
1743 430457 : GByte *dst_ptr = pabyTile + nOffset * nCacheDTSize;
1744 :
1745 430457 : if (m_bUseOptimizedCodePaths && bBothAreNumericDT)
1746 : {
1747 429727 : if (countInnerLoopInit[dimIdxSubLoop] == 1 && bSameNumericDT)
1748 : {
1749 84 : void *dst_ptr_v = dst_ptr;
1750 84 : if (nSameDTSize == 1)
1751 6 : *static_cast<uint8_t *>(dst_ptr_v) =
1752 6 : *static_cast<const uint8_t *>(src_ptr);
1753 78 : else if (nSameDTSize == 2)
1754 : {
1755 2 : *static_cast<uint16_t *>(dst_ptr_v) =
1756 2 : *static_cast<const uint16_t *>(src_ptr);
1757 : }
1758 76 : else if (nSameDTSize == 4)
1759 : {
1760 2 : *static_cast<uint32_t *>(dst_ptr_v) =
1761 2 : *static_cast<const uint32_t *>(src_ptr);
1762 : }
1763 74 : else if (nSameDTSize == 8)
1764 : {
1765 52 : *static_cast<uint64_t *>(dst_ptr_v) =
1766 52 : *static_cast<const uint64_t *>(src_ptr);
1767 : }
1768 22 : else if (nSameDTSize == 16)
1769 : {
1770 22 : static_cast<uint64_t *>(dst_ptr_v)[0] =
1771 22 : static_cast<const uint64_t *>(src_ptr)[0];
1772 22 : static_cast<uint64_t *>(dst_ptr_v)[1] =
1773 22 : static_cast<const uint64_t *>(src_ptr)[1];
1774 : }
1775 : else
1776 : {
1777 0 : CPLAssert(false);
1778 : }
1779 : }
1780 429643 : else if (step <=
1781 429643 : static_cast<GIntBig>(
1782 859286 : std::numeric_limits<int>::max() / nDTSize) &&
1783 429643 : srcBufferStrideBytes[dimIdxSubLoop] <=
1784 429643 : std::numeric_limits<int>::max())
1785 : {
1786 859286 : GDALCopyWords64(
1787 : src_ptr, bufferDataType.GetNumericDataType(),
1788 429643 : static_cast<int>(srcBufferStrideBytes[dimIdxSubLoop]),
1789 : dst_ptr, m_oType.GetNumericDataType(),
1790 : static_cast<int>(step * nDTSize),
1791 : static_cast<GPtrDiff_t>(
1792 429643 : countInnerLoopInit[dimIdxSubLoop]));
1793 : }
1794 429727 : goto end_inner_loop;
1795 : }
1796 :
1797 2188 : for (size_t i = 0; i < countInnerLoopInit[dimIdxSubLoop];
1798 1458 : ++i, dst_ptr += step * nCacheDTSize,
1799 1458 : src_ptr = static_cast<const uint8_t *>(src_ptr) +
1800 1458 : srcBufferStrideBytes[dimIdxSubLoop])
1801 : {
1802 1458 : if (bSameNumericDT)
1803 : {
1804 300 : void *dst_ptr_v = dst_ptr;
1805 300 : if (nSameDTSize == 1)
1806 0 : *static_cast<uint8_t *>(dst_ptr_v) =
1807 0 : *static_cast<const uint8_t *>(src_ptr);
1808 300 : else if (nSameDTSize == 2)
1809 : {
1810 0 : *static_cast<uint16_t *>(dst_ptr_v) =
1811 0 : *static_cast<const uint16_t *>(src_ptr);
1812 : }
1813 300 : else if (nSameDTSize == 4)
1814 : {
1815 0 : *static_cast<uint32_t *>(dst_ptr_v) =
1816 0 : *static_cast<const uint32_t *>(src_ptr);
1817 : }
1818 300 : else if (nSameDTSize == 8)
1819 : {
1820 220 : *static_cast<uint64_t *>(dst_ptr_v) =
1821 220 : *static_cast<const uint64_t *>(src_ptr);
1822 : }
1823 80 : else if (nSameDTSize == 16)
1824 : {
1825 80 : static_cast<uint64_t *>(dst_ptr_v)[0] =
1826 80 : static_cast<const uint64_t *>(src_ptr)[0];
1827 80 : static_cast<uint64_t *>(dst_ptr_v)[1] =
1828 80 : static_cast<const uint64_t *>(src_ptr)[1];
1829 : }
1830 : else
1831 : {
1832 0 : CPLAssert(false);
1833 : }
1834 : }
1835 1158 : else if (bSameCompoundAndNoDynamicMem)
1836 : {
1837 0 : memcpy(dst_ptr, src_ptr, nDTSize);
1838 : }
1839 1158 : else if (m_oType.GetClass() == GEDTC_STRING)
1840 : {
1841 6 : const char *pSrcStr =
1842 : *static_cast<const char *const *>(src_ptr);
1843 6 : if (pSrcStr)
1844 : {
1845 6 : const size_t nLen = strlen(pSrcStr);
1846 6 : if (m_aoDtypeElts.back().nativeType ==
1847 : DtypeElt::NativeType::STRING_UNICODE)
1848 : {
1849 : try
1850 : {
1851 : const auto ucs4 = UTF8ToUCS4(
1852 : pSrcStr,
1853 8 : m_aoDtypeElts.back().needByteSwapping);
1854 4 : const auto ucs4Len = ucs4.size();
1855 4 : memcpy(dst_ptr, ucs4.data(),
1856 4 : std::min(ucs4Len, nNativeSize));
1857 4 : if (ucs4Len > nNativeSize)
1858 : {
1859 1 : CPLError(CE_Warning, CPLE_AppDefined,
1860 : "Too long string truncated");
1861 : }
1862 3 : else if (ucs4Len < nNativeSize)
1863 : {
1864 1 : memset(dst_ptr + ucs4Len, 0,
1865 1 : nNativeSize - ucs4Len);
1866 : }
1867 : }
1868 0 : catch (const std::exception &)
1869 : {
1870 0 : memset(dst_ptr, 0, nNativeSize);
1871 : }
1872 : }
1873 : else
1874 : {
1875 2 : memcpy(dst_ptr, pSrcStr,
1876 2 : std::min(nLen, nNativeSize));
1877 2 : if (nLen < nNativeSize)
1878 1 : memset(dst_ptr + nLen, 0, nNativeSize - nLen);
1879 : }
1880 : }
1881 : else
1882 : {
1883 0 : memset(dst_ptr, 0, nNativeSize);
1884 : }
1885 : }
1886 : else
1887 : {
1888 1152 : if (m_oType.NeedsFreeDynamicMemory())
1889 0 : m_oType.FreeDynamicMemory(dst_ptr);
1890 1152 : GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
1891 1152 : dst_ptr, m_oType);
1892 : }
1893 : }
1894 : }
1895 : else
1896 : {
1897 : // This level of loop loops over individual samples, within a
1898 : // block
1899 22753 : countInnerLoop[dimIdxSubLoop] = countInnerLoopInit[dimIdxSubLoop];
1900 : while (true)
1901 : {
1902 430406 : dimIdxSubLoop++;
1903 430406 : srcPtrStackInnerLoop[dimIdxSubLoop] =
1904 430406 : srcPtrStackInnerLoop[dimIdxSubLoop - 1];
1905 860812 : offsetDstBuffer[dimIdxSubLoop] =
1906 1291220 : static_cast<size_t>(offsetDstBuffer[dimIdxSubLoop - 1] *
1907 430406 : m_anBlockSize[dimIdxSubLoop] +
1908 430406 : (indicesOuterLoop[dimIdxSubLoop] -
1909 860812 : tileIndices[dimIdxSubLoop] *
1910 430406 : m_anBlockSize[dimIdxSubLoop]));
1911 430406 : goto lbl_next_depth_inner_loop;
1912 430406 : lbl_return_to_caller_inner_loop:
1913 430406 : dimIdxSubLoop--;
1914 430406 : --countInnerLoop[dimIdxSubLoop];
1915 430406 : if (countInnerLoop[dimIdxSubLoop] == 0)
1916 : {
1917 22753 : break;
1918 : }
1919 407653 : srcPtrStackInnerLoop[dimIdxSubLoop] +=
1920 407653 : srcBufferStrideBytes[dimIdxSubLoop];
1921 407653 : offsetDstBuffer[dimIdxSubLoop] +=
1922 407653 : static_cast<size_t>(arrayStep[dimIdxSubLoop]);
1923 : }
1924 : }
1925 430457 : end_inner_loop:
1926 453210 : if (dimIdxSubLoop > 0)
1927 430406 : goto lbl_return_to_caller_inner_loop;
1928 : }
1929 : else
1930 : {
1931 : // This level of loop loops over blocks
1932 1752 : indicesOuterLoop[dimIdx] = arrayStartIdx[dimIdx];
1933 1752 : tileIndices[dimIdx] = indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1934 : while (true)
1935 : {
1936 24078 : dimIdx++;
1937 24078 : srcPtrStackOuterLoop[dimIdx] = srcPtrStackOuterLoop[dimIdx - 1];
1938 24078 : goto lbl_next_depth;
1939 24078 : lbl_return_to_caller:
1940 24078 : dimIdx--;
1941 24078 : if (count[dimIdx] == 1 || arrayStep[dimIdx] == 0)
1942 : break;
1943 :
1944 : size_t nIncr;
1945 23878 : if (static_cast<GUInt64>(arrayStep[dimIdx]) < m_anBlockSize[dimIdx])
1946 : {
1947 : // Compute index at next block boundary
1948 : auto newIdx =
1949 23686 : indicesOuterLoop[dimIdx] +
1950 23686 : (m_anBlockSize[dimIdx] -
1951 23686 : (indicesOuterLoop[dimIdx] % m_anBlockSize[dimIdx]));
1952 : // And round up compared to arrayStartIdx, arrayStep
1953 23686 : nIncr = static_cast<size_t>((newIdx - indicesOuterLoop[dimIdx] +
1954 23686 : arrayStep[dimIdx] - 1) /
1955 23686 : arrayStep[dimIdx]);
1956 : }
1957 : else
1958 : {
1959 192 : nIncr = 1;
1960 : }
1961 23878 : indicesOuterLoop[dimIdx] += nIncr * arrayStep[dimIdx];
1962 23878 : if (indicesOuterLoop[dimIdx] >
1963 23878 : arrayStartIdx[dimIdx] + (count[dimIdx] - 1) * arrayStep[dimIdx])
1964 1552 : break;
1965 22326 : srcPtrStackOuterLoop[dimIdx] +=
1966 22326 : bufferStride[dimIdx] *
1967 22326 : static_cast<GPtrDiff_t>(nIncr * nBufferDTSize);
1968 44652 : tileIndices[dimIdx] =
1969 22326 : indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1970 22326 : }
1971 : }
1972 24556 : if (dimIdx > 0)
1973 24078 : goto lbl_return_to_caller;
1974 :
1975 478 : return true;
1976 : }
1977 :
1978 : /************************************************************************/
1979 : /* ZarrArray::IsEmptyTile() */
1980 : /************************************************************************/
1981 :
1982 22804 : bool ZarrArray::IsEmptyTile(const ZarrByteVectorQuickResize &abyTile) const
1983 : {
1984 44441 : if (m_pabyNoData == nullptr || (m_oType.GetClass() == GEDTC_NUMERIC &&
1985 21637 : GetNoDataValueAsDouble() == 0.0))
1986 : {
1987 22496 : const size_t nBytes = abyTile.size();
1988 22496 : size_t i = 0;
1989 25745 : for (; i + (sizeof(size_t) - 1) < nBytes; i += sizeof(size_t))
1990 : {
1991 25102 : if (*reinterpret_cast<const size_t *>(abyTile.data() + i) != 0)
1992 : {
1993 21853 : return false;
1994 : }
1995 : }
1996 1572 : for (; i < nBytes; ++i)
1997 : {
1998 990 : if (abyTile[i] != 0)
1999 : {
2000 61 : return false;
2001 : }
2002 : }
2003 582 : return true;
2004 : }
2005 616 : else if (m_oType.GetClass() == GEDTC_NUMERIC &&
2006 308 : !GDALDataTypeIsComplex(m_oType.GetNumericDataType()))
2007 : {
2008 308 : const int nDTSize = static_cast<int>(m_oType.GetSize());
2009 308 : const size_t nElts = abyTile.size() / nDTSize;
2010 308 : const auto eDT = m_oType.GetNumericDataType();
2011 308 : return GDALBufferHasOnlyNoData(abyTile.data(), GetNoDataValueAsDouble(),
2012 : nElts, // nWidth
2013 : 1, // nHeight
2014 : nElts, // nLineStride
2015 : 1, // nComponents
2016 : nDTSize * 8, // nBitsPerSample
2017 308 : GDALDataTypeIsInteger(eDT)
2018 112 : ? (GDALDataTypeIsSigned(eDT)
2019 112 : ? GSF_SIGNED_INT
2020 : : GSF_UNSIGNED_INT)
2021 308 : : GSF_FLOATING_POINT);
2022 : }
2023 0 : return false;
2024 : }
2025 :
2026 : /************************************************************************/
2027 : /* ZarrArray::OpenTilePresenceCache() */
2028 : /************************************************************************/
2029 :
2030 : std::shared_ptr<GDALMDArray>
2031 26598 : ZarrArray::OpenTilePresenceCache(bool bCanCreate) const
2032 : {
2033 26598 : if (m_bHasTriedCacheTilePresenceArray)
2034 26222 : return m_poCacheTilePresenceArray;
2035 376 : m_bHasTriedCacheTilePresenceArray = true;
2036 :
2037 376 : if (m_nTotalTileCount == 1)
2038 170 : return nullptr;
2039 :
2040 412 : std::string osCacheFilename;
2041 412 : auto poRGCache = GetCacheRootGroup(bCanCreate, osCacheFilename);
2042 206 : if (!poRGCache)
2043 196 : return nullptr;
2044 :
2045 10 : const std::string osTilePresenceArrayName(MassageName(GetFullName()) +
2046 20 : "_tile_presence");
2047 20 : auto poTilePresenceArray = poRGCache->OpenMDArray(osTilePresenceArrayName);
2048 20 : const auto eByteDT = GDALExtendedDataType::Create(GDT_Byte);
2049 10 : if (poTilePresenceArray)
2050 : {
2051 8 : bool ok = true;
2052 8 : const auto &apoDimsCache = poTilePresenceArray->GetDimensions();
2053 16 : if (poTilePresenceArray->GetDataType() != eByteDT ||
2054 8 : apoDimsCache.size() != m_aoDims.size())
2055 : {
2056 0 : ok = false;
2057 : }
2058 : else
2059 : {
2060 24 : for (size_t i = 0; i < m_aoDims.size(); i++)
2061 : {
2062 : const auto nExpectedDimSize =
2063 16 : (m_aoDims[i]->GetSize() + m_anBlockSize[i] - 1) /
2064 16 : m_anBlockSize[i];
2065 16 : if (apoDimsCache[i]->GetSize() != nExpectedDimSize)
2066 : {
2067 0 : ok = false;
2068 0 : break;
2069 : }
2070 : }
2071 : }
2072 8 : if (!ok)
2073 : {
2074 0 : CPLError(CE_Failure, CPLE_NotSupported,
2075 : "Array %s in %s has not expected characteristics",
2076 : osTilePresenceArrayName.c_str(), osCacheFilename.c_str());
2077 0 : return nullptr;
2078 : }
2079 :
2080 8 : if (!poTilePresenceArray->GetAttribute("filling_status") && !bCanCreate)
2081 : {
2082 0 : CPLDebug(ZARR_DEBUG_KEY,
2083 : "Cache tile presence array for %s found, but filling not "
2084 : "finished",
2085 0 : GetFullName().c_str());
2086 0 : return nullptr;
2087 : }
2088 :
2089 8 : CPLDebug(ZARR_DEBUG_KEY, "Using cache tile presence for %s",
2090 8 : GetFullName().c_str());
2091 : }
2092 2 : else if (bCanCreate)
2093 : {
2094 2 : int idxDim = 0;
2095 2 : std::string osBlockSize;
2096 2 : std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
2097 6 : for (const auto &poDim : m_aoDims)
2098 : {
2099 4 : auto poNewDim = poRGCache->CreateDimension(
2100 8 : osTilePresenceArrayName + '_' + std::to_string(idxDim),
2101 8 : std::string(), std::string(),
2102 4 : (poDim->GetSize() + m_anBlockSize[idxDim] - 1) /
2103 12 : m_anBlockSize[idxDim]);
2104 4 : if (!poNewDim)
2105 0 : return nullptr;
2106 4 : apoNewDims.emplace_back(poNewDim);
2107 :
2108 4 : if (!osBlockSize.empty())
2109 2 : osBlockSize += ',';
2110 4 : constexpr GUInt64 BLOCKSIZE = 256;
2111 : osBlockSize +=
2112 4 : std::to_string(std::min(poNewDim->GetSize(), BLOCKSIZE));
2113 :
2114 4 : idxDim++;
2115 : }
2116 :
2117 2 : CPLStringList aosOptionsTilePresence;
2118 2 : aosOptionsTilePresence.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
2119 : poTilePresenceArray =
2120 6 : poRGCache->CreateMDArray(osTilePresenceArrayName, apoNewDims,
2121 4 : eByteDT, aosOptionsTilePresence.List());
2122 2 : if (!poTilePresenceArray)
2123 : {
2124 0 : CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
2125 : osTilePresenceArrayName.c_str(), osCacheFilename.c_str());
2126 0 : return nullptr;
2127 : }
2128 2 : poTilePresenceArray->SetNoDataValue(0);
2129 : }
2130 : else
2131 : {
2132 0 : return nullptr;
2133 : }
2134 :
2135 10 : m_poCacheTilePresenceArray = poTilePresenceArray;
2136 :
2137 10 : return poTilePresenceArray;
2138 : }
2139 :
2140 : /************************************************************************/
2141 : /* ZarrArray::CacheTilePresence() */
2142 : /************************************************************************/
2143 :
2144 4 : bool ZarrArray::CacheTilePresence()
2145 : {
2146 4 : if (m_nTotalTileCount == 1)
2147 0 : return true;
2148 :
2149 8 : const std::string osDirectoryName = GetDataDirectory();
2150 :
2151 : struct DirCloser
2152 : {
2153 : DirCloser(const DirCloser &) = delete;
2154 : DirCloser &operator=(const DirCloser &) = delete;
2155 :
2156 : VSIDIR *m_psDir;
2157 :
2158 4 : explicit DirCloser(VSIDIR *psDir) : m_psDir(psDir)
2159 : {
2160 4 : }
2161 :
2162 4 : ~DirCloser()
2163 4 : {
2164 4 : VSICloseDir(m_psDir);
2165 4 : }
2166 : };
2167 :
2168 4 : auto psDir = VSIOpenDir(osDirectoryName.c_str(), -1, nullptr);
2169 4 : if (!psDir)
2170 0 : return false;
2171 8 : DirCloser dirCloser(psDir);
2172 :
2173 8 : auto poTilePresenceArray = OpenTilePresenceCache(true);
2174 4 : if (!poTilePresenceArray)
2175 : {
2176 0 : return false;
2177 : }
2178 :
2179 4 : if (poTilePresenceArray->GetAttribute("filling_status"))
2180 : {
2181 2 : CPLDebug(ZARR_DEBUG_KEY,
2182 : "CacheTilePresence(): %s already filled. Nothing to do",
2183 2 : poTilePresenceArray->GetName().c_str());
2184 2 : return true;
2185 : }
2186 :
2187 4 : std::vector<GUInt64> anTileIdx(m_aoDims.size());
2188 4 : const std::vector<size_t> anCount(m_aoDims.size(), 1);
2189 4 : const std::vector<GInt64> anArrayStep(m_aoDims.size(), 0);
2190 4 : const std::vector<GPtrDiff_t> anBufferStride(m_aoDims.size(), 0);
2191 2 : const auto &apoDimsCache = poTilePresenceArray->GetDimensions();
2192 4 : const auto eByteDT = GDALExtendedDataType::Create(GDT_Byte);
2193 :
2194 2 : CPLDebug(ZARR_DEBUG_KEY,
2195 : "CacheTilePresence(): Iterating over %s to find which tiles are "
2196 : "present...",
2197 : osDirectoryName.c_str());
2198 2 : uint64_t nCounter = 0;
2199 : const char chSrcFilenameDirSeparator =
2200 2 : VSIGetDirectorySeparator(osDirectoryName.c_str())[0];
2201 14 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
2202 : {
2203 12 : if (!VSI_ISDIR(psEntry->nMode))
2204 : {
2205 : const CPLStringList aosTokens = GetTileIndicesFromFilename(
2206 0 : CPLString(psEntry->pszName)
2207 9 : .replaceAll(chSrcFilenameDirSeparator, '/')
2208 18 : .c_str());
2209 9 : if (aosTokens.size() == static_cast<int>(m_aoDims.size()))
2210 : {
2211 : // Get tile indices from filename
2212 5 : bool unexpectedIndex = false;
2213 15 : for (int i = 0; i < aosTokens.size(); ++i)
2214 : {
2215 10 : if (CPLGetValueType(aosTokens[i]) != CPL_VALUE_INTEGER)
2216 : {
2217 2 : unexpectedIndex = true;
2218 : }
2219 20 : anTileIdx[i] =
2220 10 : static_cast<GUInt64>(CPLAtoGIntBig(aosTokens[i]));
2221 10 : if (anTileIdx[i] >= apoDimsCache[i]->GetSize())
2222 : {
2223 0 : unexpectedIndex = true;
2224 : }
2225 : }
2226 5 : if (unexpectedIndex)
2227 : {
2228 1 : continue;
2229 : }
2230 :
2231 4 : nCounter++;
2232 4 : if ((nCounter % 1000) == 0)
2233 : {
2234 0 : CPLDebug(ZARR_DEBUG_KEY,
2235 : "CacheTilePresence(): Listing in progress "
2236 : "(last examined %s, at least %.02f %% completed)",
2237 0 : psEntry->pszName,
2238 0 : 100.0 * double(nCounter) /
2239 0 : double(m_nTotalTileCount));
2240 : }
2241 4 : constexpr GByte byOne = 1;
2242 : // CPLDebugOnly(ZARR_DEBUG_KEY, "Marking %s has present",
2243 : // psEntry->pszName);
2244 8 : if (!poTilePresenceArray->Write(
2245 4 : anTileIdx.data(), anCount.data(), anArrayStep.data(),
2246 : anBufferStride.data(), eByteDT, &byOne))
2247 : {
2248 0 : return false;
2249 : }
2250 : }
2251 : }
2252 12 : }
2253 2 : CPLDebug(ZARR_DEBUG_KEY, "CacheTilePresence(): finished");
2254 :
2255 : // Write filling_status attribute
2256 2 : auto poAttr = poTilePresenceArray->CreateAttribute(
2257 4 : "filling_status", {}, GDALExtendedDataType::CreateString(), nullptr);
2258 2 : if (poAttr)
2259 : {
2260 2 : if (nCounter == 0)
2261 0 : poAttr->Write("no_tile_present");
2262 2 : else if (nCounter == m_nTotalTileCount)
2263 0 : poAttr->Write("all_tiles_present");
2264 : else
2265 2 : poAttr->Write("some_tiles_missing");
2266 : }
2267 :
2268 : // Force closing
2269 2 : m_poCacheTilePresenceArray = nullptr;
2270 2 : m_bHasTriedCacheTilePresenceArray = false;
2271 :
2272 2 : return true;
2273 : }
2274 :
2275 : /************************************************************************/
2276 : /* ZarrArray::CreateAttribute() */
2277 : /************************************************************************/
2278 :
2279 108 : std::shared_ptr<GDALAttribute> ZarrArray::CreateAttribute(
2280 : const std::string &osName, const std::vector<GUInt64> &anDimensions,
2281 : const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
2282 : {
2283 108 : if (!CheckValidAndErrorOutIfNot())
2284 0 : return nullptr;
2285 :
2286 108 : if (!m_bUpdatable)
2287 : {
2288 2 : CPLError(CE_Failure, CPLE_NotSupported,
2289 : "Dataset not open in update mode");
2290 2 : return nullptr;
2291 : }
2292 106 : if (anDimensions.size() >= 2)
2293 : {
2294 2 : CPLError(CE_Failure, CPLE_NotSupported,
2295 : "Cannot create attributes of dimension >= 2");
2296 2 : return nullptr;
2297 : }
2298 : return m_oAttrGroup.CreateAttribute(osName, anDimensions, oDataType,
2299 104 : papszOptions);
2300 : }
2301 :
2302 : /************************************************************************/
2303 : /* ZarrGroupBase::DeleteAttribute() */
2304 : /************************************************************************/
2305 :
2306 18 : bool ZarrArray::DeleteAttribute(const std::string &osName, CSLConstList)
2307 : {
2308 18 : if (!CheckValidAndErrorOutIfNot())
2309 0 : return false;
2310 :
2311 18 : if (!m_bUpdatable)
2312 : {
2313 6 : CPLError(CE_Failure, CPLE_NotSupported,
2314 : "Dataset not open in update mode");
2315 6 : return false;
2316 : }
2317 :
2318 12 : return m_oAttrGroup.DeleteAttribute(osName);
2319 : }
2320 :
2321 : /************************************************************************/
2322 : /* ZarrArray::SetSpatialRef() */
2323 : /************************************************************************/
2324 :
2325 38 : bool ZarrArray::SetSpatialRef(const OGRSpatialReference *poSRS)
2326 : {
2327 38 : if (!CheckValidAndErrorOutIfNot())
2328 0 : return false;
2329 :
2330 38 : if (!m_bUpdatable)
2331 : {
2332 2 : return GDALPamMDArray::SetSpatialRef(poSRS);
2333 : }
2334 36 : m_poSRS.reset();
2335 36 : if (poSRS)
2336 36 : m_poSRS.reset(poSRS->Clone());
2337 36 : m_bSRSModified = true;
2338 36 : return true;
2339 : }
2340 :
2341 : /************************************************************************/
2342 : /* ZarrArray::SetUnit() */
2343 : /************************************************************************/
2344 :
2345 9 : bool ZarrArray::SetUnit(const std::string &osUnit)
2346 : {
2347 9 : if (!CheckValidAndErrorOutIfNot())
2348 0 : return false;
2349 :
2350 9 : if (!m_bUpdatable)
2351 : {
2352 0 : CPLError(CE_Failure, CPLE_NotSupported,
2353 : "Dataset not open in update mode");
2354 0 : return false;
2355 : }
2356 9 : m_osUnit = osUnit;
2357 9 : m_bUnitModified = true;
2358 9 : return true;
2359 : }
2360 :
2361 : /************************************************************************/
2362 : /* ZarrArray::GetOffset() */
2363 : /************************************************************************/
2364 :
2365 79 : double ZarrArray::GetOffset(bool *pbHasOffset,
2366 : GDALDataType *peStorageType) const
2367 : {
2368 79 : if (pbHasOffset)
2369 79 : *pbHasOffset = m_bHasOffset;
2370 79 : if (peStorageType)
2371 0 : *peStorageType = GDT_Unknown;
2372 79 : return m_dfOffset;
2373 : }
2374 :
2375 : /************************************************************************/
2376 : /* ZarrArray::GetScale() */
2377 : /************************************************************************/
2378 :
2379 77 : double ZarrArray::GetScale(bool *pbHasScale, GDALDataType *peStorageType) const
2380 : {
2381 77 : if (pbHasScale)
2382 77 : *pbHasScale = m_bHasScale;
2383 77 : if (peStorageType)
2384 0 : *peStorageType = GDT_Unknown;
2385 77 : return m_dfScale;
2386 : }
2387 :
2388 : /************************************************************************/
2389 : /* ZarrArray::SetOffset() */
2390 : /************************************************************************/
2391 :
2392 3 : bool ZarrArray::SetOffset(double dfOffset, GDALDataType /* eStorageType */)
2393 : {
2394 3 : if (!CheckValidAndErrorOutIfNot())
2395 0 : return false;
2396 :
2397 3 : m_dfOffset = dfOffset;
2398 3 : m_bHasOffset = true;
2399 3 : m_bOffsetModified = true;
2400 3 : return true;
2401 : }
2402 :
2403 : /************************************************************************/
2404 : /* ZarrArray::SetScale() */
2405 : /************************************************************************/
2406 :
2407 3 : bool ZarrArray::SetScale(double dfScale, GDALDataType /* eStorageType */)
2408 : {
2409 3 : if (!CheckValidAndErrorOutIfNot())
2410 0 : return false;
2411 :
2412 3 : m_dfScale = dfScale;
2413 3 : m_bHasScale = true;
2414 3 : m_bScaleModified = true;
2415 3 : return true;
2416 : }
2417 :
2418 : /************************************************************************/
2419 : /* GetDimensionTypeDirection() */
2420 : /************************************************************************/
2421 :
2422 : /* static */
2423 138 : void ZarrArray::GetDimensionTypeDirection(CPLJSONObject &oAttributes,
2424 : std::string &osType,
2425 : std::string &osDirection)
2426 : {
2427 276 : std::string osUnit;
2428 414 : const auto unit = oAttributes[CF_UNITS];
2429 138 : if (unit.GetType() == CPLJSONObject::Type::String)
2430 : {
2431 50 : osUnit = unit.ToString();
2432 : }
2433 :
2434 414 : const auto oStdName = oAttributes[CF_STD_NAME];
2435 138 : if (oStdName.GetType() == CPLJSONObject::Type::String)
2436 : {
2437 150 : const auto osStdName = oStdName.ToString();
2438 50 : if (osStdName == CF_PROJ_X_COORD || osStdName == CF_LONGITUDE_STD_NAME)
2439 : {
2440 25 : osType = GDAL_DIM_TYPE_HORIZONTAL_X;
2441 25 : oAttributes.Delete(CF_STD_NAME);
2442 25 : if (osUnit == CF_DEGREES_EAST)
2443 : {
2444 23 : osDirection = "EAST";
2445 : }
2446 : }
2447 48 : else if (osStdName == CF_PROJ_Y_COORD ||
2448 23 : osStdName == CF_LATITUDE_STD_NAME)
2449 : {
2450 25 : osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
2451 25 : oAttributes.Delete(CF_STD_NAME);
2452 25 : if (osUnit == CF_DEGREES_NORTH)
2453 : {
2454 23 : osDirection = "NORTH";
2455 : }
2456 : }
2457 0 : else if (osStdName == "time")
2458 : {
2459 0 : osType = GDAL_DIM_TYPE_TEMPORAL;
2460 0 : oAttributes.Delete(CF_STD_NAME);
2461 : }
2462 : }
2463 :
2464 414 : const auto osAxis = oAttributes[CF_AXIS].ToString();
2465 138 : if (osAxis == "Z")
2466 : {
2467 0 : osType = GDAL_DIM_TYPE_VERTICAL;
2468 0 : const auto osPositive = oAttributes["positive"].ToString();
2469 0 : if (osPositive == "up")
2470 : {
2471 0 : osDirection = "UP";
2472 0 : oAttributes.Delete("positive");
2473 : }
2474 0 : else if (osPositive == "down")
2475 : {
2476 0 : osDirection = "DOWN";
2477 0 : oAttributes.Delete("positive");
2478 : }
2479 0 : oAttributes.Delete(CF_AXIS);
2480 : }
2481 138 : }
2482 :
2483 : /************************************************************************/
2484 : /* GetCoordinateVariables() */
2485 : /************************************************************************/
2486 :
2487 : std::vector<std::shared_ptr<GDALMDArray>>
2488 2 : ZarrArray::GetCoordinateVariables() const
2489 : {
2490 2 : if (!CheckValidAndErrorOutIfNot())
2491 0 : return {};
2492 :
2493 4 : std::vector<std::shared_ptr<GDALMDArray>> ret;
2494 6 : const auto poCoordinates = GetAttribute("coordinates");
2495 1 : if (poCoordinates &&
2496 3 : poCoordinates->GetDataType().GetClass() == GEDTC_STRING &&
2497 1 : poCoordinates->GetDimensionCount() == 0)
2498 : {
2499 1 : const char *pszCoordinates = poCoordinates->ReadAsString();
2500 1 : if (pszCoordinates)
2501 : {
2502 2 : auto poGroup = m_poGroupWeak.lock();
2503 1 : if (!poGroup)
2504 : {
2505 0 : CPLError(CE_Failure, CPLE_AppDefined,
2506 : "Cannot access coordinate variables of %s has "
2507 : "belonging group has gone out of scope",
2508 0 : GetName().c_str());
2509 : }
2510 : else
2511 : {
2512 : const CPLStringList aosNames(
2513 2 : CSLTokenizeString2(pszCoordinates, " ", 0));
2514 3 : for (int i = 0; i < aosNames.size(); i++)
2515 : {
2516 6 : auto poCoordinateVar = poGroup->OpenMDArray(aosNames[i]);
2517 2 : if (poCoordinateVar)
2518 : {
2519 2 : ret.emplace_back(poCoordinateVar);
2520 : }
2521 : else
2522 : {
2523 0 : CPLError(CE_Warning, CPLE_AppDefined,
2524 : "Cannot find variable corresponding to "
2525 : "coordinate %s",
2526 : aosNames[i]);
2527 : }
2528 : }
2529 : }
2530 : }
2531 : }
2532 :
2533 2 : return ret;
2534 : }
2535 :
2536 : /************************************************************************/
2537 : /* Resize() */
2538 : /************************************************************************/
2539 :
2540 16 : bool ZarrArray::Resize(const std::vector<GUInt64> &anNewDimSizes,
2541 : CSLConstList /* papszOptions */)
2542 : {
2543 16 : if (!CheckValidAndErrorOutIfNot())
2544 0 : return false;
2545 :
2546 16 : if (!IsWritable())
2547 : {
2548 3 : CPLError(CE_Failure, CPLE_AppDefined,
2549 : "Resize() not supported on read-only file");
2550 3 : return false;
2551 : }
2552 :
2553 13 : const auto nDimCount = GetDimensionCount();
2554 13 : if (anNewDimSizes.size() != nDimCount)
2555 : {
2556 0 : CPLError(CE_Failure, CPLE_IllegalArg,
2557 : "Not expected number of values in anNewDimSizes.");
2558 0 : return false;
2559 : }
2560 :
2561 13 : auto &dims = GetDimensions();
2562 26 : std::vector<size_t> anGrownDimIdx;
2563 26 : std::map<GDALDimension *, GUInt64> oMapDimToSize;
2564 27 : for (size_t i = 0; i < nDimCount; ++i)
2565 : {
2566 21 : auto oIter = oMapDimToSize.find(dims[i].get());
2567 21 : if (oIter != oMapDimToSize.end() && oIter->second != anNewDimSizes[i])
2568 : {
2569 2 : CPLError(CE_Failure, CPLE_AppDefined,
2570 : "Cannot resize a dimension referenced several times "
2571 : "to different sizes");
2572 7 : return false;
2573 : }
2574 19 : if (anNewDimSizes[i] != dims[i]->GetSize())
2575 : {
2576 14 : if (anNewDimSizes[i] < dims[i]->GetSize())
2577 : {
2578 5 : CPLError(CE_Failure, CPLE_NotSupported,
2579 : "Resize() does not support shrinking the array.");
2580 5 : return false;
2581 : }
2582 :
2583 9 : oMapDimToSize[dims[i].get()] = anNewDimSizes[i];
2584 9 : anGrownDimIdx.push_back(i);
2585 : }
2586 : else
2587 : {
2588 5 : oMapDimToSize[dims[i].get()] = dims[i]->GetSize();
2589 : }
2590 : }
2591 6 : if (!anGrownDimIdx.empty())
2592 : {
2593 6 : m_bDefinitionModified = true;
2594 13 : for (size_t dimIdx : anGrownDimIdx)
2595 : {
2596 14 : auto dim = std::dynamic_pointer_cast<ZarrDimension>(dims[dimIdx]);
2597 7 : if (dim)
2598 : {
2599 7 : dim->SetSize(anNewDimSizes[dimIdx]);
2600 7 : if (dim->GetName() != dim->GetFullName())
2601 : {
2602 : // This is not a local dimension
2603 7 : m_poSharedResource->UpdateDimensionSize(dim);
2604 : }
2605 : }
2606 : else
2607 : {
2608 0 : CPLAssert(false);
2609 : }
2610 : }
2611 : }
2612 6 : return true;
2613 : }
2614 :
2615 : /************************************************************************/
2616 : /* NotifyChildrenOfRenaming() */
2617 : /************************************************************************/
2618 :
2619 15 : void ZarrArray::NotifyChildrenOfRenaming()
2620 : {
2621 15 : m_oAttrGroup.ParentRenamed(m_osFullName);
2622 15 : }
2623 :
2624 : /************************************************************************/
2625 : /* ParentRenamed() */
2626 : /************************************************************************/
2627 :
2628 9 : void ZarrArray::ParentRenamed(const std::string &osNewParentFullName)
2629 : {
2630 9 : GDALMDArray::ParentRenamed(osNewParentFullName);
2631 :
2632 18 : auto poParent = m_poGroupWeak.lock();
2633 : // The parent necessarily exist, since it notified us
2634 9 : CPLAssert(poParent);
2635 :
2636 : m_osFilename =
2637 9 : CPLFormFilename(CPLFormFilename(poParent->GetDirectoryName().c_str(),
2638 9 : m_osName.c_str(), nullptr),
2639 18 : CPLGetFilename(m_osFilename.c_str()), nullptr);
2640 9 : }
2641 :
2642 : /************************************************************************/
2643 : /* Rename() */
2644 : /************************************************************************/
2645 :
2646 21 : bool ZarrArray::Rename(const std::string &osNewName)
2647 : {
2648 21 : if (!CheckValidAndErrorOutIfNot())
2649 0 : return false;
2650 :
2651 21 : if (!m_bUpdatable)
2652 : {
2653 6 : CPLError(CE_Failure, CPLE_NotSupported,
2654 : "Dataset not open in update mode");
2655 6 : return false;
2656 : }
2657 15 : if (!ZarrGroupBase::IsValidObjectName(osNewName))
2658 : {
2659 3 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid array name");
2660 3 : return false;
2661 : }
2662 :
2663 24 : auto poParent = m_poGroupWeak.lock();
2664 12 : if (poParent)
2665 : {
2666 12 : if (!poParent->CheckArrayOrGroupWithSameNameDoesNotExist(osNewName))
2667 6 : return false;
2668 : }
2669 :
2670 : const std::string osRootDirectoryName(
2671 12 : CPLGetDirname(CPLGetDirname(m_osFilename.c_str())));
2672 : const std::string osOldDirectoryName =
2673 12 : CPLFormFilename(osRootDirectoryName.c_str(), m_osName.c_str(), nullptr);
2674 : const std::string osNewDirectoryName = CPLFormFilename(
2675 12 : osRootDirectoryName.c_str(), osNewName.c_str(), nullptr);
2676 :
2677 6 : if (VSIRename(osOldDirectoryName.c_str(), osNewDirectoryName.c_str()) != 0)
2678 : {
2679 0 : CPLError(CE_Failure, CPLE_AppDefined, "Renaming of %s to %s failed",
2680 : osOldDirectoryName.c_str(), osNewDirectoryName.c_str());
2681 0 : return false;
2682 : }
2683 :
2684 6 : m_poSharedResource->RenameZMetadataRecursive(osOldDirectoryName,
2685 : osNewDirectoryName);
2686 :
2687 : m_osFilename =
2688 : CPLFormFilename(osNewDirectoryName.c_str(),
2689 6 : CPLGetFilename(m_osFilename.c_str()), nullptr);
2690 :
2691 6 : if (poParent)
2692 : {
2693 6 : poParent->NotifyArrayRenamed(m_osName, osNewName);
2694 : }
2695 :
2696 6 : BaseRename(osNewName);
2697 :
2698 6 : return true;
2699 : }
2700 :
2701 : /************************************************************************/
2702 : /* NotifyChildrenOfDeletion() */
2703 : /************************************************************************/
2704 :
2705 8 : void ZarrArray::NotifyChildrenOfDeletion()
2706 : {
2707 8 : m_oAttrGroup.ParentDeleted();
2708 8 : }
2709 :
2710 : /************************************************************************/
2711 : /* ParseSpecialAttributes() */
2712 : /************************************************************************/
2713 :
2714 743 : void ZarrArray::ParseSpecialAttributes(
2715 : const std::shared_ptr<GDALGroup> &poGroup, CPLJSONObject &oAttributes)
2716 : {
2717 2229 : const auto crs = oAttributes[CRS_ATTRIBUTE_NAME];
2718 743 : std::shared_ptr<OGRSpatialReference> poSRS;
2719 743 : if (crs.GetType() == CPLJSONObject::Type::Object)
2720 : {
2721 35 : for (const char *key : {"url", "wkt", "projjson"})
2722 : {
2723 70 : const auto item = crs[key];
2724 35 : if (item.IsValid())
2725 : {
2726 20 : poSRS = std::make_shared<OGRSpatialReference>();
2727 40 : if (poSRS->SetFromUserInput(
2728 40 : item.ToString().c_str(),
2729 : OGRSpatialReference::
2730 20 : SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
2731 : OGRERR_NONE)
2732 : {
2733 20 : oAttributes.Delete(CRS_ATTRIBUTE_NAME);
2734 20 : break;
2735 : }
2736 0 : poSRS.reset();
2737 : }
2738 : }
2739 : }
2740 : else
2741 : {
2742 : // Check if SRS is using CF-1 conventions
2743 2169 : const auto gridMapping = oAttributes["grid_mapping"];
2744 723 : if (gridMapping.GetType() == CPLJSONObject::Type::String)
2745 : {
2746 : const auto gridMappingArray =
2747 6 : poGroup->OpenMDArray(gridMapping.ToString());
2748 2 : if (gridMappingArray)
2749 : {
2750 2 : poSRS = std::make_shared<OGRSpatialReference>();
2751 4 : CPLStringList aosKeyValues;
2752 22 : for (const auto &poAttr : gridMappingArray->GetAttributes())
2753 : {
2754 20 : if (poAttr->GetDataType().GetClass() == GEDTC_STRING)
2755 : {
2756 4 : aosKeyValues.SetNameValue(poAttr->GetName().c_str(),
2757 8 : poAttr->ReadAsString());
2758 : }
2759 16 : else if (poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
2760 : {
2761 32 : std::string osVal;
2762 32 : for (double val : poAttr->ReadAsDoubleArray())
2763 : {
2764 16 : if (!osVal.empty())
2765 0 : osVal += ',';
2766 16 : osVal += CPLSPrintf("%.17g", val);
2767 : }
2768 16 : aosKeyValues.SetNameValue(poAttr->GetName().c_str(),
2769 32 : osVal.c_str());
2770 : }
2771 : }
2772 2 : if (poSRS->importFromCF1(aosKeyValues.List(), nullptr) !=
2773 : OGRERR_NONE)
2774 : {
2775 0 : poSRS.reset();
2776 : }
2777 : }
2778 : }
2779 : }
2780 :
2781 743 : if (poSRS)
2782 : {
2783 22 : int iDimX = 0;
2784 22 : int iDimY = 0;
2785 22 : int iCount = 1;
2786 66 : for (const auto &poDim : GetDimensions())
2787 : {
2788 44 : if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
2789 2 : iDimX = iCount;
2790 42 : else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
2791 2 : iDimY = iCount;
2792 44 : iCount++;
2793 : }
2794 22 : if ((iDimX == 0 || iDimY == 0) && GetDimensionCount() >= 2)
2795 : {
2796 20 : iDimX = static_cast<int>(GetDimensionCount());
2797 20 : iDimY = iDimX - 1;
2798 : }
2799 22 : if (iDimX > 0 && iDimY > 0)
2800 : {
2801 22 : if (poSRS->GetDataAxisToSRSAxisMapping() == std::vector<int>{2, 1})
2802 0 : poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
2803 22 : else if (poSRS->GetDataAxisToSRSAxisMapping() ==
2804 44 : std::vector<int>{1, 2})
2805 22 : poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
2806 : }
2807 :
2808 22 : SetSRS(poSRS);
2809 : }
2810 :
2811 2229 : const auto unit = oAttributes[CF_UNITS];
2812 743 : if (unit.GetType() == CPLJSONObject::Type::String)
2813 : {
2814 159 : std::string osUnit = unit.ToString();
2815 53 : oAttributes.Delete(CF_UNITS);
2816 53 : RegisterUnit(osUnit);
2817 : }
2818 :
2819 2229 : const auto offset = oAttributes[CF_ADD_OFFSET];
2820 743 : const auto offsetType = offset.GetType();
2821 743 : if (offsetType == CPLJSONObject::Type::Integer ||
2822 743 : offsetType == CPLJSONObject::Type::Long ||
2823 : offsetType == CPLJSONObject::Type::Double)
2824 : {
2825 3 : double dfOffset = offset.ToDouble();
2826 3 : oAttributes.Delete(CF_ADD_OFFSET);
2827 3 : RegisterOffset(dfOffset);
2828 : }
2829 :
2830 2229 : const auto scale = oAttributes[CF_SCALE_FACTOR];
2831 743 : const auto scaleType = scale.GetType();
2832 743 : if (scaleType == CPLJSONObject::Type::Integer ||
2833 743 : scaleType == CPLJSONObject::Type::Long ||
2834 : scaleType == CPLJSONObject::Type::Double)
2835 : {
2836 3 : double dfScale = scale.ToDouble();
2837 3 : oAttributes.Delete(CF_SCALE_FACTOR);
2838 3 : RegisterScale(dfScale);
2839 : }
2840 743 : }
2841 :
2842 : /************************************************************************/
2843 : /* SetStatistics() */
2844 : /************************************************************************/
2845 :
2846 1 : bool ZarrArray::SetStatistics(bool bApproxStats, double dfMin, double dfMax,
2847 : double dfMean, double dfStdDev,
2848 : GUInt64 nValidCount, CSLConstList papszOptions)
2849 : {
2850 2 : if (!bApproxStats && m_bUpdatable &&
2851 1 : CPLTestBool(
2852 : CSLFetchNameValueDef(papszOptions, "UPDATE_METADATA", "NO")))
2853 : {
2854 3 : auto poAttr = GetAttribute("actual_range");
2855 1 : if (!poAttr)
2856 : {
2857 : poAttr =
2858 1 : CreateAttribute("actual_range", {2}, GetDataType(), nullptr);
2859 : }
2860 1 : if (poAttr)
2861 : {
2862 2 : std::vector<GUInt64> startIdx = {0};
2863 2 : std::vector<size_t> count = {2};
2864 1 : std::vector<double> values = {dfMin, dfMax};
2865 2 : poAttr->Write(startIdx.data(), count.data(), nullptr, nullptr,
2866 2 : GDALExtendedDataType::Create(GDT_Float64),
2867 1 : values.data(), nullptr, 0);
2868 : }
2869 : }
2870 1 : return GDALPamMDArray::SetStatistics(bApproxStats, dfMin, dfMax, dfMean,
2871 1 : dfStdDev, nValidCount, papszOptions);
2872 : }
|