Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/geojson/ogrjsoncollectionstreamingparser.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Streaming parser for GeoJSON-like FeatureCollection
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2017, Even Rouault <even.rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogrjsoncollectionstreamingparser.h"
14
15
#include "cpl_string.h"
16
#include "ogrlibjsonutils.h"  // CPL_json_object_object_get
17
18
#include "ogr_feature.h"
19
20
#define JSON_C_VER_013 (13 << 8)
21
22
#include <json.h>  // JSON-C
23
24
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
25
#include <json_object_private.h>  // just for sizeof(struct json_object)
26
#endif
27
28
#include <charconv>
29
#include <limits>
30
31
#include "include_fast_float.h"
32
33
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
34
const size_t ESTIMATE_BASE_OBJECT_SIZE = sizeof(struct json_object);
35
#elif JSON_C_VERSION_NUM == JSON_C_VER_013  // no way to get the size
36
#if SIZEOF_VOIDP == 8
37
const size_t ESTIMATE_BASE_OBJECT_SIZE = 72;
38
#else
39
const size_t ESTIMATE_BASE_OBJECT_SIZE = 36;
40
#endif
41
#elif JSON_C_VERSION_NUM > JSON_C_VER_013  // we have json_c_object_sizeof()
42
const size_t ESTIMATE_BASE_OBJECT_SIZE = json_c_object_sizeof();
43
#endif
44
45
const size_t ESTIMATE_ARRAY_SIZE =
46
    ESTIMATE_BASE_OBJECT_SIZE + sizeof(struct array_list);
47
const size_t ESTIMATE_ARRAY_ELT_SIZE = sizeof(void *);
48
const size_t ESTIMATE_OBJECT_ELT_SIZE = sizeof(struct lh_entry);
49
const size_t ESTIMATE_OBJECT_SIZE =
50
    ESTIMATE_BASE_OBJECT_SIZE + sizeof(struct lh_table) +
51
    JSON_OBJECT_DEF_HASH_ENTRIES * ESTIMATE_OBJECT_ELT_SIZE;
52
53
/************************************************************************/
54
/*                     OGRJSONCollectionStreamingParser()                */
55
/************************************************************************/
56
57
OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser(
58
    bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize)
59
0
    : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData),
60
0
      m_nMaxObjectSize(nMaxObjectSize)
61
0
{
62
0
}
63
64
/************************************************************************/
65
/*                   ~OGRJSONCollectionStreamingParser()                */
66
/************************************************************************/
67
68
OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser()
69
0
{
70
0
    if (m_poRootObj)
71
0
        json_object_put(m_poRootObj);
72
0
    if (m_poCurObj && m_poCurObj != m_poRootObj)
73
0
        json_object_put(m_poCurObj);
74
0
}
75
76
/************************************************************************/
77
/*                          StealRootObject()                           */
78
/************************************************************************/
79
80
json_object *OGRJSONCollectionStreamingParser::StealRootObject()
81
0
{
82
0
    json_object *poRet = m_poRootObj;
83
0
    if (m_poCurObj == m_poRootObj)
84
0
        m_poCurObj = nullptr;
85
0
    m_poRootObj = nullptr;
86
0
    return poRet;
87
0
}
88
89
/************************************************************************/
90
/*                            AppendObject()                            */
91
/************************************************************************/
92
93
void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj)
94
0
{
95
0
    if (m_bKeySet)
96
0
    {
97
0
        CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object);
98
0
        json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(),
99
0
                               poNewObj);
100
0
        m_osCurKey.clear();
101
0
        m_bKeySet = false;
102
0
    }
103
0
    else
104
0
    {
105
0
        CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array);
106
0
        json_object_array_add(m_apoCurObj.back(), poNewObj);
107
0
    }
