Coverage Report

Created: 2026-09-26 08:22

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
4.27k
    : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData),
60
4.27k
      m_nMaxObjectSize(nMaxObjectSize)
61
4.27k
{
62
4.27k
}
63
64
/************************************************************************/
65
/*                 ~OGRJSONCollectionStreamingParser()                  */
66
/************************************************************************/
67
68
OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser()
69
4.27k
{
70
4.27k
    if (m_poRootObj)
71
3.16k
        json_object_put(m_poRootObj);
72
4.27k
    if (m_poCurObj && m_poCurObj != m_poRootObj)
73
1.86k
        json_object_put(m_poCurObj);
74
4.27k
}
75
76
/************************************************************************/
77
/*                          StealRootObject()                           */
78
/************************************************************************/
79
80
json_object *OGRJSONCollectionStreamingParser::StealRootObject()
81
862
{
82
862
    json_object *poRet = m_poRootObj;
83
862
    if (m_poCurObj == m_poRootObj)
84
3
        m_poCurObj = nullptr;
85
862
    m_poRootObj = nullptr;
86
862
    return poRet;
87
862
}
88
89
/************************************************************************/
90
/*                            AppendObject()                            */
91
/************************************************************************/
92
93
void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj)
94
3.59M
{
95
3.59M
    if (m_bKeySet)
96
2.98M
    {
97
2.98M
        CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object);
98
2.98M
        json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(),
99
2.98M
                               poNewObj);
100
2.98M
        m_osCurKey.clear();
101
2.98M
        m_bKeySet = false;
102
2.98M
    }
103
618k
    else
104
618k
    {
105
618k
        CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array);
106
618k
        json_object_array_add(m_apoCurObj.back(), poNewObj);
107
618k
    }
108
3.59M
}
109
110
/************************************************************************/
111
/*                            StartObject()                             */
112
/************************************************************************/
113
114
void OGRJSONCollectionStreamingParser::StartObject()
115
1.17M
{
116
1.17M
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
117
0
    {
118
0
        TooComplex();
119
0
        return;
120
0
    }
121
122
1.17M
    if (m_bInFeaturesArray && m_nDepth == 2)
123
535k
    {
124
535k
        m_poCurObj = json_object_new_object();
125
535k
        m_apoCurObj.push_back(m_poCurObj);
126
535k
        if (m_bStoreNativeData)
127
0
        {
128
0
            m_osJson = "{";
129
0
            m_abFirstMember.push_back(true);
130
0
        }
131
535k
        m_bStartFeature = true;
132
535k
    }
133
634k
    else if (m_poCurObj)
134
621k
    {
135
621k
        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
621k
        m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE;
142
143
621k
        json_object *poNewObj = json_object_new_object();
144
621k
        AppendObject(poNewObj);
145
621k
        m_apoCurObj.push_back(poNewObj);
146
621k
    }
147
13.3k
    else if (m_bFirstPass && m_nDepth == 0)
148
4.02k
    {
149
4.02k
        m_poRootObj = json_object_new_object();
150
4.02k
        m_apoCurObj.push_back(m_poRootObj);
151
4.02k
        m_poCurObj = m_poRootObj;
152
4.02k
    }
153
154
1.17M
    m_nDepth++;
155
1.17M
}
156
157
/************************************************************************/
158
/*                             EndObject()                              */
159
/************************************************************************/
160
161
void OGRJSONCollectionStreamingParser::EndObject()
162
1.10M
{
163
1.10M
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
164
0
    {
165
0
        TooComplex();
166
0
        return;
167
0
    }
168
169
1.10M
    m_nDepth--;
170
171
1.10M
    if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj)
172
533k
    {
173
533k
        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/geo+json");
179
0
        }
180
181
533k
        json_object *poObjTypeObj =
182
533k
            CPL_json_object_object_get(m_poCurObj, "type");
183
533k
        if (poObjTypeObj &&
184
532k
            json_object_get_type(poObjTypeObj) == json_type_string)
