Coverage Report

Created: 2025-06-13 06:29

/src/gdal/ogr/ogrsf_frmts/geojson/ogrjsoncollectionstreamingparser.cpp
Line
Count
Source (jump to first uncovered line)
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 <limits>
29
30
#if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
31
const size_t ESTIMATE_BASE_OBJECT_SIZE = sizeof(struct json_object);
32
#elif JSON_C_VERSION_NUM == JSON_C_VER_013  // no way to get the size
33
#if SIZEOF_VOIDP == 8
34
const size_t ESTIMATE_BASE_OBJECT_SIZE = 72;
35
#else
36
const size_t ESTIMATE_BASE_OBJECT_SIZE = 36;
37
#endif
38
#elif JSON_C_VERSION_NUM > JSON_C_VER_013  // we have json_c_object_sizeof()
39
const size_t ESTIMATE_BASE_OBJECT_SIZE = json_c_object_sizeof();
40
#endif
41
42
const size_t ESTIMATE_ARRAY_SIZE =
43
    ESTIMATE_BASE_OBJECT_SIZE + sizeof(struct array_list);
44
const size_t ESTIMATE_ARRAY_ELT_SIZE = sizeof(void *);
45
const size_t ESTIMATE_OBJECT_ELT_SIZE = sizeof(struct lh_entry);
46
const size_t ESTIMATE_OBJECT_SIZE =
47
    ESTIMATE_BASE_OBJECT_SIZE + sizeof(struct lh_table) +
48
    JSON_OBJECT_DEF_HASH_ENTRIES * ESTIMATE_OBJECT_ELT_SIZE;
49
50
/************************************************************************/
51
/*                     OGRJSONCollectionStreamingParser()                */
52
/************************************************************************/
53
54
OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser(
55
    bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize)
56
0
    : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData),
57
0
      m_nMaxObjectSize(nMaxObjectSize)
58
0
{
59
0
}
60
61
/************************************************************************/
62
/*                   ~OGRJSONCollectionStreamingParser()                */
63
/************************************************************************/
64
65
OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser()
66
0
{
67
0
    if (m_poRootObj)
68
0
        json_object_put(m_poRootObj);
69
0
    if (m_poCurObj && m_poCurObj != m_poRootObj)
70
0
        json_object_put(m_poCurObj);
71
0
}
72
73
/************************************************************************/
74
/*                          StealRootObject()                           */
75
/************************************************************************/
76
77
json_object *OGRJSONCollectionStreamingParser::StealRootObject()
78
0
{
79
0
    json_object *poRet = m_poRootObj;
80
0
    if (m_poCurObj == m_poRootObj)
81
0
        m_poCurObj = nullptr;
82
0
    m_poRootObj = nullptr;
83
0
    return poRet;
84
0
}
85
86
/************************************************************************/
87
/*                            AppendObject()                            */
88
/************************************************************************/
89
90
void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj)
91
0
{
92
0
    if (m_bKeySet)
93
0
    {
94
0
        CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object);
95
0
        json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(),
96
0
                               poNewObj);
97
0
        m_osCurKey.clear();
98
0
        m_bKeySet = false;
99
0
    }
100
0
    else
101
0
    {
102
0
        CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array);
103
0
        json_object_array_add(m_apoCurObj.back(), poNewObj);
104
0
    }
