Coverage Report

Created: 2025-06-09 07:42

/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
0
{
66
    // A UCS4 char can require up to 6 bytes in UTF8.
67
0
    if (nSize > (std::numeric_limits<size_t>::max() - 1) / 6 * 4)
68
0
        return nullptr;
69
0
    const size_t nOutSize = nSize / 4 * 6 + 1;
70
0
    char *ret = static_cast<char *>(VSI_MALLOC_VERBOSE(nOutSize));
71
0
    if (ret == nullptr)
72
0
        return nullptr;
73
0
    size_t outPos = 0;
74
0
    for (size_t i = 0; i + sizeof(uint32_t) - 1 < nSize; i += sizeof(uint32_t))
75
0
    {
76
0
        uint32_t ucs4;
77
0
        memcpy(&ucs4, ucs4Ptr + i, sizeof(uint32_t));
78
0
        if (needByteSwap)
79
0
        {
80
0
            CPL_SWAP32PTR(&ucs4);
81
0
        }
82
0
        int written =
83
0
            FcUcs4ToUtf8(ucs4, reinterpret_cast<uint8_t *>(ret + outPos));
84
0
        outPos += written;
85
0
    }
86
0
    ret[outPos] = 0;
87
0
    return ret;
88
0
}
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
0
{
100
0
    size_t nBlockSize = oType.GetSize();
101
0
    for (const auto &item : oChunks)
102
0
    {
103
0
        const auto nSize = static_cast<GUInt64>(item.ToLong());
104
0
        if (nSize == 0)
105
0
        {
106
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid content for chunks");
107
0
            return false;
108
0
        }
109
0
        if (nBlockSize > std::numeric_limits<size_t>::max() / nSize)
110
0
        {
111
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too large chunks");
112
0
            return false;
113
0
        }
114
0
        nBlockSize *= static_cast<size_t>(nSize);
115
0
        anBlockSize.emplace_back(nSize);
116
0
    }
117
118
0
    return true;
119
0
}
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
0
{
130
0
    uint64_t nTotalTileCount = 1;
131
0
    for (size_t i = 0; i < aoDims.size(); ++i)
132
0
    {
133
0
        uint64_t nTileThisDim =
134
0
            (aoDims[i]->GetSize() / anBlockSize[i]) +
135
0
            (((aoDims[i]->GetSize() % anBlockSize[i]) != 0) ? 1 : 0);
136
0
        if (nTileThisDim != 0 &&
137
0
            nTotalTileCount >
138
0
                std::numeric_limits<uint64_t>::max() / nTileThisDim)
139
0
        {
140
0
            CPLError(
141
0
                CE_Failure, CPLE_NotSupported,
142
0
                "Array %s has more than 2^64 tiles. This is not supported.",
143
0
                osName.c_str());
144
0
            return 0;
145
0
        }
146
0
        nTotalTileCount *= nTileThisDim;
147
0
    }
148
0
    return nTotalTileCount;
149
0
}
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
0
      GDALPamMDArray(osParentName, osName, poSharedResource->GetPAM()),
166
0
      m_poSharedResource(poSharedResource), m_aoDims(aoDims), m_oType(oType),
167
0
      m_aoDtypeElts(aoDtypeElts), m_anBlockSize(anBlockSize),
168
0
      m_oAttrGroup(m_osFullName, /*bContainerIsGroup=*/false)
