Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/zarr/zarr_attribute.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Zarr driver
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "zarr.h"
14
15
#include <algorithm>
16
#include <cassert>
17
#include <map>
18
19
constexpr const char *ATTRIBUTE_GROUP_SUFFIX = "/_GLOBAL_";
20
21
/************************************************************************/
22
/*             ZarrAttributeGroup::ZarrAttributeGroup()                 */
23
/************************************************************************/
24
25
ZarrAttributeGroup::ZarrAttributeGroup(const std::string &osParentName,
26
                                       bool bContainerIsGroup)
27
8.73k
    : m_bContainerIsGroup(bContainerIsGroup),
28
8.73k
      m_poGroup(MEMGroup::Create(
29
8.73k
          bContainerIsGroup
30
8.73k
              ? (osParentName == "/" ? ATTRIBUTE_GROUP_SUFFIX
31
6.98k
                                     : osParentName + ATTRIBUTE_GROUP_SUFFIX)
32
8.73k
              : osParentName,
33
8.73k
          nullptr))
34
8.73k
{
35
8.73k
}
36
37
/************************************************************************/
38
/*                   ZarrAttributeGroup::Init()                         */
39
/************************************************************************/
40
41
void ZarrAttributeGroup::Init(const CPLJSONObject &obj, bool bUpdatable)
42
1.60k
{
43
1.60k
    if (obj.GetType() != CPLJSONObject::Type::Object)
44
4
        return;
45
1.59k
    const auto children = obj.GetChildren();
46
1.59k
    for (const auto &item : children)
47
3.34k
    {
48
3.34k
        const auto itemType = item.GetType();
49
3.34k
        bool bDone = false;
50
3.34k
        std::shared_ptr<GDALAttribute> poAttr;
51
3.34k
        switch (itemType)
52
3.34k
        {
53
1.62k
            case CPLJSONObject::Type::String:
54
1.62k
            {
55
1.62k
                bDone = true;
56
1.62k
                poAttr = m_poGroup->CreateAttribute(
57
1.62k
                    item.GetName(), {}, GDALExtendedDataType::CreateString(),
58
1.62k
                    nullptr);
59
1.62k
                if (poAttr)
60
1.62k
                {
61
1.62k
                    const GUInt64 arrayStartIdx = 0;
62
1.62k
                    const size_t count = 1;
63
1.62k
                    const GInt64 arrayStep = 0;
64
1.62k
                    const GPtrDiff_t bufferStride = 0;
65
1.62k
                    const std::string str = item.ToString();
66
1.62k
                    const char *c_str = str.c_str();
67
1.62k
                    poAttr->Write(&arrayStartIdx, &count, &arrayStep,
68
1.62k
                                  &bufferStride, poAttr->GetDataType(), &c_str);
69
1.62k
                }
70
1.62k
                break;
71
0
            }
72
78
            case CPLJSONObject::Type::Integer:
73
78
            {
74
78
                bDone = true;
75
78
                poAttr = m_poGroup->CreateAttribute(
76
78
                    item.GetName(), {}, GDALExtendedDataType::Create(GDT_Int32),
77
78
                    nullptr);
78
78
                if (poAttr)
79
78
                {
80
78
                    const GUInt64 arrayStartIdx = 0;
81
78
                    const size_t count = 1;
82
78
                    const GInt64 arrayStep = 0;
83
78
                    const GPtrDiff_t bufferStride = 0;
84
78
                    const int val = item.ToInteger();
85
78
                    poAttr->Write(
86
78
                        &arrayStartIdx, &count, &arrayStep, &bufferStride,
87
78
                        GDALExtendedDataType::Create(GDT_Int32), &val);
88
78
                }
89
78
                break;
90
0
            }
91
0
            case CPLJSONObject::Type::Long:
92
0
            {
93
0
                bDone = true;
94
0
                poAttr = m_poGroup->CreateAttribute(
95
0
                    item.GetName(), {}, GDALExtendedDataType::Create(GDT_Int64),
96
0
                    nullptr);
97
0
                if (poAttr)
98
0
                {
99
0
                    const GUInt64 arrayStartIdx = 0;
100
0
                    const size_t count = 1;
101
0
                    const GInt64 arrayStep = 0;
102
0
                    const GPtrDiff_t bufferStride = 0;
103
0
                    const int64_t val = item.ToLong();
104
0
                    poAttr->Write(
105
0
                        &arrayStartIdx, &count, &arrayStep, &bufferStride,
106
0
                        GDALExtendedDataType::Create(GDT_Int64), &val);
107
0
                }
108
0
                break;
109
0
            }
110
452
            case CPLJSONObject::Type::Double:
111
452
            {
112
452
                bDone = true;
113
452
                poAttr = m_poGroup->CreateAttribute(
114
452
                    item.GetName(), {},
115
452
                    GDALExtendedDataType::Create(GDT_Float64), nullptr);
116
452
                if (poAttr)
117
452
                {
118
452
                    const GUInt64 arrayStartIdx = 0;
119
452
                    const size_t count = 1;
120
452
                    const GInt64 arrayStep = 0;
121
452
                    const GPtrDiff_t bufferStride = 0;
122
452
                    const double val = item.ToDouble();
123
452
                    poAttr->Write(
124
452
                        &arrayStartIdx, &count, &arrayStep, &bufferStride,
125
452
                        GDALExtendedDataType::Create(GDT_Float64), &val);
126
452
                }
127
452
                break;
128
0
            }
129
807
            case CPLJSONObject::Type::Array:
130
807
            {
131
807
                const auto array = item.ToArray();
132
807
                bool isFirst = true;
133
807
                bool isString = false;
134
807
                bool isNumeric = false;
135
807
                bool foundInt64 = false;
136
807
                bool foundDouble = false;
137
807
                bool mixedType = false;
138
807
                size_t countItems = 0;
139
807
                for (const auto &subItem : array)
140
147k
                {
141
147k
                    const auto subItemType = subItem.GetType();
142
147k
                    if (subItemType == CPLJSONObject::Type::String)
143
146k
                    {
144
146k
                        if (isFirst)
145
90
                        {
146
90
                            isString = true;
147
90
                        }
148
146k
                        else if (!isString)
149
0
                        {
150
0
                            mixedType = true;
151
0
                            break;
152
0
                        }
153
146k
                        countItems++;
154
146k
                    }
155
1.43k
                    else if (subItemType == CPLJSONObject::Type::Integer ||
156
15
                             subItemType == CPLJSONObject::Type::Long ||
157
0
                             subItemType == CPLJSONObject::Type::Double)
158
1.43k
                    {
159
1.43k
                        if (isFirst)
160
717
                        {
161
717
                            isNumeric = true;
162
717
                        }
163
717
                        else if (!isNumeric)
164
0
                        {
165
0
                            mixedType = true;
166
0
                            break;
167
0
                        }
168
1.43k
                        if (subItemType == CPLJSONObject::Type::Double)
169
0
                            foundDouble = true;
170
1.43k
                        else if (subItemType == CPLJSONObject::Type::Long)
171
15
                            foundInt64 = true;
172
1.43k
                        countItems++;
173
1.43k
                    }
174
0
                    else
175
0
                    {
176
0
                        mixedType = true;
177
0
                        break;
178
0
                    }
179
147k
                    isFirst = false;
180
147k
                }
181
182
807
                if (!mixedType && !isFirst)
183
807
                {
184
807
                    bDone = true;
185
807
                    poAttr = m_poGroup->CreateAttribute(
186
807
                        item.GetName(), {countItems},
187
807
                        isString ? GDALExtendedDataType::CreateString()
188
807
                                 : GDALExtendedDataType::Create(
189
717
                                       foundDouble  ? GDT_Float64
190
717
                                       : foundInt64 ? GDT_Int64
191
717
                                                    : GDT_Int32),
192
807
                        nullptr);
193
807
                    if (poAttr)
194
807
                    {
195
807
                        size_t idx = 0;
196
807
                        for (const auto &subItem : array)
197
147k
                        {
198
147k
                            const GUInt64 arrayStartIdx = idx;
199
147k
                            const size_t count = 1;
200
147k
                            const GInt64 arrayStep = 0;
201
147k
                            const GPtrDiff_t bufferStride = 0;
202
147k
                            const auto subItemType = subItem.GetType();
203
147k
                            switch (subItemType)
204
147k
                            {
205
146k
                                case CPLJSONObject::Type::String:
206
146k
                                {
207
146k
                                    const std::string str = subItem.ToString();
208
146k
                                    const char *c_str = str.c_str();
209
146k
                                    poAttr->Write(&arrayStartIdx, &count,
210
146k
                                                  &arrayStep, &bufferStride,
211
146k
                                                  poAttr->GetDataType(),
212
146k
                                                  &c_str);
213
146k
                                    break;
214
0
                                }
215
1.41k
                                case CPLJSONObject::Type::Integer:
216
1.41k
                                {
217
1.41k
                                    const int val = subItem.ToInteger();
218
1.41k
                                    poAttr->Write(
219
1.41k
                                        &arrayStartIdx, &count, &arrayStep,
220
1.41k
                                        &bufferStride,
221
1.41k
                                        GDALExtendedDataType::Create(GDT_Int32),
222
1.41k
                                        &val);
223
1.41k
                                    break;
224
0
                                }
225
15
                                case CPLJSONObject::Type::Long:
226
15
                                {
227
15
                                    const int64_t val = subItem.ToLong();
228
15
                                    poAttr->Write(
229
15
                                        &arrayStartIdx, &count, &arrayStep,
230
15
                                        &bufferStride,
231
15
                                        GDALExtendedDataType::Create(GDT_Int64),
232
15
                                        &val);
233
15
                                    break;
234
0
                                }
235
0
                                case CPLJSONObject::Type::Double:
236
0
                                {
237
0
                                    const double val = subItem.ToDouble();
238
0
                                    poAttr->Write(&arrayStartIdx, &count,
239
0
                                                  &arrayStep, &bufferStride,
240
0
                                                  GDALExtendedDataType::Create(
241
0
                                                      GDT_Float64),
242
0
                                                  &val);
243
0
                                    break;
244
0
                                }
245
0
                                default:
246
                                    // Ignore other JSON object types
247
0
                                    break;
248
147k
                            }
249
147k
                            ++idx;
250
147k
                        }
251
807
                    }
252
807
                }
253
807
                break;
254
807
            }
255
807
            default:
256
                // Ignore other JSON object types
257
388
                break;
258
3.34k
        }
259
260
3.34k
        if (!bDone)
261
388
        {
262
388
            constexpr size_t nMaxStringLength = 0;
263
388
            const auto eDT = GDALExtendedDataType::CreateString(
264
388
                nMaxStringLength, GEDTST_JSON);
265
388
            poAttr =
266
388
                m_poGroup->CreateAttribute(item.GetName(), {}, eDT, nullptr);
267
388
            if (poAttr)
268
388
            {
269
388
                const GUInt64 arrayStartIdx = 0;
270
388
                const size_t count = 1;
271
388
                const GInt64 arrayStep = 0;
272
388
                const GPtrDiff_t bufferStride = 0;
273
388
                const std::string str = item.ToString();
274
388
                const char *c_str = str.c_str();
275
388
                poAttr->Write(&arrayStartIdx, &count, &arrayStep, &bufferStride,
276
388
                              poAttr->GetDataType(), &c_str);
277
388
            }
278
388
        }
279
280
3.34k
        auto poMemAttr = std::dynamic_pointer_cast<MEMAttribute>(poAttr);
281
3.34k
        if (poMemAttr)
282
3.34k
            poMemAttr->SetModified(false);
283
3.34k
    }
284
1.59k
    SetUpdatable(bUpdatable);
285
1.59k
}
286
287
/************************************************************************/
288
/*                    ZarrAttributeGroup::Serialize()                   */
289
/************************************************************************/
290
291
CPLJSONObject ZarrAttributeGroup::Serialize() const
292
281
{
293
281
    CPLJSONObject o;
294
281
    const auto attrs = m_poGroup->GetAttributes(nullptr);
295
281
    for (const auto &attr : attrs)
296
4.30k
    {
297
4.30k
        const auto &oType = attr->GetDataType();
298
4.30k
        if (oType.GetClass() == GEDTC_STRING)
299
4.30k
        {
300
4.30k
            const auto anDims = attr->GetDimensionsSize();
301
4.30k
            if (anDims.size() == 0)
302
4.20k
            {
303
4.20k
                const char *pszStr = attr->ReadAsString();
304
4.20k
                if (pszStr)
305
4.20k
                {
306
4.20k
                    CPLJSONDocument oDoc;
307
4.20k
                    if (oType.GetSubType() == GEDTST_JSON &&
308
0
                        oDoc.LoadMemory(pszStr))
309
0
                    {
310
0
                        o.Add(attr->GetName(), oDoc.GetRoot());
311
0
                    }
312
4.20k
                    else
313
4.20k
                    {
314
4.20k
                        o.Add(attr->GetName(), pszStr);
315
4.20k
                    }
316
4.20k
                }
317
0
                else
318
0
                {
319
0
                    o.AddNull(attr->GetName());
320
0
                }
321
4.20k
            }
322
97
            else if (anDims.size() == 1)
323
97
            {
324
97
                const auto list = attr->ReadAsStringArray();
325
97
                CPLJSONArray arr;
326
24.2k
                for (int i = 0; i < list.size(); ++i)
327
24.1k
                {
328
24.1k
                    arr.Add(list[i]);
329
24.1k
                }
330
97
                o.Add(attr->GetName(), arr);
331
97
            }
332
0
            else
333
0
            {
334
0
                CPLError(
335
0
                    CE_Warning, CPLE_AppDefined,
336
0
                    "Cannot serialize attribute %s of dimension count >= 2",
337
0
                    attr->GetName().c_str());
338
0
            }
339
4.30k
        }
340
0
        else if (oType.GetClass() == GEDTC_NUMERIC)
341
0
        {
342
0
            const auto anDims = attr->GetDimensionsSize();
343
0
            const auto eDT = oType.GetNumericDataType();
344
0
            if (anDims.size() == 0)
345
0
            {
346
0
                if (eDT == GDT_Int8 || eDT == GDT_Int16 || eDT == GDT_Int32 ||
347
0
                    eDT == GDT_Int64)
348
0
                {
349
0
                    const int64_t nVal = attr->ReadAsInt64();
350
0
                    o.Add(attr->GetName(), static_cast<GInt64>(nVal));
351
0
                }
352
0
                else if (eDT == GDT_Byte || eDT == GDT_UInt16 ||
353
0
                         eDT == GDT_UInt32 || eDT == GDT_UInt64)
354
0
                {
355
0
                    const int64_t nVal = attr->ReadAsInt64();
356
0
                    o.Add(attr->GetName(), static_cast<uint64_t>(nVal));
357
0
                }
358
0
                else
359
0
                {
360
0
                    const double dfVal = attr->ReadAsDouble();
361
0
                    o.Add(attr->GetName(), dfVal);
362
0
                }
363
0
            }
364
0
            else if (anDims.size() == 1)
365
0
            {
366
0
                CPLJSONArray arr;
367
0
                if (eDT == GDT_Int8 || eDT == GDT_Int16 || eDT == GDT_Int32 ||
368
0
                    eDT == GDT_Int64)
369
0
                {
370
0
                    const auto list = attr->ReadAsInt64Array();
371
0
                    for (const auto nVal : list)
372
0
                    {
373
0
                        arr.Add(static_cast<GInt64>(nVal));
374
0
                    }
375
0
                }
376
0
                else if (eDT == GDT_Byte || eDT == GDT_UInt16 ||
377
0
                         eDT == GDT_UInt32 || eDT == GDT_UInt64)
378
0
                {
379
0
                    const auto list = attr->ReadAsInt64Array();
380
0
                    for (const auto nVal : list)
381
0
                    {
382
0
                        arr.Add(static_cast<uint64_t>(nVal));
383
0
                    }
384
0
                }
385
0
                else
386
0
                {
387
0
                    const auto list = attr->ReadAsDoubleArray();
388
0
                    for (const auto dfVal : list)
389
0
                    {
390
0
                        arr.Add(dfVal);
391
0
                    }
392
0
                }
393
0
                o.Add(attr->GetName(), arr);
394
0
            }
395
0
            else
396
0
            {
397
0
                CPLError(
398
0
                    CE_Warning, CPLE_AppDefined,
399
0
                    "Cannot serialize attribute %s of dimension count >= 2",
400
0
                    attr->GetName().c_str());
401
0
            }
402
0
        }
403
4.30k
    }
404
281
    return o;
405
281
}
406
407
/************************************************************************/
408
/*                          ParentRenamed()                             */
409
/************************************************************************/
410
411
void ZarrAttributeGroup::ParentRenamed(const std::string &osNewParentFullName)
412
0
{
413
0
    if (m_bContainerIsGroup)
414
0
        m_poGroup->SetFullName(osNewParentFullName + ATTRIBUTE_GROUP_SUFFIX);
415
0
    else
416
0
        m_poGroup->SetFullName(osNewParentFullName);
417
0
    const auto attrs = m_poGroup->GetAttributes(nullptr);
418
0
    for (auto &attr : attrs)
419
0
    {
420
0
        attr->ParentRenamed(m_poGroup->GetFullName());
421
0
    }
422
0
}
423
424
/************************************************************************/
425
/*                          ParentDeleted()                             */
426
/************************************************************************/
427
428
void ZarrAttributeGroup::ParentDeleted()
429
0
{
430
0
    m_poGroup->Deleted();
431
0
}