105
0
}
106
107
/************************************************************************/
108
/*                            StartObject()                             */
109
/************************************************************************/
110
111
void OGRJSONCollectionStreamingParser::StartObject()
112
0
{
113
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
114
0
    {
115
0
        TooComplex();
116
0
        return;
117
0
    }
118
119
0
    if (m_bInFeaturesArray && m_nDepth == 2)
120
0
    {
121
0
        m_poCurObj = json_object_new_object();
122
0
        m_apoCurObj.push_back(m_poCurObj);
123
0
        if (m_bStoreNativeData)
124
0
        {
125
0
            m_osJson = "{";
126
0
            m_abFirstMember.push_back(true);
127
0
        }
128
0
        m_bStartFeature = true;
129
0
    }
130
0
    else if (m_poCurObj)
131
0
    {
132
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
133
0
        {
134
0
            m_osJson += "{";
135
0
            m_abFirstMember.push_back(true);
136
0
        }
137
138
0
        m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE;
139
140
0
        json_object *poNewObj = json_object_new_object();
141
0
        AppendObject(poNewObj);
142
0
        m_apoCurObj.push_back(poNewObj);
143
0
    }
144
0
    else if (m_bFirstPass && m_nDepth == 0)
145
0
    {
146
0
        m_poRootObj = json_object_new_object();
147
0
        m_apoCurObj.push_back(m_poRootObj);
148
0
        m_poCurObj = m_poRootObj;
149
0
    }
150
151
0
    m_nDepth++;
152
0
}
153
154
/************************************************************************/
155
/*                             EndObject()                              */
156
/************************************************************************/
157
158
void OGRJSONCollectionStreamingParser::EndObject()
159
0
{
160
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
161
0
    {
162
0
        TooComplex();
163
0
        return;
164
0
    }
165
166
0
    m_nDepth--;
167
168
0
    if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj)
169
0
    {
170
0
        if (m_bStoreNativeData)
171
0
        {
172
0
            m_abFirstMember.pop_back();
173
0
            m_osJson += "}";
174
0
            m_nTotalOGRFeatureMemEstimate +=
175
0
                m_osJson.size() + strlen("application/vnd.geo+json");
176
0
        }
177
178
0
        json_object *poObjTypeObj =
179
0
            CPL_json_object_object_get(m_poCurObj, "type");
180
0
        if (poObjTypeObj &&
181
0
            json_object_get_type(poObjTypeObj) == json_type_string)
182
0
        {
183
0
            const char *pszObjType = json_object_get_string(poObjTypeObj);
184
0
            if (strcmp(pszObjType, "Feature") == 0)
185
0
            {
186
0
                GotFeature(m_poCurObj, m_bFirstPass, m_osJson);
187
0
            }
188
0
        }
189
190
0
        json_object_put(m_poCurObj);
191
0
        m_poCurObj = nullptr;
192
0
        m_apoCurObj.clear();
193
0
        m_nCurObjMemEstimate = 0;
194
0
        m_bInCoordinates = false;
195
0
        m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature);
196
0
        m_osJson.clear();
197
0
        m_abFirstMember.clear();
198
0
        m_bEndFeature = true;
199
0
    }
200
0
    else if (m_poCurObj)
201
0
    {
202
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
203
0
        {
204
0
            m_abFirstMember.pop_back();
205
0
            m_osJson += "}";
206
0
        }
207
208
0
        m_apoCurObj.pop_back();
209
0
    }
210
0
    else if (m_nDepth == 1)
211
0
    {
212
0
        m_bInFeatures = false;
213
0
    }
214
0
}
215
216
/************************************************************************/
217
/*                         StartObjectMember()                          */
218
/************************************************************************/
219
220
void OGRJSONCollectionStreamingParser::StartObjectMember(const char *pszKey,
221
                                                         size_t nKeyLen)
