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