185
531k
        {
186
531k
            const char *pszObjType = json_object_get_string(poObjTypeObj);
187
531k
            if (strcmp(pszObjType, "Feature") == 0)
188
530k
            {
189
530k
                GotFeature(m_poCurObj, m_bFirstPass, m_osJson);
190
530k
            }
191
531k
        }
192
193
533k
        json_object_put(m_poCurObj);
194
533k
        m_poCurObj = nullptr;
195
533k
        m_apoCurObj.clear();
196
533k
        m_nCurObjMemEstimate = 0;
197
533k
        m_bInCoordinates = false;
198
533k
        m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature);
199
533k
        m_osJson.clear();
200
533k
        m_abFirstMember.clear();
201
533k
        m_bEndFeature = true;
202
533k
    }
203
570k
    else if (m_poCurObj)
204
565k
    {
205
565k
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
206
0
        {
207
0
            m_abFirstMember.pop_back();
208
0
            m_osJson += "}";
209
0
        }
210
211
565k
        m_apoCurObj.pop_back();
212
565k
    }
213
5.12k
    else if (m_nDepth == 1)
214
6
    {
215
6
        m_bInFeatures = false;
216
6
        m_bInMeasures = false;
217
6
        m_bInMeasuresEnabled = false;
218
6
    }