222
0
{
223
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
224
0
    {
225
0
        TooComplex();
226
0
        return;
227
0
    }
228
229
0
    if (m_nDepth == 1)
230
0
    {
231
0
        m_bInFeatures = strcmp(pszKey, "features") == 0;
232
0
        m_bCanEasilyAppend = m_bInFeatures;
233
0
        m_bInType = strcmp(pszKey, "type") == 0;
234
0
        if (m_bInType || m_bInFeatures)
235
0
        {
236
0
            m_poCurObj = nullptr;
237
0
            m_apoCurObj.clear();
238
0
            m_nRootObjMemEstimate = m_nCurObjMemEstimate;
239
0
        }
240
0
        else if (m_poRootObj)
241
0
        {
242
0
            m_poCurObj = m_poRootObj;
243
0
            m_apoCurObj.clear();
244
0
            m_apoCurObj.push_back(m_poCurObj);
245
0
            m_nCurObjMemEstimate = m_nRootObjMemEstimate;
246
0
        }
247
0
    }
248
0
    else if (m_nDepth == 3 && m_bInFeaturesArray)
249
0
    {
250
0
        m_bInCoordinates = strcmp(pszKey, "coordinates") == 0 ||
251
0
                           strcmp(pszKey, "geometries") == 0;
252
0
    }
253
254
0
    if (m_poCurObj)
255
0
    {
256
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
257
0
        {
258
0
            if (!m_abFirstMember.back())
259
0
                m_osJson += ",";
260
0
            m_abFirstMember.back() = false;
261
0
            m_osJson +=
262
0
                CPLJSonStreamingParser::GetSerializedString(pszKey) + ":";
263
0
        }
264
265
0
        m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE;
266
0
        m_osCurKey.assign(pszKey, nKeyLen);
267
0
        m_bKeySet = true;
268
0
    }
269
0
}
270
271
/************************************************************************/
272
/*                             StartArray()                             */
273
/************************************************************************/
274
275
void OGRJSONCollectionStreamingParser::StartArray()
276
0
{
277
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
278
0
    {
279
0
        TooComplex();
280
0
        return;
281
0
    }
282
283
0
    if (m_nDepth == 1 && m_bInFeatures)
284
0
    {
285
0
        m_bInFeaturesArray = true;
286
0
    }
287
0
    else if (m_poCurObj)
288
0
    {
289
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
290
0
        {
291
0
            m_osJson += "[";
292
0
            m_abFirstMember.push_back(true);
293
0
        }
294
295
0
        m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE;
296
297
0
        json_object *poNewObj = json_object_new_array();
298
0
        AppendObject(poNewObj);
299
0
        m_apoCurObj.push_back(poNewObj);
300
0
    }
301
0
    m_nDepth++;
302
0
}
303
304
/************************************************************************/
305
/*                          StartArrayMember()                          */
306
/************************************************************************/
307
308
void OGRJSONCollectionStreamingParser::StartArrayMember()
309
0
{
310
0
    if (m_poCurObj)
311
0
    {
312
0
        m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE;
313
314
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
315
0
        {
316
0
            if (!m_abFirstMember.back())
317
0
                m_osJson += ",";
318
0
            m_abFirstMember.back() = false;
319
0
        }
320
0
    }
321
0
}
322
323
/************************************************************************/
324
/*                               EndArray()                             */
325
/************************************************************************/
326
327
void OGRJSONCollectionStreamingParser::EndArray()
328
0
{
329
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
330
0
    {
331
0
        TooComplex();
332
0
        return;
333
0
    }
334
335
0
    m_nDepth--;
336
0
    if (m_nDepth == 1 && m_bInFeaturesArray)
337
0
    {
338
0
        m_bInFeaturesArray = false;
339
0
    }
340
0
    else if (m_poCurObj)
341
0
    {
342
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
343
0
        {
344
0
            m_abFirstMember.pop_back();
345
0
            m_osJson += "]";
346
0
        }
347
348
0
        m_apoCurObj.pop_back();
349
0
    }
350
0
}
351
352
/************************************************************************/
353
/*                              String()                                */
354
/************************************************************************/
355
356
void OGRJSONCollectionStreamingParser::String(const char *pszValue, size_t nLen)
357
0
{
358
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
359
0
    {
360
0
        TooComplex();
361
0
        return;
362
0
    }
363
364
0
    if (m_nDepth == 1 && m_bInType)
365
0
    {
366
0
        m_bIsTypeKnown = true;
367
0
        m_bIsFeatureCollection = strcmp(pszValue, "FeatureCollection") == 0;
368
0
    }
369
0
    else if (m_poCurObj)
370
0
    {
371
0
        if (m_bFirstPass)
372
0
        {
373
0
            if (m_bInFeaturesArray)
374
0
                m_nTotalOGRFeatureMemEstimate += sizeof(OGRField) + nLen;
375
376
0
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
377
0
            m_nCurObjMemEstimate += nLen + sizeof(void *);
378
0
        }
379
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
380
0
        {
381
0
            m_osJson += CPLJSonStreamingParser::GetSerializedString(pszValue);
382
0
        }
383
0
        AppendObject(json_object_new_string(pszValue));
384
0
    }
385
0
}
386
387
/************************************************************************/
388
/*                              Number()                                */
389
/************************************************************************/
390
391
void OGRJSONCollectionStreamingParser::Number(const char *pszValue, size_t nLen)
392
0
{
393
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
394
0
    {
395
0
        TooComplex();
396
0
        return;
397
0
    }
398
399
0
    if (m_poCurObj)
400
0
    {
401
0
        if (m_bFirstPass)
402
0
        {
403
0
            if (m_bInFeaturesArray)
404
0
            {
405
0
                if (m_bInCoordinates)
406
0
                    m_nTotalOGRFeatureMemEstimate += sizeof(double);
407
0
                else
408
0
                    m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
409
0
            }
410
411
0
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
412
0
        }
413
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
414
0
        {
415
0
            m_osJson.append(pszValue, nLen);
416
0
        }
417
418
0
        if (CPLGetValueType(pszValue) == CPL_VALUE_REAL)
419
0
        {
420
0
            AppendObject(json_object_new_double(CPLAtof(pszValue)));
421
0
        }
422
0
        else if (nLen == strlen("Infinity") && EQUAL(pszValue, "Infinity"))
423
0
        {
424
0
            AppendObject(json_object_new_double(
425
0
                std::numeric_limits<double>::infinity()));
426
0
        }
427
0
        else if (nLen == strlen("-Infinity") && EQUAL(pszValue, "-Infinity"))
428
0
        {
429
0
            AppendObject(json_object_new_double(
430
0
                -std::numeric_limits<double>::infinity()));
431
0
        }
432
0
        else if (nLen == strlen("NaN") && EQUAL(pszValue, "NaN"))
433
0
        {
434
0
            AppendObject(json_object_new_double(
435
0
                std::numeric_limits<double>::quiet_NaN()));
436
0
        }
437
0
        else
438
0
        {
439
0
            AppendObject(json_object_new_int64(CPLAtoGIntBig(pszValue)));
440
0
        }
441
0
    }
442
0
}
443
444
/************************************************************************/
445
/*                              Boolean()                               */
446
/************************************************************************/
447
448
void OGRJSONCollectionStreamingParser::Boolean(bool bVal)
449
0
{
450
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
451
0
    {
452
0
        TooComplex();
453
0
        return;
454
0
    }
455
456
0
    if (m_poCurObj)
457
0
    {
458
0
        if (m_bFirstPass)
459
0
        {
460
0
            if (m_bInFeaturesArray)
461
0
                m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
462
463
0
            m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
464
0
        }
465
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
466
0
        {
467
0
            m_osJson += bVal ? "true" : "false";
468
0
        }
469
470
0
        AppendObject(json_object_new_boolean(bVal));
471
0
    }
472
0
}
473
474
/************************************************************************/
475
/*                               Null()                                 */
476
/************************************************************************/
477
478
void OGRJSONCollectionStreamingParser::Null()
479
0
{
480
0
    if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
481
0
    {
482
0
        TooComplex();
483
0
        return;
484
0
    }
485
486
0
    if (m_poCurObj)
487
0
    {
488
0
        if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
489
0
        {
490
0
            m_osJson += "null";
491
0
        }
492
493
0
        m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
494
0
        AppendObject(nullptr);
495
0
    }
496
0
}
497
498
/************************************************************************/
499
/*                             Exception()                              */
500
/************************************************************************/
501
502
void OGRJSONCollectionStreamingParser::Exception(const char *pszMessage)
503
0
{
504
0
    CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMessage);
505
0
}