108
0
}
109
110
/************************************************************************/
111
/*                            StartObject()                             */
112
/************************************************************************/
113
114
void OGRJSONCollectionStreamingParser::StartObject()
115
0
{
116
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
117
0
    {
118
0
        TooComplex();
119
0
        return;
120
0
    }
121
122
0
    if (m_bInFeaturesArray && m_nDepth == 2)
123
0
    {
124
0
        m_poCurObj = json_object_new_object();
125
0
        m_apoCurObj.push_back(m_poCurObj);
126
0
        if (m_bStoreNativeData)
127
0
        {
128
0
            m_osJson = "{";
129
0
            m_abFirstMember.push_back(true);
130
0
        }
131
0
        m_bStartFeature = true;
132
0
    }
133
0
    else if (m_poCurObj)
134
0
    {
135
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
136
0
        {
137
0
            m_osJson += "{";
138
0
            m_abFirstMember.push_back(true);
139
0
        }
140
141
0
        m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE;
142
143
0
        json_object *poNewObj = json_object_new_object();
144
0
        AppendObject(poNewObj);
145
0
        m_apoCurObj.push_back(poNewObj);
146
0
    }
147
0
    else if (m_bFirstPass && m_nDepth == 0)
148
0
    {
149
0
        m_poRootObj = json_object_new_object();
150
0
        m_apoCurObj.push_back(m_poRootObj);
151
0
        m_poCurObj = m_poRootObj;
152
0
    }
153
154
0
    m_nDepth++;
155
0
}
156
157
/************************************************************************/
158
/*                             EndObject()                              */
159
/************************************************************************/
160
161
void OGRJSONCollectionStreamingParser::EndObject()
162
0
{
163
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
164
0
    {
165
0
        TooComplex();
166
0
        return;
167
0
    }
168
169
0
    m_nDepth--;
170
171
0
    if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj)
172
0
    {
173
0
        if (m_bStoreNativeData)
174
0
        {
175
0
            m_abFirstMember.pop_back();
176
0
            m_osJson += "}";
177
0
            m_nTotalOGRFeatureMemEstimate +=
178
0
                m_osJson.size() + strlen("application/vnd.geo+json");
179
0
        }
180
181
0
        json_object *poObjTypeObj =
182
0
            CPL_json_object_object_get(m_poCurObj, "type");
183
0
        if (poObjTypeObj &&
184
0
            json_object_get_type(poObjTypeObj) == json_type_string)
185
0
        {
186
0
            const char *pszObjType = json_object_get_string(poObjTypeObj);
187
0
            if (strcmp(pszObjType, "Feature") == 0)
188
0
            {
189
0
                GotFeature(m_poCurObj, m_bFirstPass, m_osJson);
190
0
            }
191
0
        }
192
193
0
        json_object_put(m_poCurObj);
194
0
        m_poCurObj = nullptr;
195
0
        m_apoCurObj.clear();
196
0
        m_nCurObjMemEstimate = 0;
197
0
        m_bInCoordinates = false;
198
0
        m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature);
199
0
        m_osJson.clear();
200
0
        m_abFirstMember.clear();
201
0
        m_bEndFeature = true;
202
0
    }
203
0
    else if (m_poCurObj)
204
0
    {
205
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
206
0
        {
207
0
            m_abFirstMember.pop_back();
208
0
            m_osJson += "}";
209
0
        }
210
211
0
        m_apoCurObj.pop_back();
212
0
    }
213
0
    else if (m_nDepth == 1)
214
0
    {
215
0
        m_bInFeatures = false;
216
0
        m_bInMeasures = false;
217
0
        m_bInMeasuresEnabled = false;
218
0
    }