219
1.10M
}
220
221
/************************************************************************/
222
/*                         StartObjectMember()                          */
223
/************************************************************************/
224
225
void OGRJSONCollectionStreamingParser::StartObjectMember(std::string_view sKey)
226
3.02M
{
227
3.02M
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
228
0
    {
229
0
        TooComplex();
230
0
        return;
231
0
    }
232
233
3.02M
    if (m_nDepth == 1)
234
10.4k
    {
235
10.4k
        m_bInFeatures = sKey == "features";
236
10.4k
        m_bInMeasures = sKey == "measures";
237
10.4k
        m_bCanEasilyAppend = m_bInFeatures;
238
10.4k
        m_bInType = sKey == "type";
239
10.4k
        if (m_bInType || m_bInFeatures)
240
6.01k
        {
241
6.01k
            m_poCurObj = nullptr;
242
6.01k
            m_apoCurObj.clear();
243
6.01k
            m_nRootObjMemEstimate = m_nCurObjMemEstimate;
244
6.01k
        }
245
4.39k
        else if (m_poRootObj)
246
4.17k
        {
247
4.17k
            m_poCurObj = m_poRootObj;
248
4.17k
            m_apoCurObj.clear();
249
4.17k
            m_apoCurObj.push_back(m_poCurObj);
250
4.17k
            m_nCurObjMemEstimate = m_nRootObjMemEstimate;
251
4.17k
        }
252
10.4k
    }
253
3.01M
    else if (m_nDepth == 2 && m_bInMeasures)
254
0
    {
255
0
        m_bInMeasuresEnabled = sKey == "enabled";
256
0
    }
257
3.01M
    else if (m_nDepth == 3 && m_bInFeaturesArray)
258
1.61M
    {
259
1.61M
        m_bInCoordinates = sKey == "coordinates" || sKey == "geometries";
260
1.61M
    }
261
262
3.02M
    if (m_poCurObj)
263
2.98M
    {
264
2.98M
        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
2.98M
        m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE;
273
2.98M
        m_osCurKey = sKey;
274
2.98M
        m_bKeySet = true;
275
2.98M
    }
276
3.02M
}
277
278
/************************************************************************/
279
/*                             StartArray()                             */
280
/************************************************************************/
281
282
void OGRJSONCollectionStreamingParser::StartArray()
283
584k
{
284
584k
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
285
0
    {
286
0
        TooComplex();
287
0
        return;
288
0
    }
289
290
584k
    if (m_nDepth == 1 && m_bInFeatures)
291
3.99k
    {
292
3.99k
        m_bInFeaturesArray = true;
293
3.99k
    }
294
580k
    else if (m_poCurObj)
295
568k
    {
296
568k
        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
568k
        m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE;
303
304
568k
        json_object *poNewObj = json_object_new_array();
305
568k
        AppendObject(poNewObj);
306
568k
        m_apoCurObj.push_back(poNewObj);
307
568k
    }
308
584k
    m_nDepth++;
309
584k
}
310
311
/************************************************************************/
312
/*                          StartArrayMember()                          */
313
/************************************************************************/
314
315
void OGRJSONCollectionStreamingParser::StartArrayMember()
316
1.16M
{
317
1.16M
    if (m_poCurObj)
318
619k
    {
319
619k
        m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE;
320
321
619k
        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
619k
    }
328
1.16M
}
329
330
/************************************************************************/
331
/*                              EndArray()                              */
332
/************************************************************************/
333
334
void OGRJSONCollectionStreamingParser::EndArray()
335
534k
{
336
534k
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
337
0
    {
338
0
        TooComplex();
339
0
        return;
340
0
    }
341
342
534k
    m_nDepth--;
343
534k
    if (m_nDepth == 1 && m_bInFeaturesArray)
344
1.55k
    {
345
1.55k
        m_bInFeaturesArray = false;
346
1.55k
    }
347
532k
    else if (m_poCurObj)
348
526k
    {
349
526k
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
350
0
        {
351
0
            m_abFirstMember.pop_back();
352
0
            m_osJson += "]";
353
0
        }
354
355
526k
        m_apoCurObj.pop_back();
356
526k
    }
357
534k
}
358
359
/************************************************************************/
360
/*                               String()                               */
361
/************************************************************************/
362
363
void OGRJSONCollectionStreamingParser::String(std::string_view sValue)
364
1.45M
{
365
1.45M
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
366
0
    {
367
0
        TooComplex();
368
0
        return;
369
0
    }
370
371
1.45M
    if (m_nDepth == 1 && m_bInType)
372
1.96k
    {
373
1.96k
        m_bIsTypeKnown = true;
374
1.96k
        m_bIsFeatureCollection = sValue == "FeatureCollection";
375
1.96k
    }
376
1.45M
    else if (m_poCurObj)
377
1.43M
    {
378
1.43M
        if (m_bFirstPass)
379
1.36M
        {
380
1.36M
            if (m_bInFeaturesArray)
381
1.32M
                m_nTotalOGRFeatureMemEstimate +=
382
1.32M
                    sizeof(OGRField) + sValue.size();
383
384
1.36M
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
385
1.36M
            m_nCurObjMemEstimate += sValue.size() + sizeof(void *);
386
1.36M
        }
387
1.43M
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
388
0
        {
389
0
            m_osJson += CPLJSonStreamingParser::GetSerializedString(sValue);
390
0
        }
391
1.43M
        if (sValue.size() < static_cast<size_t>(INT_MAX - 1))
392
1.43M
            AppendObject(json_object_new_string_len(
393
1.43M
                sValue.data(), static_cast<int>(sValue.size())));
394
0
        else
395
0
            EmitException(
396
0
                "OGRJSONCollectionStreamingParser::String(): too large string");
397
1.43M
    }
398
1.45M
}
399
400
/************************************************************************/
401
/*                               Number()                               */
402
/************************************************************************/
403
404
// recent libc++ std::from_chars() involve unsigned integer overflow
405
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
406
void OGRJSONCollectionStreamingParser::Number(std::string_view sValue)
407
337k
{
408
337k
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
409
0
    {
410
0
        TooComplex();
411
0
        return;
412
0
    }
413
414
337k
    if (m_poCurObj)
415
329k
    {
416
329k
        if (m_bFirstPass)
417
287k
        {
418
287k
            if (m_bInFeaturesArray)
419
262k
            {
420
262k
                if (m_bInCoordinates)
421
21
                    m_nTotalOGRFeatureMemEstimate += sizeof(double);
422
262k
                else
423
262k
                    m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
424
262k
            }
425
426
287k
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
427
287k
        }
428
329k
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
429
0
        {
430
0
            m_osJson.append(sValue);
431
0
        }
432
433
329k
        if (sValue.size() == strlen("Infinity") &&
434
62.1k
            EQUALN(sValue.data(), "Infinity", strlen("Infinity")))
435
3
        {
436
3
            AppendObject(json_object_new_double(
437
3
                std::numeric_limits<double>::infinity()));
438
3
        }
439
329k
        else if (sValue.size() == strlen("-Infinity") &&
440
24.9k
                 EQUALN(sValue.data(), "-Infinity", strlen("-Infinity")))
441
5
        {
442
5
            AppendObject(json_object_new_double(
443
5
                -std::numeric_limits<double>::infinity()));
444
5
        }
445
329k
        else if (sValue.size() == strlen("NaN") &&
446
22.1k
                 EQUALN(sValue.data(), "NaN", strlen("NaN")))
447
4
        {
448
4
            AppendObject(json_object_new_double(
449
4
                std::numeric_limits<double>::quiet_NaN()));
450
4
        }
451
329k
        else if (sValue.find_first_of("eE.") != std::string::npos ||
452
188k
                 sValue.size() >= 20)
453
147k
        {
454
147k
            double dfValue = 0;
455
147k
            const fast_float::parse_options options{
456
147k
                fast_float::chars_format::general, '.'};
457
147k
            auto answer = fast_float::from_chars_advanced(
458
147k
                sValue.data(), sValue.data() + sValue.size(), dfValue, options);
459
147k
            if (answer.ec == std::errc() &&
460
147k
                answer.ptr == sValue.data() + sValue.size())
461
147k
            {
462
147k
                AppendObject(json_object_new_double(dfValue));
463
147k
            }
464
148
            else
465
148
            {
466
148
                EmitException(
467
148
                    ("Unrecognized number: " + std::string(sValue)).c_str());
468
148
            }
469
147k
        }
470
181k
        else
471
181k
        {
472
181k
            GIntBig nValue = 0;
473
181k
            auto answer = std::from_chars(
474
181k
                sValue.data(), sValue.data() + sValue.size(), nValue);
475
181k
            if (answer.ec == std::errc() &&
476
181k
                answer.ptr == sValue.data() + sValue.size())
477
181k
            {
478
181k
                AppendObject(json_object_new_int64(nValue));
479
181k
            }
480
121
            else
481
121
            {
482
121
                EmitException(
483
121
                    ("Unrecognized number: " + std::string(sValue)).c_str());
484
121
            }
485
181k
        }
486
329k
    }
487
337k
}
488
489
/************************************************************************/
490
/*                              Boolean()                               */
491
/************************************************************************/
492
493
void OGRJSONCollectionStreamingParser::Boolean(bool bVal)
494
87.3k
{
495
87.3k
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
496
0
    {
497
0
        TooComplex();
498
0
        return;
499
0
    }
500
501
87.3k
    if (m_bInMeasuresEnabled)
502
0
        m_bHasTopLevelMeasures = bVal;
503
504
87.3k
    if (m_poCurObj)
505
84.9k
    {
506
84.9k
        if (m_bFirstPass)
507
74.3k
        {
508
74.3k
            if (m_bInFeaturesArray)
509
73.9k
                m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
510
511
74.3k
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
512
74.3k
        }
513
84.9k
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
514
0
        {
515
0
            m_osJson += bVal ? "true" : "false";
516
0
        }
517
518
84.9k
        AppendObject(json_object_new_boolean(bVal));
519
84.9k
    }
520
87.3k
}
521
522
/************************************************************************/
523
/*                                Null()                                */
524
/************************************************************************/
525
526
void OGRJSONCollectionStreamingParser::Null()
527
565k
{
528
565k
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
529
0
    {
530
0
        TooComplex();
531
0
        return;
532
0
    }
533
534
565k
    if (m_poCurObj)
535
560k
    {
536
560k
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
537
0
        {
538
0
            m_osJson += "null";
539
0
        }
540
541
560k
        m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
542
560k
        AppendObject(nullptr);
543
560k
    }
544
565k
}
545
546
/************************************************************************/
547
/*                             Exception()                              */
548
/************************************************************************/
549
550
void OGRJSONCollectionStreamingParser::Exception(const char *pszMessage)
551
2.92k
{
552
2.92k
    CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMessage);
553
2.92k
}