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