219
0
}
220
221
/************************************************************************/
222
/*                         StartObjectMember()                          */
223
/************************************************************************/
224
225
void OGRJSONCollectionStreamingParser::StartObjectMember(std::string_view sKey)
226
0
{
227
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
228
0
    {
229
0
        TooComplex();
230
0
        return;
231
0
    }
232
233
0
    if (m_nDepth == 1)
234
0
    {
235
0
        m_bInFeatures = sKey == "features";
236
0
        m_bInMeasures = sKey == "measures";
237
0
        m_bCanEasilyAppend = m_bInFeatures;
238
0
        m_bInType = sKey == "type";
239
0
        if (m_bInType || m_bInFeatures)
240
0
        {
241
0
            m_poCurObj = nullptr;
242
0
            m_apoCurObj.clear();
243
0
            m_nRootObjMemEstimate = m_nCurObjMemEstimate;
244
0
        }
245
0
        else if (m_poRootObj)
246
0
        {
247
0
            m_poCurObj = m_poRootObj;
248
0
            m_apoCurObj.clear();
249
0
            m_apoCurObj.push_back(m_poCurObj);
250
0
            m_nCurObjMemEstimate = m_nRootObjMemEstimate;
251
0
        }
252
0
    }
253
0
    else if (m_nDepth == 2 && m_bInMeasures)
254
0
    {
255
0
        m_bInMeasuresEnabled = sKey == "enabled";
256
0
    }
257
0
    else if (m_nDepth == 3 && m_bInFeaturesArray)
258
0
    {
259
0
        m_bInCoordinates = sKey == "coordinates" || sKey == "geometries";
260
0
    }
261
262
0
    if (m_poCurObj)
263
0
    {
264
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
265
0
        {
266
0
            if (!m_abFirstMember.back())
267
0
                m_osJson += ",";
268
0
            m_abFirstMember.back() = false;
269
0
            m_osJson += CPLJSonStreamingParser::GetSerializedString(sKey) + ":";
270
0
        }
271
272
0
        m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE;
273
0
        m_osCurKey = sKey;
274
0
        m_bKeySet = true;
275
0
    }
276
0
}
277
278
/************************************************************************/
279
/*                             StartArray()                             */
280
/************************************************************************/
281
282
void OGRJSONCollectionStreamingParser::StartArray()
283
0
{
284
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
285
0
    {
286
0
        TooComplex();
287
0
        return;
288
0
    }
289
290
0
    if (m_nDepth == 1 && m_bInFeatures)
291
0
    {
292
0
        m_bInFeaturesArray = true;
293
0
    }
294
0
    else if (m_poCurObj)
295
0
    {
296
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
297
0
        {
298
0
            m_osJson += "[";
299
0
            m_abFirstMember.push_back(true);
300
0
        }
301
302
0
        m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE;
303
304
0
        json_object *poNewObj = json_object_new_array();
305
0
        AppendObject(poNewObj);
306
0
        m_apoCurObj.push_back(poNewObj);
307
0
    }
308
0
    m_nDepth++;
309
0
}
310
311
/************************************************************************/
312
/*                          StartArrayMember()                          */
313
/************************************************************************/
314
315
void OGRJSONCollectionStreamingParser::StartArrayMember()
316
0
{
317
0
    if (m_poCurObj)
318
0
    {
319
0
        m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE;
320
321
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
322
0
        {
323
0
            if (!m_abFirstMember.back())
324
0
                m_osJson += ",";
325
0
            m_abFirstMember.back() = false;
326
0
        }
327
0
    }
328
0
}
329
330
/************************************************************************/
331
/*                               EndArray()                             */
332
/************************************************************************/
333
334
void OGRJSONCollectionStreamingParser::EndArray()
335
0
{
336
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
337
0
    {
338
0
        TooComplex();
339
0
        return;
340
0
    }
341
342
0
    m_nDepth--;
343
0
    if (m_nDepth == 1 && m_bInFeaturesArray)
344
0
    {
345
0
        m_bInFeaturesArray = false;
346
0
    }
347
0
    else if (m_poCurObj)
348
0
    {
349
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
350
0
        {
351
0
            m_abFirstMember.pop_back();
352
0
            m_osJson += "]";
353
0
        }
354
355
0
        m_apoCurObj.pop_back();
356
0
    }
357
0
}
358
359
/************************************************************************/
360
/*                              String()                                */
361
/************************************************************************/
362
363
void OGRJSONCollectionStreamingParser::String(std::string_view sValue)
364
0
{
365
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
366
0
    {
367
0
        TooComplex();
368
0
        return;
369
0
    }
370
371
0
    if (m_nDepth == 1 && m_bInType)
372
0
    {
373
0
        m_bIsTypeKnown = true;
374
0
        m_bIsFeatureCollection = sValue == "FeatureCollection";
375
0
    }
376
0
    else if (m_poCurObj)
377
0
    {
378
0
        if (m_bFirstPass)
379
0
        {
380
0
            if (m_bInFeaturesArray)
381
0
                m_nTotalOGRFeatureMemEstimate +=
382
0
                    sizeof(OGRField) + sValue.size();
383
384
0
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
385
0
            m_nCurObjMemEstimate += sValue.size() + sizeof(void *);
386
0
        }
387
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
388
0
        {
389
0
            m_osJson += CPLJSonStreamingParser::GetSerializedString(sValue);
390
0
        }
391
0
        if (sValue.size() < static_cast<size_t>(INT_MAX - 1))
392
0
            AppendObject(json_object_new_string_len(
393
0
                sValue.data(), static_cast<int>(sValue.size())));
394
0
        else
395
0
            EmitException(
396
0
                "OGRJSONCollectionStreamingParser::String(): too large string");
397
0
    }
398
0
}
399
400
/************************************************************************/
401
/*                              Number()                                */
402
/************************************************************************/
403
404
void OGRJSONCollectionStreamingParser::Number(std::string_view sValue)
405
0
{
406
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
407
0
    {
408
0
        TooComplex();
409
0
        return;
410
0
    }
411
412
0
    if (m_poCurObj)
413
0
    {
414
0
        if (m_bFirstPass)
415
0
        {
416
0
            if (m_bInFeaturesArray)
417
0
            {
418
0
                if (m_bInCoordinates)
419
0
                    m_nTotalOGRFeatureMemEstimate += sizeof(double);
420
0
                else
421
0
                    m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
422
0
            }
423
424
0
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
425
0
        }
426
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
427
0
        {
428
0
            m_osJson.append(sValue);
429
0
        }
430
431
0
        if (sValue.size() == strlen("Infinity") &&
432
0
            EQUALN(sValue.data(), "Infinity", strlen("Infinity")))
433
0
        {
434
0
            AppendObject(json_object_new_double(
435
0
                std::numeric_limits<double>::infinity()));
436
0
        }
437
0
        else if (sValue.size() == strlen("-Infinity") &&
438
0
                 EQUALN(sValue.data(), "-Infinity", strlen("-Infinity")))
439
0
        {
440
0
            AppendObject(json_object_new_double(
441
0
                -std::numeric_limits<double>::infinity()));
442
0
        }
443
0
        else if (sValue.size() == strlen("NaN") &&
444
0
                 EQUALN(sValue.data(), "NaN", strlen("NaN")))
445
0
        {
446
0
            AppendObject(json_object_new_double(
447
0
                std::numeric_limits<double>::quiet_NaN()));
448
0
        }
449
0
        else if (sValue.find_first_of("eE.") != std::string::npos ||
450
0
                 sValue.size() >= 20)
451
0
        {
452
0
            double dfValue = 0;
453
0
            const fast_float::parse_options options{
454
0
                fast_float::chars_format::general, '.'};
455
0
            auto answer = fast_float::from_chars_advanced(
456
0
                sValue.data(), sValue.data() + sValue.size(), dfValue, options);
457
0
            if (answer.ec == std::errc() &&
458
0
                answer.ptr == sValue.data() + sValue.size())
459
0
            {
460
0
                AppendObject(json_object_new_double(dfValue));
461
0
            }
462
0
            else
463
0
            {
464
0
                EmitException(
465
0
                    ("Unrecognized number: " + std::string(sValue)).c_str());
466
0
            }
467
0
        }
468
0
        else
469
0
        {
470
0
            GIntBig nValue = 0;
471
0
            auto answer = std::from_chars(
472
0
                sValue.data(), sValue.data() + sValue.size(), nValue);
473
0
            if (answer.ec == std::errc() &&
474
0
                answer.ptr == sValue.data() + sValue.size())
475
0
            {
476
0
                AppendObject(json_object_new_int64(nValue));
477
0
            }
478
0
            else
479
0
            {
480
0
                EmitException(
481
0
                    ("Unrecognized number: " + std::string(sValue)).c_str());
482
0
            }
483
0
        }
484
0
    }
485
0
}
486
487
/************************************************************************/
488
/*                              Boolean()                               */
489
/************************************************************************/
490
491
void OGRJSONCollectionStreamingParser::Boolean(bool bVal)
492
0
{
493
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
494
0
    {
495
0
        TooComplex();
496
0
        return;
497
0
    }
498
499
0
    if (m_bInMeasuresEnabled)
500
0
        m_bHasTopLevelMeasures = bVal;
501
502
0
    if (m_poCurObj)
503
0
    {
504
0
        if (m_bFirstPass)
505
0
        {
506
0
            if (m_bInFeaturesArray)
507
0
                m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
508
509
0
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
510
0
        }
511
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
512
0
        {
513
0
            m_osJson += bVal ? "true" : "false";
514
0
        }
515
516
0
        AppendObject(json_object_new_boolean(bVal));
517
0
    }
518
0
}
519
520
/************************************************************************/
521
/*                               Null()                                 */
522
/************************************************************************/
523
524
void OGRJSONCollectionStreamingParser::Null()
525
0
{
526
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
527
0
    {
528
0
        TooComplex();
529
0
        return;
530
0
    }
531
532
0
    if (m_poCurObj)
533
0
    {
534
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
535
0
        {
536
0
            m_osJson += "null";
537
0
        }
538
539
0
        m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
540
0
        AppendObject(nullptr);
541
0
    }
542
0
}
543
544
/************************************************************************/
545
/*                             Exception()                              */
546
/************************************************************************/
547
548
void OGRJSONCollectionStreamingParser::Exception(const char *pszMessage)
549
0
{
550
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMessage);
551
0
}