Coverage Report

Created: 2025-06-09 08:44

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