Coverage Report

Created: 2026-08-11 08:26

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