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