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