169
0
{
170
0
    m_nTotalTileCount = ComputeTileCount(osName, aoDims, anBlockSize);
171
0
    if (m_nTotalTileCount == 0)
172
0
        return;
173
174
    // Compute individual tile size
175
0
    const size_t nSourceSize =
176
0
        m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
177
0
    m_nTileSize = nSourceSize;
178
0
    for (const auto &nBlockSize : m_anBlockSize)
179
0
    {
180
0
        m_nTileSize *= static_cast<size_t>(nBlockSize);
181
0
    }
182
183
0
    m_bUseOptimizedCodePaths = CPLTestBool(
184
0
        CPLGetConfigOption("GDAL_ZARR_USE_OPTIMIZED_CODE_PATHS", "YES"));
185
0
}
186
187
/************************************************************************/
188
/*                              ~ZarrArray()                            */
189
/************************************************************************/
190
191
ZarrArray::~ZarrArray()
192
0
{
193
0
    if (m_pabyNoData)
194
0
    {
195
0
        m_oType.FreeDynamicMemory(&m_pabyNoData[0]);
196
0
        CPLFree(m_pabyNoData);
197
0
    }
198
199
0
    DeallocateDecodedTileData();
200
0
}
201
202
/************************************************************************/
203
/*              ZarrArray::SerializeSpecialAttributes()                 */
204
/************************************************************************/
205
206
CPLJSONObject ZarrArray::SerializeSpecialAttributes()
207
0
{
208
0
    m_bSRSModified = false;
209
0
    m_oAttrGroup.UnsetModified();
210
211
0
    auto oAttrs = m_oAttrGroup.Serialize();
212
213
0
    if (m_poSRS)
214
0
    {
215
0
        CPLJSONObject oCRS;
216
0
        const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
217
0
        char *pszWKT = nullptr;
218
0
        if (m_poSRS->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE)
219
0
        {
220
0
            oCRS.Add("wkt", pszWKT);
221
0
        }
222
0
        CPLFree(pszWKT);
223
224
0
        {
225
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
226
0
            char *projjson = nullptr;
227
0
            if (m_poSRS->exportToPROJJSON(&projjson, nullptr) == OGRERR_NONE &&
228
0
                projjson != nullptr)
229
0
            {
230
0
                CPLJSONDocument oDocProjJSON;
231
0
                if (oDocProjJSON.LoadMemory(std::string(projjson)))
232
0
                {
233
0
                    oCRS.Add("projjson", oDocProjJSON.GetRoot());
234
0
                }
235
0
            }
236
0
            CPLFree(projjson);
237
0
        }
238
239
0
        const char *pszAuthorityCode = m_poSRS->GetAuthorityCode(nullptr);
240
0
        const char *pszAuthorityName = m_poSRS->GetAuthorityName(nullptr);
241
0
        if (pszAuthorityCode && pszAuthorityName &&
242
0
            EQUAL(pszAuthorityName, "EPSG"))
243
0
        {
244
0
            oCRS.Add("url",
245
0
                     std::string("http://www.opengis.net/def/crs/EPSG/0/") +
246
0
                         pszAuthorityCode);
247
0
        }
248
249
0
        oAttrs.Add(CRS_ATTRIBUTE_NAME, oCRS);
250
0
    }
251
252
0
    if (m_osUnit.empty())
253
0
    {
254
0
        if (m_bUnitModified)
255
0
            oAttrs.Delete(CF_UNITS);
256
0
    }
257
0
    else
258
0
    {
259
0
        oAttrs.Set(CF_UNITS, m_osUnit);
260
0
    }
261
0
    m_bUnitModified = false;
262
263
0
    if (!m_bHasOffset)
264
0
    {
265
0
        oAttrs.Delete(CF_ADD_OFFSET);
266
0
    }
267
0
    else
268
0
    {
269
0
        oAttrs.Set(CF_ADD_OFFSET, m_dfOffset);
270
0
    }
271
0
    m_bOffsetModified = false;
272
273
0
    if (!m_bHasScale)
274
0
    {
275
0
        oAttrs.Delete(CF_SCALE_FACTOR);
276
0
    }
277
0
    else
278
0
    {
279
0
        oAttrs.Set(CF_SCALE_FACTOR, m_dfScale);
280
0
    }
281
0
    m_bScaleModified = false;
282
283
0
    return oAttrs;
284
0
}
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
0
{
296
0
    const auto nDims = aoDimensions.size();
297
0
    anBlockSize.resize(nDims);
298
0
    for (size_t i = 0; i < nDims; ++i)
299
0
        anBlockSize[i] = 1;
300
0
    if (nDims >= 2)
301
0
    {
302
0
        anBlockSize[nDims - 2] =
303
0
            std::min(std::max<GUInt64>(1, aoDimensions[nDims - 2]->GetSize()),
304
0
                     static_cast<GUInt64>(256));
305
0
        anBlockSize[nDims - 1] =
306
0
            std::min(std::max<GUInt64>(1, aoDimensions[nDims - 1]->GetSize()),
307
0
                     static_cast<GUInt64>(256));
308
0
    }
309
0
    else if (nDims == 1)
310
0
    {
311
0
        anBlockSize[0] = std::max<GUInt64>(1, aoDimensions[0]->GetSize());
312
0
    }
313
314
0
    const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
315
0
    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
0
    return true;
346
0
}
347
348
/************************************************************************/
349
/*                      DeallocateDecodedTileData()                     */
350
/************************************************************************/
351
352
void ZarrArray::DeallocateDecodedTileData()
353
0
{
354
0
    if (!m_abyDecodedTileData.empty())
355
0
    {
356
0
        const size_t nDTSize = m_oType.GetSize();
357
0
        GByte *pDst = &m_abyDecodedTileData[0];
358
0
        const size_t nValues = m_abyDecodedTileData.size() / nDTSize;
359
0
        for (const auto &elt : m_aoDtypeElts)
360
0
        {
361
0
            if (elt.nativeType == DtypeElt::NativeType::STRING_ASCII ||
362
0
                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
0
        }
374
0
    }
375
0
}
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
0
{
537
0
    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
0
    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
0
    else
548
0
    {
549
0
        const double dfVal = GetNoDataValueAsDouble();
550
0
        if (std::isnan(dfVal))
551
0
            oRoot.Add("fill_value", "NaN");
552
0
        else if (dfVal == std::numeric_limits<double>::infinity())
553
0
            oRoot.Add("fill_value", "Infinity");
554
0
        else if (dfVal == -std::numeric_limits<double>::infinity())
555
0
            oRoot.Add("fill_value", "-Infinity");
556
0
        else if (GDALDataTypeIsInteger(m_oType.GetNumericDataType()))
557
0
            oRoot.Add("fill_value", static_cast<GInt64>(dfVal));
558
0
        else
559
0
            oRoot.Add("fill_value", dfVal);
560
0
    }
561
0
}
562
563
/************************************************************************/
564
/*                    ZarrArray::GetSpatialRef()                        */
565
/************************************************************************/
566
567
std::shared_ptr<OGRSpatialReference> ZarrArray::GetSpatialRef() const
568
0
{
569
0
    if (!CheckValidAndErrorOutIfNot())
570
0
        return nullptr;
571
572
0
    if (m_poSRS)
573
0
        return m_poSRS;
574
0
    return GDALPamMDArray::GetSpatialRef();
575
0
}
576
577
/************************************************************************/
578
/*                        SetRawNoDataValue()                           */
579
/************************************************************************/
580
581
bool ZarrArray::SetRawNoDataValue(const void *pRawNoData)
582
0
{
583
0
    if (!CheckValidAndErrorOutIfNot())
584
0
        return false;
585
586
0
    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
0
    m_bDefinitionModified = true;
592
0
    RegisterNoDataValue(pRawNoData);
593
0
    return true;
594
0
}
595
596
/************************************************************************/
597
/*                        RegisterNoDataValue()                         */
598
/************************************************************************/
599
600
void ZarrArray::RegisterNoDataValue(const void *pNoData)
601
0
{
602
0
    if (m_pabyNoData)
603
0
    {
604
0
        m_oType.FreeDynamicMemory(&m_pabyNoData[0]);
605
0
    }
606
607
0
    if (pNoData == nullptr)
608
0
    {
609
0
        CPLFree(m_pabyNoData);
610
0
        m_pabyNoData = nullptr;
611
0
    }
612
0
    else
613
0
    {
614
0
        const auto nSize = m_oType.GetSize();
615
0
        if (m_pabyNoData == nullptr)
616
0
        {
617
0
            m_pabyNoData = static_cast<GByte *>(CPLMalloc(nSize));
618
0
        }
619
0
        memset(m_pabyNoData, 0, nSize);
620
0
        GDALExtendedDataType::CopyValue(pNoData, m_oType, m_pabyNoData,
621
0
                                        m_oType);
622
0
    }
623
0
}
624
625
/************************************************************************/
626
/*                      ZarrArray::BlockTranspose()                     */
627
/************************************************************************/
628
629
void ZarrArray::BlockTranspose(const ZarrByteVectorQuickResize &abySrc,
630
                               ZarrByteVectorQuickResize &abyDst,
631
                               bool bDecode) const
632
0
{
633
    // Perform transposition
634
0
    const size_t nDims = m_anBlockSize.size();
635
0
    const size_t nSourceSize =
636
0
        m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
637
638
0
    struct Stack
639
0
    {
640
0
        size_t nIters = 0;
641
0
        const GByte *src_ptr = nullptr;
642
0
        GByte *dst_ptr = nullptr;
643
0
        size_t src_inc_offset = 0;
644
0
        size_t dst_inc_offset = 0;
645
0
    };
646
647
0
    std::vector<Stack> stack(nDims);
648
0
    stack.emplace_back(
649
0
        Stack());  // to make gcc 9.3 -O2 -Wnull-dereference happy
650
651
0
    if (bDecode)
652
0
    {
653
0
        stack[0].src_inc_offset = nSourceSize;
654
0
        for (size_t i = 1; i < nDims; ++i)
655
0
        {
656
0
            stack[i].src_inc_offset = stack[i - 1].src_inc_offset *
657
0
                                      static_cast<size_t>(m_anBlockSize[i - 1]);
658
0
        }
659
660
0
        stack[nDims - 1].dst_inc_offset = nSourceSize;
661
0
        for (size_t i = nDims - 1; i > 0;)
662
0
        {
663
0
            --i;
664
0
            stack[i].dst_inc_offset = stack[i + 1].dst_inc_offset *
665
0
                                      static_cast<size_t>(m_anBlockSize[i + 1]);
666
0
        }
667
0
    }
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
0
    stack[0].src_ptr = abySrc.data();
687
0
    stack[0].dst_ptr = &abyDst[0];
688
689
0
    size_t dimIdx = 0;
690
0
lbl_next_depth:
691
0
    if (dimIdx == nDims)
692
0
    {
693
0
        void *dst_ptr = stack[nDims].dst_ptr;
694
0
        const void *src_ptr = stack[nDims].src_ptr;
695
0
        if (nSourceSize == 1)
696
0
            *stack[nDims].dst_ptr = *stack[nDims].src_ptr;
697
0
        else if (nSourceSize == 2)
698
0
            *static_cast<uint16_t *>(dst_ptr) =
699
0
                *static_cast<const uint16_t *>(src_ptr);
700
0
        else if (nSourceSize == 4)
701
0
            *static_cast<uint32_t *>(dst_ptr) =
702
0
                *static_cast<const uint32_t *>(src_ptr);
703
0
        else if (nSourceSize == 8)
704
0
            *static_cast<uint64_t *>(dst_ptr) =
705
0
                *static_cast<const uint64_t *>(src_ptr);
706
0
        else
707
0
            memcpy(dst_ptr, src_ptr, nSourceSize);
708
0
    }
709
0
    else
710
0
    {
711
0
        stack[dimIdx].nIters = static_cast<size_t>(m_anBlockSize[dimIdx]);
712
0
        while (true)
713
0
        {
714
0
            dimIdx++;
715
0
            stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
716
0
            stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
717
0
            goto lbl_next_depth;
718
0
        lbl_return_to_caller:
719
0
            dimIdx--;
720
0
            if ((--stack[dimIdx].nIters) == 0)
721
0
                break;
722
0
            stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
723
0
            stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
724
0
        }
725
0
    }
726
0
    if (dimIdx > 0)
727
0
        goto lbl_return_to_caller;
728
0
}
729
730
/************************************************************************/
731
/*                        DecodeSourceElt()                             */
732
/************************************************************************/
733
734
/* static */
735
void ZarrArray::DecodeSourceElt(const std::vector<DtypeElt> &elts,
736
                                const GByte *pSrc, GByte *pDst)
737
0
{
738
0
    for (const auto &elt : elts)
739
0
    {
740
0
        if (elt.nativeType == DtypeElt::NativeType::STRING_UNICODE)
741
0
        {
742
0
            char *ptr;
743
0
            char **pDstPtr = reinterpret_cast<char **>(pDst + elt.gdalOffset);
744
0
            memcpy(&ptr, pDstPtr, sizeof(ptr));
745
0
            VSIFree(ptr);
746
747
0
            char *pDstStr = UCS4ToUTF8(pSrc + elt.nativeOffset, elt.nativeSize,
748
0
                                       elt.needByteSwapping);
749
0
            memcpy(pDstPtr, &pDstStr, sizeof(pDstStr));
750
0
        }
751
0
        else if (elt.needByteSwapping)
752
0
        {
753
0
            if (elt.nativeSize == 2)
754
0
            {
755
0
                uint16_t val;
756
0
                memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
757
0
                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
0
                else
766
0
                {
767
0
                    *reinterpret_cast<uint16_t *>(pDst + elt.gdalOffset) =
768
0
                        CPL_SWAP16(val);
769
0
                }
770
0
            }
771
0
            else if (elt.nativeSize == 4)
772
0
            {
773
0
                uint32_t val;
774
0
                memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
775
0
                *reinterpret_cast<uint32_t *>(pDst + elt.gdalOffset) =
776
0
                    CPL_SWAP32(val);
777
0
            }
778
0
            else if (elt.nativeSize == 8)
779
0
            {
780
0
                if (elt.nativeType == DtypeElt::NativeType::COMPLEX_IEEEFP)
781
0
                {
782
0
                    uint32_t val;
783
0
                    memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
784
0
                    *reinterpret_cast<uint32_t *>(pDst + elt.gdalOffset) =
785
0
                        CPL_SWAP32(val);
786
0
                    memcpy(&val, pSrc + elt.nativeOffset + 4, sizeof(val));
787
0
                    *reinterpret_cast<uint32_t *>(pDst + elt.gdalOffset + 4) =
788
0
                        CPL_SWAP32(val);
789
0
                }
790
0
                else
791
0
                {
792
0
                    uint64_t val;
793
0
                    memcpy(&val, pSrc + elt.nativeOffset, sizeof(val));
794
0
                    *reinterpret_cast<uint64_t *>(pDst + elt.gdalOffset) =
795
0
                        CPL_SWAP64(val);
796
0
                }
797
0
            }
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
0
        }
813
0
        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
0
        else if (elt.nativeType == DtypeElt::NativeType::STRING_ASCII)
830
0
        {
831
0
            char *ptr;
832
0
            char **pDstPtr = reinterpret_cast<char **>(pDst + elt.gdalOffset);
833
0
            memcpy(&ptr, pDstPtr, sizeof(ptr));
834
0
            VSIFree(ptr);
835
836
0
            char *pDstStr = static_cast<char *>(CPLMalloc(elt.nativeSize + 1));
837
0
            memcpy(pDstStr, pSrc + elt.nativeOffset, elt.nativeSize);
838
0
            pDstStr[elt.nativeSize] = 0;
839
0
            memcpy(pDstPtr, &pDstStr, sizeof(pDstStr));
840
0
        }
841
0
        else
842
0
        {
843
0
            CPLAssert(elt.nativeSize == elt.gdalSize);
844
0
            memcpy(pDst + elt.gdalOffset, pSrc + elt.nativeOffset,
845
0
                   elt.nativeSize);
846
0
        }
847
0
    }
848
0
}
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
0
{
1010
0
    if (!CheckValidAndErrorOutIfNot())
1011
0
        return false;
1012
1013
0
    if (!AllocateWorkingBuffers())
1014
0
        return false;
1015
1016
    // Need to be kept in top-level scope
1017
0
    std::vector<GUInt64> arrayStartIdxMod;
1018
0
    std::vector<GInt64> arrayStepMod;
1019
0
    std::vector<GPtrDiff_t> bufferStrideMod;
1020
1021
0
    const size_t nDims = m_aoDims.size();
1022
0
    bool negativeStep = false;
1023
0
    for (size_t i = 0; i < nDims; ++i)
1024
0
    {
1025
0
        if (arrayStep[i] < 0)
1026
0
        {
1027
0
            negativeStep = true;
1028
0
            break;
1029
0
        }
1030
0
    }
1031
1032
    // const auto eBufferDT = bufferDataType.GetNumericDataType();
1033
0
    const auto nBufferDTSize = static_cast<int>(bufferDataType.GetSize());
1034
1035
    // Make sure that arrayStep[i] are positive for sake of simplicity
1036
0
    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
0
    std::vector<uint64_t> indicesOuterLoop(nDims + 1);
1074
0
    std::vector<GByte *> dstPtrStackOuterLoop(nDims + 1);
1075
1076
0
    std::vector<uint64_t> indicesInnerLoop(nDims + 1);
1077
0
    std::vector<GByte *> dstPtrStackInnerLoop(nDims + 1);
1078
1079
0
    std::vector<GPtrDiff_t> dstBufferStrideBytes;
1080
0
    for (size_t i = 0; i < nDims; ++i)
1081
0
    {
1082
0
        dstBufferStrideBytes.push_back(bufferStride[i] *
1083
0
                                       static_cast<GPtrDiff_t>(nBufferDTSize));
1084
0
    }
1085
0
    dstBufferStrideBytes.push_back(0);
1086
1087
0
    const auto nDTSize = m_oType.GetSize();
1088
1089
0
    std::vector<uint64_t> tileIndices(nDims);
1090
0
    const size_t nSourceSize =
1091
0
        m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
1092
1093
0
    std::vector<size_t> countInnerLoopInit(nDims + 1, 1);
1094
0
    std::vector<size_t> countInnerLoop(nDims);
1095
1096
0
    const bool bBothAreNumericDT = m_oType.GetClass() == GEDTC_NUMERIC &&
1097
0
                                   bufferDataType.GetClass() == GEDTC_NUMERIC;
1098
0
    const bool bSameNumericDT =
1099
0
        bBothAreNumericDT &&
1100
0
        m_oType.GetNumericDataType() == bufferDataType.GetNumericDataType();
1101
0
    const auto nSameDTSize = bSameNumericDT ? m_oType.GetSize() : 0;
1102
0
    const bool bSameCompoundAndNoDynamicMem =
1103
0
        m_oType.GetClass() == GEDTC_COMPOUND && m_oType == bufferDataType &&
1104
0
        !m_oType.NeedsFreeDynamicMemory();
1105
0
    std::vector<GByte> abyTargetNoData;
1106
0
    bool bNoDataIsZero = false;
1107
1108
0
    size_t dimIdx = 0;
1109
0
    dstPtrStackOuterLoop[0] = static_cast<GByte *>(pDstBuffer);
1110
0
lbl_next_depth:
1111
0
    if (dimIdx == nDims)
1112
0
    {
1113
0
        size_t dimIdxSubLoop = 0;
1114
0
        dstPtrStackInnerLoop[0] = dstPtrStackOuterLoop[nDims];
1115
0
        bool bEmptyTile = false;
1116
1117
0
        const GByte *pabySrcTile = m_abyDecodedTileData.empty()
1118
0
                                       ? m_abyRawTileData.data()
1119
0
                                       : m_abyDecodedTileData.data();
1120
0
        bool bMatchFoundInMapTileIndexToCachedTile = false;
1121
1122
        // Use cache built by IAdviseRead() if possible
1123
0
        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
0
        if (!bMatchFoundInMapTileIndexToCachedTile)
1154
0
        {
1155
0
            if (!tileIndices.empty() && tileIndices == m_anCachedTiledIndices)
1156
0
            {
1157
0
                if (!m_bCachedTiledValid)
1158
0
                    return false;
1159
0
                bEmptyTile = m_bCachedTiledEmpty;
1160
0
            }
1161
0
            else
1162
0
            {
1163
0
                if (!FlushDirtyTile())
1164
0
                    return false;
1165
1166
0
                m_anCachedTiledIndices = tileIndices;
1167
0
                m_bCachedTiledValid =
1168
0
                    LoadTileData(tileIndices.data(), bEmptyTile);
1169
0
                if (!m_bCachedTiledValid)
1170
0
                {
1171
0
                    return false;
1172
0
                }
1173
0
                m_bCachedTiledEmpty = bEmptyTile;
1174
0
            }
1175
1176
0
            pabySrcTile = m_abyDecodedTileData.empty()
1177
0
                              ? m_abyRawTileData.data()
1178
0
                              : m_abyDecodedTileData.data();
1179
0
        }
1180
0
        const size_t nSrcDTSize =
1181
0
            m_abyDecodedTileData.empty() ? nSourceSize : nDTSize;
1182
1183
0
        for (size_t i = 0; i < nDims; ++i)
1184
0
        {
1185
0
            countInnerLoopInit[i] = 1;
1186
0
            if (arrayStep[i] != 0)
1187
0
            {
1188
0
                const auto nextBlockIdx =
1189
0
                    std::min((1 + indicesOuterLoop[i] / m_anBlockSize[i]) *
1190
0
                                 m_anBlockSize[i],
1191
0
                             arrayStartIdx[i] + count[i] * arrayStep[i]);
1192
0
                countInnerLoopInit[i] = static_cast<size_t>(
1193
0
                    (nextBlockIdx - indicesOuterLoop[i] + arrayStep[i] - 1) /
1194
0
                    arrayStep[i]);
1195
0
            }
1196
0
        }
1197
1198
0
        if (bEmptyTile && bBothAreNumericDT && abyTargetNoData.empty())
1199
0
        {
1200
0
            abyTargetNoData.resize(nBufferDTSize);
1201
0
            if (m_pabyNoData)
1202
0
            {
1203
0
                GDALExtendedDataType::CopyValue(
1204
0
                    m_pabyNoData, m_oType, &abyTargetNoData[0], bufferDataType);
1205
0
                bNoDataIsZero = true;
1206
0
                for (size_t i = 0; i < abyTargetNoData.size(); ++i)
1207
0
                {
1208
0
                    if (abyTargetNoData[i] != 0)
1209
0
                        bNoDataIsZero = false;
1210
0
                }
1211
0
            }
1212
0
            else
1213
0
            {
1214
0
                bNoDataIsZero = true;
1215
0
                GByte zero = 0;
1216
0
                GDALCopyWords(&zero, GDT_Byte, 0, &abyTargetNoData[0],
1217
0
                              bufferDataType.GetNumericDataType(), 0, 1);
1218
0
            }
1219
0
        }
1220
1221
0
    lbl_next_depth_inner_loop:
1222
0
        if (nDims == 0 || dimIdxSubLoop == nDims - 1)
1223
0
        {
1224
0
            indicesInnerLoop[dimIdxSubLoop] = indicesOuterLoop[dimIdxSubLoop];
1225
0
            void *dst_ptr = dstPtrStackInnerLoop[dimIdxSubLoop];
1226
1227
0
            if (m_bUseOptimizedCodePaths && bEmptyTile && bBothAreNumericDT &&
1228
0
                bNoDataIsZero &&
1229
0
                nBufferDTSize == dstBufferStrideBytes[dimIdxSubLoop])
1230
0
            {
1231
0
                memset(dst_ptr, 0,
1232
0
                       nBufferDTSize * countInnerLoopInit[dimIdxSubLoop]);
1233
0
                goto end_inner_loop;
1234
0
            }
1235
0
            else if (m_bUseOptimizedCodePaths && bEmptyTile &&
1236
0
                     !abyTargetNoData.empty() && bBothAreNumericDT &&
1237
0
                     dstBufferStrideBytes[dimIdxSubLoop] <
1238
0
                         std::numeric_limits<int>::max())
1239
0
            {
1240
0
                GDALCopyWords64(
1241
0
                    abyTargetNoData.data(), bufferDataType.GetNumericDataType(),
1242
0
                    0, dst_ptr, bufferDataType.GetNumericDataType(),
1243
0
                    static_cast<int>(dstBufferStrideBytes[dimIdxSubLoop]),
1244
0
                    static_cast<GPtrDiff_t>(countInnerLoopInit[dimIdxSubLoop]));
1245
0
                goto end_inner_loop;
1246
0
            }
1247
0
            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
0
            size_t nOffset = 0;
1326
0
            for (size_t i = 0; i < nDims; i++)
1327
0
            {
1328
0
                nOffset = static_cast<size_t>(
1329
0
                    nOffset * m_anBlockSize[i] +
1330
0
                    (indicesInnerLoop[i] - tileIndices[i] * m_anBlockSize[i]));
1331
0
            }
1332
0
            const GByte *src_ptr = pabySrcTile + nOffset * nSrcDTSize;
1333
0
            const auto step = nDims == 0 ? 0 : arrayStep[dimIdxSubLoop];
1334
1335
0
            if (m_bUseOptimizedCodePaths && bBothAreNumericDT &&
1336
0
                step <= static_cast<GIntBig>(std::numeric_limits<int>::max() /
1337
0
                                             nDTSize) &&
1338
0
                dstBufferStrideBytes[dimIdxSubLoop] <=
1339
0
                    std::numeric_limits<int>::max())
1340
0
            {
1341
0
                GDALCopyWords64(
1342
0
                    src_ptr, m_oType.GetNumericDataType(),
1343
0
                    static_cast<int>(step * nDTSize), dst_ptr,
1344
0
                    bufferDataType.GetNumericDataType(),
1345
0
                    static_cast<int>(dstBufferStrideBytes[dimIdxSubLoop]),
1346
0
                    static_cast<GPtrDiff_t>(countInnerLoopInit[dimIdxSubLoop]));
1347
1348
0
                goto end_inner_loop;
1349
0
            }
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
0
        else
1422
0
        {
1423
            // This level of loop loops over individual samples, within a
1424
            // block
1425
0
            indicesInnerLoop[dimIdxSubLoop] = indicesOuterLoop[dimIdxSubLoop];
1426
0
            countInnerLoop[dimIdxSubLoop] = countInnerLoopInit[dimIdxSubLoop];
1427
0
            while (true)
1428
0
            {
1429
0
                dimIdxSubLoop++;
1430
0
                dstPtrStackInnerLoop[dimIdxSubLoop] =
1431
0
                    dstPtrStackInnerLoop[dimIdxSubLoop - 1];
1432
0
                goto lbl_next_depth_inner_loop;
1433
0
            lbl_return_to_caller_inner_loop:
1434
0
                dimIdxSubLoop--;
1435
0
                --countInnerLoop[dimIdxSubLoop];
1436
0
                if (countInnerLoop[dimIdxSubLoop] == 0)
1437
0
                {
1438
0
                    break;
1439
0
                }
1440
0
                indicesInnerLoop[dimIdxSubLoop] += arrayStep[dimIdxSubLoop];
1441
0
                dstPtrStackInnerLoop[dimIdxSubLoop] +=
1442
0
                    dstBufferStrideBytes[dimIdxSubLoop];
1443
0
            }
1444
0
        }
1445
0
    end_inner_loop:
1446
0
        if (dimIdxSubLoop > 0)
1447
0
            goto lbl_return_to_caller_inner_loop;
1448
0
    }
1449
0
    else
1450
0
    {
1451
        // This level of loop loops over blocks
1452
0
        indicesOuterLoop[dimIdx] = arrayStartIdx[dimIdx];
1453
0
        tileIndices[dimIdx] = indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1454
0
        while (true)
1455
0
        {
1456
0
            dimIdx++;
1457
0
            dstPtrStackOuterLoop[dimIdx] = dstPtrStackOuterLoop[dimIdx - 1];
1458
0
            goto lbl_next_depth;
1459
0
        lbl_return_to_caller:
1460
0
            dimIdx--;
1461
0
            if (count[dimIdx] == 1 || arrayStep[dimIdx] == 0)
1462
0
                break;
1463
1464
0
            size_t nIncr;
1465
0
            if (static_cast<GUInt64>(arrayStep[dimIdx]) < m_anBlockSize[dimIdx])
1466
0
            {
1467
                // Compute index at next block boundary
1468
0
                auto newIdx =
1469
0
                    indicesOuterLoop[dimIdx] +
1470
0
                    (m_anBlockSize[dimIdx] -
1471
0
                     (indicesOuterLoop[dimIdx] % m_anBlockSize[dimIdx]));
1472
                // And round up compared to arrayStartIdx, arrayStep
1473
0
                nIncr = static_cast<size_t>((newIdx - indicesOuterLoop[dimIdx] +
1474
0
                                             arrayStep[dimIdx] - 1) /
1475
0
                                            arrayStep[dimIdx]);
1476
0
            }
1477
0
            else
1478
0
            {
1479
0
                nIncr = 1;
1480
0
            }
1481
0
            indicesOuterLoop[dimIdx] += nIncr * arrayStep[dimIdx];
1482
0
            if (indicesOuterLoop[dimIdx] >
1483
0
                arrayStartIdx[dimIdx] + (count[dimIdx] - 1) * arrayStep[dimIdx])
1484
0
                break;
1485
0
            dstPtrStackOuterLoop[dimIdx] +=
1486
0
                bufferStride[dimIdx] *
1487
0
                static_cast<GPtrDiff_t>(nIncr * nBufferDTSize);
1488
0
            tileIndices[dimIdx] =
1489
0
                indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1490
0
        }
1491
0
    }
1492
0
    if (dimIdx > 0)
1493
0
        goto lbl_return_to_caller;
1494
1495
0
    return true;
1496
0
}
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
0
{
1507
0
    if (!CheckValidAndErrorOutIfNot())
1508
0
        return false;
1509
1510
0
    if (!AllocateWorkingBuffers())
1511
0
        return false;
1512
1513
0
    m_oMapTileIndexToCachedTile.clear();
1514
1515
    // Need to be kept in top-level scope
1516
0
    std::vector<GUInt64> arrayStartIdxMod;
1517
0
    std::vector<GInt64> arrayStepMod;
1518
0
    std::vector<GPtrDiff_t> bufferStrideMod;
1519
1520
0
    const size_t nDims = m_aoDims.size();
1521
0
    bool negativeStep = false;
1522
0
    bool bWriteWholeTileInit = true;
1523
0
    for (size_t i = 0; i < nDims; ++i)
1524
0
    {
1525
0
        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
0
        else if (arrayStep[i] != 1 && count[i] > 1)
1532
0
            bWriteWholeTileInit = false;
1533
0
    }
1534
1535
0
    const auto nBufferDTSize = static_cast<int>(bufferDataType.GetSize());
1536
1537
    // Make sure that arrayStep[i] are positive for sake of simplicity
1538
0
    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
0
    std::vector<uint64_t> indicesOuterLoop(nDims + 1);
1576
0
    std::vector<const GByte *> srcPtrStackOuterLoop(nDims + 1);
1577
1578
0
    std::vector<size_t> offsetDstBuffer(nDims + 1);
1579
0
    std::vector<const GByte *> srcPtrStackInnerLoop(nDims + 1);
1580
1581
0
    std::vector<GPtrDiff_t> srcBufferStrideBytes;
1582
0
    for (size_t i = 0; i < nDims; ++i)
1583
0
    {
1584
0
        srcBufferStrideBytes.push_back(bufferStride[i] *
1585
0
                                       static_cast<GPtrDiff_t>(nBufferDTSize));
1586
0
    }
1587
0
    srcBufferStrideBytes.push_back(0);
1588
1589
0
    const auto nDTSize = m_oType.GetSize();
1590
1591
0
    std::vector<uint64_t> tileIndices(nDims);
1592
0
    const size_t nNativeSize =
1593
0
        m_aoDtypeElts.back().nativeOffset + m_aoDtypeElts.back().nativeSize;
1594
1595
0
    std::vector<size_t> countInnerLoopInit(nDims + 1, 1);
1596
0
    std::vector<size_t> countInnerLoop(nDims);
1597
1598
0
    const bool bBothAreNumericDT = m_oType.GetClass() == GEDTC_NUMERIC &&
1599
0
                                   bufferDataType.GetClass() == GEDTC_NUMERIC;
1600
0
    const bool bSameNumericDT =
1601
0
        bBothAreNumericDT &&
1602
0
        m_oType.GetNumericDataType() == bufferDataType.GetNumericDataType();
1603
0
    const auto nSameDTSize = bSameNumericDT ? m_oType.GetSize() : 0;
1604
0
    const bool bSameCompoundAndNoDynamicMem =
1605
0
        m_oType.GetClass() == GEDTC_COMPOUND && m_oType == bufferDataType &&
1606
0
        !m_oType.NeedsFreeDynamicMemory();
1607
1608
0
    size_t dimIdx = 0;
1609
0
    size_t dimIdxForCopy = nDims == 0 ? 0 : nDims - 1;
1610
0
    if (nDims)
1611
0
    {
1612
0
        while (dimIdxForCopy > 0 && count[dimIdxForCopy] == 1)
1613
0
            --dimIdxForCopy;
1614
0
    }
1615
1616
0
    srcPtrStackOuterLoop[0] = static_cast<const GByte *>(pSrcBuffer);
1617
0
lbl_next_depth:
1618
0
    if (dimIdx == nDims)
1619
0
    {
1620
0
        bool bWriteWholeTile = bWriteWholeTileInit;
1621
0
        bool bPartialTile = false;
1622
0
        for (size_t i = 0; i < nDims; ++i)
1623
0
        {
1624
0
            countInnerLoopInit[i] = 1;
1625
0
            if (arrayStep[i] != 0)
1626
0
            {
1627
0
                const auto nextBlockIdx =
1628
0
                    std::min((1 + indicesOuterLoop[i] / m_anBlockSize[i]) *
1629
0
                                 m_anBlockSize[i],
1630
0
                             arrayStartIdx[i] + count[i] * arrayStep[i]);
1631
0
                countInnerLoopInit[i] = static_cast<size_t>(
1632
0
                    (nextBlockIdx - indicesOuterLoop[i] + arrayStep[i] - 1) /
1633
0
                    arrayStep[i]);
1634
0
            }
1635
0
            if (bWriteWholeTile)
1636
0
            {
1637
0
                const bool bWholePartialTileThisDim =
1638
0
                    indicesOuterLoop[i] == 0 &&
1639
0
                    countInnerLoopInit[i] == m_aoDims[i]->GetSize();
1640
0
                bWriteWholeTile = (countInnerLoopInit[i] == m_anBlockSize[i] ||
1641
0
                                   bWholePartialTileThisDim);
1642
0
                if (bWholePartialTileThisDim)
1643
0
                {
1644
0
                    bPartialTile = true;
1645
0
                }
1646
0
            }
1647
0
        }
1648
1649
0
        size_t dimIdxSubLoop = 0;
1650
0
        srcPtrStackInnerLoop[0] = srcPtrStackOuterLoop[nDims];
1651
0
        const size_t nCacheDTSize =
1652
0
            m_abyDecodedTileData.empty() ? nNativeSize : nDTSize;
1653
0
        auto &abyTile = m_abyDecodedTileData.empty() ? m_abyRawTileData
1654
0
                                                     : m_abyDecodedTileData;
1655
1656
0
        if (!tileIndices.empty() && tileIndices == m_anCachedTiledIndices)
1657
0
        {
1658
0
            if (!m_bCachedTiledValid)
1659
0
                return false;
1660
0
        }
1661
0
        else
1662
0
        {
1663
0
            if (!FlushDirtyTile())
1664
0
                return false;
1665
1666
0
            m_anCachedTiledIndices = tileIndices;
1667
0
            m_bCachedTiledValid = true;
1668
1669
0
            if (bWriteWholeTile)
1670
0
            {
1671
0
                if (bPartialTile)
1672
0
                {
1673
0
                    DeallocateDecodedTileData();
1674
0
                    memset(&abyTile[0], 0, abyTile.size());
1675
0
                }
1676
0
            }
1677
0
            else
1678
0
            {
1679
                // If we don't write the whole tile, we need to fetch a
1680
                // potentially existing one.
1681
0
                bool bEmptyTile = false;
1682
0
                m_bCachedTiledValid =
1683
0
                    LoadTileData(tileIndices.data(), bEmptyTile);
1684
0
                if (!m_bCachedTiledValid)
1685
0
                {
1686
0
                    return false;
1687
0
                }
1688
1689
0
                if (bEmptyTile)
1690
0
                {
1691
0
                    DeallocateDecodedTileData();
1692
1693
0
                    if (m_pabyNoData == nullptr)
1694
0
                    {
1695
0
                        memset(&abyTile[0], 0, abyTile.size());
1696
0
                    }
1697
0
                    else
1698
0
                    {
1699
0
                        const size_t nElts = abyTile.size() / nCacheDTSize;
1700
0
                        GByte *dstPtr = &abyTile[0];
1701
0
                        if (m_oType.GetClass() == GEDTC_NUMERIC)
1702
0
                        {
1703
0
                            GDALCopyWords64(
1704
0
                                m_pabyNoData, m_oType.GetNumericDataType(), 0,
1705
0
                                dstPtr, m_oType.GetNumericDataType(),
1706
0
                                static_cast<int>(m_oType.GetSize()),
1707
0
                                static_cast<GPtrDiff_t>(nElts));
1708
0
                        }
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
0
                    }
1719
0
                }
1720
0
            }
1721
0
        }
1722
0
        m_bDirtyTile = true;
1723
0
        m_bCachedTiledEmpty = false;
1724
0
        if (nDims)
1725
0
            offsetDstBuffer[0] = static_cast<size_t>(
1726
0
                indicesOuterLoop[0] - tileIndices[0] * m_anBlockSize[0]);
1727
1728
0
        GByte *pabyTile = &abyTile[0];
1729
1730
0
    lbl_next_depth_inner_loop:
1731
0
        if (dimIdxSubLoop == dimIdxForCopy)
1732
0
        {
1733
0
            size_t nOffset = offsetDstBuffer[dimIdxSubLoop];
1734
0
            GInt64 step = nDims == 0 ? 0 : arrayStep[dimIdxSubLoop];
1735
0
            for (size_t i = dimIdxSubLoop + 1; i < nDims; ++i)
1736
0
            {
1737
0
                nOffset = static_cast<size_t>(
1738
0
                    nOffset * m_anBlockSize[i] +
1739
0
                    (indicesOuterLoop[i] - tileIndices[i] * m_anBlockSize[i]));
1740
0
                step *= m_anBlockSize[i];
1741
0
            }
1742
0
            const void *src_ptr = srcPtrStackInnerLoop[dimIdxSubLoop];
1743
0
            GByte *dst_ptr = pabyTile + nOffset * nCacheDTSize;
1744
1745
0
            if (m_bUseOptimizedCodePaths && bBothAreNumericDT)
1746
0
            {
1747
0
                if (countInnerLoopInit[dimIdxSubLoop] == 1 && bSameNumericDT)
1748
0
                {
1749
0
                    void *dst_ptr_v = dst_ptr;
1750
0
                    if (nSameDTSize == 1)
1751
0
                        *static_cast<uint8_t *>(dst_ptr_v) =
1752
0
                            *static_cast<const uint8_t *>(src_ptr);
1753
0
                    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
0
                    else if (nSameDTSize == 4)
1759
0
                    {
1760
0
                        *static_cast<uint32_t *>(dst_ptr_v) =
1761
0
                            *static_cast<const uint32_t *>(src_ptr);
1762
0
                    }
1763
0
                    else if (nSameDTSize == 8)
1764
0
                    {
1765
0
                        *static_cast<uint64_t *>(dst_ptr_v) =
1766
0
                            *static_cast<const uint64_t *>(src_ptr);
1767
0
                    }
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
0
                }
1780
0
                else if (step <=
1781
0
                             static_cast<GIntBig>(
1782
0
                                 std::numeric_limits<int>::max() / nDTSize) &&
1783
0
                         srcBufferStrideBytes[dimIdxSubLoop] <=
1784
0
                             std::numeric_limits<int>::max())
1785
0
                {
1786
0
                    GDALCopyWords64(
1787
0
                        src_ptr, bufferDataType.GetNumericDataType(),
1788
0
                        static_cast<int>(srcBufferStrideBytes[dimIdxSubLoop]),
1789
0
                        dst_ptr, m_oType.GetNumericDataType(),
1790
0
                        static_cast<int>(step * nDTSize),
1791
0
                        static_cast<GPtrDiff_t>(
1792
0
                            countInnerLoopInit[dimIdxSubLoop]));
1793
0
                }
1794
0
                goto end_inner_loop;
1795
0
            }
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
0
        else
1896
0
        {
1897
            // This level of loop loops over individual samples, within a
1898
            // block
1899
0
            countInnerLoop[dimIdxSubLoop] = countInnerLoopInit[dimIdxSubLoop];
1900
0
            while (true)
1901
0
            {
1902
0
                dimIdxSubLoop++;
1903
0
                srcPtrStackInnerLoop[dimIdxSubLoop] =
1904
0
                    srcPtrStackInnerLoop[dimIdxSubLoop - 1];
1905
0
                offsetDstBuffer[dimIdxSubLoop] =
1906
0
                    static_cast<size_t>(offsetDstBuffer[dimIdxSubLoop - 1] *
1907
0
                                            m_anBlockSize[dimIdxSubLoop] +
1908
0
                                        (indicesOuterLoop[dimIdxSubLoop] -
1909
0
                                         tileIndices[dimIdxSubLoop] *
1910
0
                                             m_anBlockSize[dimIdxSubLoop]));
1911
0
                goto lbl_next_depth_inner_loop;
1912
0
            lbl_return_to_caller_inner_loop:
1913
0
                dimIdxSubLoop--;
1914
0
                --countInnerLoop[dimIdxSubLoop];
1915
0
                if (countInnerLoop[dimIdxSubLoop] == 0)
1916
0
                {
1917
0
                    break;
1918
0
                }
1919
0
                srcPtrStackInnerLoop[dimIdxSubLoop] +=
1920
0
                    srcBufferStrideBytes[dimIdxSubLoop];
1921
0
                offsetDstBuffer[dimIdxSubLoop] +=
1922
0
                    static_cast<size_t>(arrayStep[dimIdxSubLoop]);
1923
0
            }
1924
0
        }
1925
0
    end_inner_loop:
1926
0
        if (dimIdxSubLoop > 0)
1927
0
            goto lbl_return_to_caller_inner_loop;
1928
0
    }
1929
0
    else
1930
0
    {
1931
        // This level of loop loops over blocks
1932
0
        indicesOuterLoop[dimIdx] = arrayStartIdx[dimIdx];
1933
0
        tileIndices[dimIdx] = indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1934
0
        while (true)
1935
0
        {
1936
0
            dimIdx++;
1937
0
            srcPtrStackOuterLoop[dimIdx] = srcPtrStackOuterLoop[dimIdx - 1];
1938
0
            goto lbl_next_depth;
1939
0
        lbl_return_to_caller:
1940
0
            dimIdx--;
1941
0
            if (count[dimIdx] == 1 || arrayStep[dimIdx] == 0)
1942
0
                break;
1943
1944
0
            size_t nIncr;
1945
0
            if (static_cast<GUInt64>(arrayStep[dimIdx]) < m_anBlockSize[dimIdx])
1946
0
            {
1947
                // Compute index at next block boundary
1948
0
                auto newIdx =
1949
0
                    indicesOuterLoop[dimIdx] +
1950
0
                    (m_anBlockSize[dimIdx] -
1951
0
                     (indicesOuterLoop[dimIdx] % m_anBlockSize[dimIdx]));
1952
                // And round up compared to arrayStartIdx, arrayStep
1953
0
                nIncr = static_cast<size_t>((newIdx - indicesOuterLoop[dimIdx] +
1954
0
                                             arrayStep[dimIdx] - 1) /
1955
0
                                            arrayStep[dimIdx]);
1956
0
            }
1957
0
            else
1958
0
            {
1959
0
                nIncr = 1;
1960
0
            }
1961
0
            indicesOuterLoop[dimIdx] += nIncr * arrayStep[dimIdx];
1962
0
            if (indicesOuterLoop[dimIdx] >
1963
0
                arrayStartIdx[dimIdx] + (count[dimIdx] - 1) * arrayStep[dimIdx])
1964
0
                break;
1965
0
            srcPtrStackOuterLoop[dimIdx] +=
1966
0
                bufferStride[dimIdx] *
1967
0
                static_cast<GPtrDiff_t>(nIncr * nBufferDTSize);
1968
0
            tileIndices[dimIdx] =
1969
0
                indicesOuterLoop[dimIdx] / m_anBlockSize[dimIdx];
1970
0
        }
1971
0
    }
1972
0
    if (dimIdx > 0)
1973
0
        goto lbl_return_to_caller;
1974
1975
0
    return true;
1976
0
}
1977
1978
/************************************************************************/
1979
/*                   ZarrArray::IsEmptyTile()                           */
1980
/************************************************************************/
1981
1982
bool ZarrArray::IsEmptyTile(const ZarrByteVectorQuickResize &abyTile) const
1983
0
{
1984
0
    if (m_pabyNoData == nullptr || (m_oType.GetClass() == GEDTC_NUMERIC &&
1985
0
                                    GetNoDataValueAsDouble() == 0.0))
1986
0
    {
1987
0
        const size_t nBytes = abyTile.size();
1988
0
        size_t i = 0;
1989
0
        for (; i + (sizeof(size_t) - 1) < nBytes; i += sizeof(size_t))
1990
0
        {
1991
0
            if (*reinterpret_cast<const size_t *>(abyTile.data() + i) != 0)
1992
0
            {
1993
0
                return false;
1994
0
            }
1995
0
        }
1996
0
        for (; i < nBytes; ++i)
1997
0
        {
1998
0
            if (abyTile[i] != 0)
1999
0
            {
2000
0
                return false;
2001
0
            }
2002
0
        }
2003
0
        return true;
2004
0
    }
2005
0
    else if (m_oType.GetClass() == GEDTC_NUMERIC &&
2006
0
             !GDALDataTypeIsComplex(m_oType.GetNumericDataType()))
2007
0
    {
2008
0
        const int nDTSize = static_cast<int>(m_oType.GetSize());
2009
0
        const size_t nElts = abyTile.size() / nDTSize;
2010
0
        const auto eDT = m_oType.GetNumericDataType();
2011
0
        return GDALBufferHasOnlyNoData(abyTile.data(), GetNoDataValueAsDouble(),
2012
0
                                       nElts,        // nWidth
2013
0
                                       1,            // nHeight
2014
0
                                       nElts,        // nLineStride
2015
0
                                       1,            // nComponents
2016
0
                                       nDTSize * 8,  // nBitsPerSample
2017
0
                                       GDALDataTypeIsInteger(eDT)
2018
0
                                           ? (GDALDataTypeIsSigned(eDT)
2019
0
                                                  ? GSF_SIGNED_INT
2020
0
                                                  : GSF_UNSIGNED_INT)
2021
0
                                           : GSF_FLOATING_POINT);
2022
0
    }
2023
0
    return false;
2024
0
}
2025
2026
/************************************************************************/
2027
/*                  ZarrArray::OpenTilePresenceCache()                  */
2028
/************************************************************************/
2029
2030
std::shared_ptr<GDALMDArray>
2031
ZarrArray::OpenTilePresenceCache(bool bCanCreate) const
2032
0
{
2033
0
    if (m_bHasTriedCacheTilePresenceArray)
2034
0
        return m_poCacheTilePresenceArray;
2035
0
    m_bHasTriedCacheTilePresenceArray = true;
2036
2037
0
    if (m_nTotalTileCount == 1)
2038
0
        return nullptr;
2039
2040
0
    std::string osCacheFilename;
2041
0
    auto poRGCache = GetCacheRootGroup(bCanCreate, osCacheFilename);
2042
0
    if (!poRGCache)
2043
0
        return nullptr;
2044
2045
0
    const std::string osTilePresenceArrayName(MassageName(GetFullName()) +
2046
0
                                              "_tile_presence");
2047
0
    auto poTilePresenceArray = poRGCache->OpenMDArray(osTilePresenceArrayName);
2048
0
    const auto eByteDT = GDALExtendedDataType::Create(GDT_Byte);
2049
0
    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
0
    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
0
    else
2131
0
    {
2132
0
        return nullptr;
2133
0
    }
2134
2135
0
    m_poCacheTilePresenceArray = poTilePresenceArray;
2136
2137
0
    return poTilePresenceArray;
2138
0
}
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
0
{
2283
0
    if (!CheckValidAndErrorOutIfNot())
2284
0
        return nullptr;
2285
2286
0
    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
0
    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
0
    return m_oAttrGroup.CreateAttribute(osName, anDimensions, oDataType,
2299
0
                                        papszOptions);
2300
0
}
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
0
{
2327
0
    if (!CheckValidAndErrorOutIfNot())
2328
0
        return false;
2329
2330
0
    if (!m_bUpdatable)
2331
0
    {
2332
0
        return GDALPamMDArray::SetSpatialRef(poSRS);
2333
0
    }
2334
0
    m_poSRS.reset();
2335
0
    if (poSRS)
2336
0
        m_poSRS.reset(poSRS->Clone());
2337
0
    m_bSRSModified = true;
2338
0
    return true;
2339
0
}
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
0
{
2368
0
    if (pbHasOffset)
2369
0
        *pbHasOffset = m_bHasOffset;
2370
0
    if (peStorageType)
2371
0
        *peStorageType = GDT_Unknown;
2372
0
    return m_dfOffset;
2373
0
}
2374
2375
/************************************************************************/
2376
/*                       ZarrArray::GetScale()                          */
2377
/************************************************************************/
2378
2379
double ZarrArray::GetScale(bool *pbHasScale, GDALDataType *peStorageType) const
2380
0
{
2381
0
    if (pbHasScale)
2382
0
        *pbHasScale = m_bHasScale;
2383
0
    if (peStorageType)
2384
0
        *peStorageType = GDT_Unknown;
2385
0
    return m_dfScale;
2386
0
}
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
0
{
2409
0
    if (!CheckValidAndErrorOutIfNot())
2410
0
        return false;
2411
2412
0
    m_dfScale = dfScale;
2413
0
    m_bHasScale = true;
2414
0
    m_bScaleModified = true;
2415
0
    return true;
2416
0
}
2417
2418
/************************************************************************/
2419
/*                      GetDimensionTypeDirection()                     */
2420
/************************************************************************/
2421
2422
/* static */
2423
void ZarrArray::GetDimensionTypeDirection(CPLJSONObject &oAttributes,
2424
                                          std::string &osType,
2425
                                          std::string &osDirection)
2426
0
{
2427
0
    std::string osUnit;
2428
0
    const auto unit = oAttributes[CF_UNITS];
2429
0
    if (unit.GetType() == CPLJSONObject::Type::String)
2430
0
    {
2431
0
        osUnit = unit.ToString();
2432
0
    }
2433
2434
0
    const auto oStdName = oAttributes[CF_STD_NAME];
2435
0
    if (oStdName.GetType() == CPLJSONObject::Type::String)
2436
0
    {
2437
0
        const auto osStdName = oStdName.ToString();
2438
0
        if (osStdName == CF_PROJ_X_COORD || osStdName == CF_LONGITUDE_STD_NAME)
2439
0
        {
2440
0
            osType = GDAL_DIM_TYPE_HORIZONTAL_X;
2441
0
            oAttributes.Delete(CF_STD_NAME);
2442
0
            if (osUnit == CF_DEGREES_EAST)
2443
0
            {
2444
0
                osDirection = "EAST";
2445
0
            }
2446
0
        }
2447
0
        else if (osStdName == CF_PROJ_Y_COORD ||
2448
0
                 osStdName == CF_LATITUDE_STD_NAME)
2449
0
        {
2450
0
            osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
2451
0
            oAttributes.Delete(CF_STD_NAME);
2452
0
            if (osUnit == CF_DEGREES_NORTH)
2453
0
            {
2454
0
                osDirection = "NORTH";
2455
0
            }
2456
0
        }
2457
0
        else if (osStdName == "time")
2458
0
        {
2459
0
            osType = GDAL_DIM_TYPE_TEMPORAL;
2460
0
            oAttributes.Delete(CF_STD_NAME);
2461
0
        }
2462
0
    }
2463
2464
0
    const auto osAxis = oAttributes[CF_AXIS].ToString();
2465
0
    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
0
}
2482
2483
/************************************************************************/
2484
/*                      GetCoordinateVariables()                        */
2485
/************************************************************************/
2486
2487
std::vector<std::shared_ptr<GDALMDArray>>
2488
ZarrArray::GetCoordinateVariables() const
2489
0
{
2490
0
    if (!CheckValidAndErrorOutIfNot())
2491
0
        return {};
2492
2493
0
    std::vector<std::shared_ptr<GDALMDArray>> ret;
2494
0
    const auto poCoordinates = GetAttribute("coordinates");
2495
0
    if (poCoordinates &&
2496
0
        poCoordinates->GetDataType().GetClass() == GEDTC_STRING &&
2497
0
        poCoordinates->GetDimensionCount() == 0)
2498
0
    {
2499
0
        const char *pszCoordinates = poCoordinates->ReadAsString();
2500
0
        if (pszCoordinates)
2501
0
        {
2502
0
            auto poGroup = m_poGroupWeak.lock();
2503
0
            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
0
            else
2511
0
            {
2512
0
                const CPLStringList aosNames(
2513
0
                    CSLTokenizeString2(pszCoordinates, " ", 0));
2514
0
                for (int i = 0; i < aosNames.size(); i++)
2515
0
                {
2516
0
                    auto poCoordinateVar = poGroup->OpenMDArray(aosNames[i]);
2517
0
                    if (poCoordinateVar)
2518
0
                    {
2519
0
                        ret.emplace_back(poCoordinateVar);
2520
0
                    }
2521
0
                    else
2522
0
                    {
2523
0
                        CPLError(CE_Warning, CPLE_AppDefined,
2524
0
                                 "Cannot find variable corresponding to "
2525
0
                                 "coordinate %s",
2526
0
                                 aosNames[i]);
2527
0
                    }
2528
0
                }
2529
0
            }
2530
0
        }
2531
0
    }
2532
2533
0
    return ret;
2534
0
}
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
0
{
2718
0
    const auto crs = oAttributes[CRS_ATTRIBUTE_NAME];
2719
0
    std::shared_ptr<OGRSpatialReference> poSRS;
2720
0
    if (crs.GetType() == CPLJSONObject::Type::Object)
2721
0
    {
2722
0
        for (const char *key : {"url", "wkt", "projjson"})
2723
0
        {
2724
0
            const auto item = crs[key];
2725
0
            if (item.IsValid())
2726
0
            {
2727
0
                poSRS = std::make_shared<OGRSpatialReference>();
2728
0
                poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2729
0
                if (poSRS->SetFromUserInput(
2730
0
                        item.ToString().c_str(),
2731
0
                        OGRSpatialReference::
2732
0
                            SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
2733
0
                    OGRERR_NONE)
2734
0
                {
2735
0
                    oAttributes.Delete(CRS_ATTRIBUTE_NAME);
2736
0
                    break;
2737
0
                }
2738
0
                poSRS.reset();
2739
0
            }
2740
0
        }
2741
0
    }
2742
0
    else
2743
0
    {
2744
        // Check if SRS is using CF-1 conventions
2745
0
        const auto gridMapping = oAttributes["grid_mapping"];
2746
0
        if (gridMapping.GetType() == CPLJSONObject::Type::String)
2747
0
        {
2748
0
            const auto gridMappingArray =
2749
0
                poGroup->OpenMDArray(gridMapping.ToString());
2750
0
            if (gridMappingArray)
2751
0
            {
2752
0
                poSRS = std::make_shared<OGRSpatialReference>();
2753
0
                poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2754
0
                CPLStringList aosKeyValues;
2755
0
                for (const auto &poAttr : gridMappingArray->GetAttributes())
2756
0
                {
2757
0
                    if (poAttr->GetDataType().GetClass() == GEDTC_STRING)
2758
0
                    {
2759
0
                        aosKeyValues.SetNameValue(poAttr->GetName().c_str(),
2760
0
                                                  poAttr->ReadAsString());
2761
0
                    }
2762
0
                    else if (poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
2763
0
                    {
2764
0
                        std::string osVal;
2765
0
                        for (double val : poAttr->ReadAsDoubleArray())
2766
0
                        {
2767
0
                            if (!osVal.empty())
2768
0
                                osVal += ',';
2769
0
                            osVal += CPLSPrintf("%.17g", val);
2770
0
                        }
2771
0
                        aosKeyValues.SetNameValue(poAttr->GetName().c_str(),
2772
0
                                                  osVal.c_str());
2773
0
                    }
2774
0
                }
2775
0
                if (poSRS->importFromCF1(aosKeyValues.List(), nullptr) !=
2776
0
                    OGRERR_NONE)
2777
0
                {
2778
0
                    poSRS.reset();
2779
0
                }
2780
0
            }
2781
0
        }
2782
0
    }
2783
2784
0
    if (poSRS)
2785
0
    {
2786
0
        int iDimX = 0;
2787
0
        int iDimY = 0;
2788
0
        int iCount = 1;
2789
0
        for (const auto &poDim : GetDimensions())
2790
0
        {
2791
0
            if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
2792
0
                iDimX = iCount;
2793
0
            else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
2794
0
                iDimY = iCount;
2795
0
            iCount++;
2796
0
        }
2797
0
        if ((iDimX == 0 || iDimY == 0) && GetDimensionCount() >= 2)
2798
0
        {
2799
0
            iDimX = static_cast<int>(GetDimensionCount());
2800
0
            iDimY = iDimX - 1;
2801
0
        }
2802
0
        if (iDimX > 0 && iDimY > 0)
2803
0
        {
2804
0
            const auto &oMapping = poSRS->GetDataAxisToSRSAxisMapping();
2805
0
            if (oMapping == std::vector<int>{2, 1} ||
2806
0
                oMapping == std::vector<int>{2, 1, 3})
2807
0
                poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
2808
0
            else if (oMapping == std::vector<int>{1, 2} ||
2809
0
                     oMapping == std::vector<int>{1, 2, 3})
2810
0
                poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
2811
0
        }
2812
2813
0
        SetSRS(poSRS);
2814
0
    }
2815
2816
0
    const auto unit = oAttributes[CF_UNITS];
2817
0
    if (unit.GetType() == CPLJSONObject::Type::String)
2818
0
    {
2819
0
        std::string osUnit = unit.ToString();
2820
0
        oAttributes.Delete(CF_UNITS);
2821
0
        RegisterUnit(osUnit);
2822
0
    }
2823
2824
0
    const auto offset = oAttributes[CF_ADD_OFFSET];
2825
0
    const auto offsetType = offset.GetType();
2826
0
    if (offsetType == CPLJSONObject::Type::Integer ||
2827
0
        offsetType == CPLJSONObject::Type::Long ||
2828
0
        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
0
    const auto scale = oAttributes[CF_SCALE_FACTOR];
2836
0
    const auto scaleType = scale.GetType();
2837
0
    if (scaleType == CPLJSONObject::Type::Integer ||
2838
0
        scaleType == CPLJSONObject::Type::Long ||
2839
0
        scaleType == CPLJSONObject::Type::Double)
2840
0
    {
2841
0
        double dfScale = scale.ToDouble();
2842
0
        oAttributes.Delete(CF_SCALE_FACTOR);
2843
0
        RegisterScale(dfScale);
2844
0
    }
2845
0
}
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
}