Line data Source code
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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "ogrjsoncollectionstreamingparser.h"
30 :
31 : #include "cpl_string.h"
32 : #include "ogrgeojsonreader.h" // CPL_json_object_object_get
33 :
34 : #define JSON_C_VER_013 (13 << 8)
35 :
36 : #include <json.h> // JSON-C
37 :
38 : #if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
39 : #include <json_object_private.h> // just for sizeof(struct json_object)
40 : #endif
41 :
42 : #include <limits>
43 :
44 : #if (!defined(JSON_C_VERSION_NUM)) || (JSON_C_VERSION_NUM < JSON_C_VER_013)
45 : const size_t ESTIMATE_BASE_OBJECT_SIZE = sizeof(struct json_object);
46 : #elif JSON_C_VERSION_NUM == JSON_C_VER_013 // no way to get the size
47 : #if SIZEOF_VOIDP == 8
48 : const size_t ESTIMATE_BASE_OBJECT_SIZE = 72;
49 : #else
50 : const size_t ESTIMATE_BASE_OBJECT_SIZE = 36;
51 : #endif
52 : #elif JSON_C_VERSION_NUM > JSON_C_VER_013 // we have json_c_object_sizeof()
53 : const size_t ESTIMATE_BASE_OBJECT_SIZE = json_c_object_sizeof();
54 : #endif
55 :
56 : const size_t ESTIMATE_ARRAY_SIZE =
57 : ESTIMATE_BASE_OBJECT_SIZE + sizeof(struct array_list);
58 : const size_t ESTIMATE_ARRAY_ELT_SIZE = sizeof(void *);
59 : const size_t ESTIMATE_OBJECT_ELT_SIZE = sizeof(struct lh_entry);
60 : const size_t ESTIMATE_OBJECT_SIZE =
61 : ESTIMATE_BASE_OBJECT_SIZE + sizeof(struct lh_table) +
62 : JSON_OBJECT_DEF_HASH_ENTRIES * ESTIMATE_OBJECT_ELT_SIZE;
63 :
64 : /************************************************************************/
65 : /* OGRJSONCollectionStreamingParser() */
66 : /************************************************************************/
67 :
68 763 : OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser(
69 763 : bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize)
70 : : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData),
71 763 : m_nMaxObjectSize(nMaxObjectSize)
72 : {
73 763 : }
74 :
75 : /************************************************************************/
76 : /* ~OGRJSONCollectionStreamingParser() */
77 : /************************************************************************/
78 :
79 763 : OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser()
80 : {
81 763 : if (m_poRootObj)
82 3 : json_object_put(m_poRootObj);
83 763 : if (m_poCurObj && m_poCurObj != m_poRootObj)
84 1 : json_object_put(m_poCurObj);
85 763 : }
86 :
87 : /************************************************************************/
88 : /* StealRootObject() */
89 : /************************************************************************/
90 :
91 329 : json_object *OGRJSONCollectionStreamingParser::StealRootObject()
92 : {
93 329 : json_object *poRet = m_poRootObj;
94 329 : if (m_poCurObj == m_poRootObj)
95 22 : m_poCurObj = nullptr;
96 329 : m_poRootObj = nullptr;
97 329 : return poRet;
98 : }
99 :
100 : /************************************************************************/
101 : /* AppendObject() */
102 : /************************************************************************/
103 :
104 524656 : void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj)
105 : {
106 524656 : if (m_bKeySet)
107 : {
108 33505 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object);
109 33505 : json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(),
110 : poNewObj);
111 33505 : m_osCurKey.clear();
112 33505 : m_bKeySet = false;
113 : }
114 : else
115 : {
116 491151 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array);
117 491151 : json_object_array_add(m_apoCurObj.back(), poNewObj);
118 : }
119 524656 : }
120 :
121 : /************************************************************************/
122 : /* StartObject() */
123 : /************************************************************************/
124 :
125 14325 : void OGRJSONCollectionStreamingParser::StartObject()
126 : {
127 14325 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
128 : {
129 0 : TooComplex();
130 0 : return;
131 : }
132 :
133 14325 : if (m_bInFeaturesArray && m_nDepth == 2)
134 : {
135 4412 : m_poCurObj = json_object_new_object();
136 4412 : m_apoCurObj.push_back(m_poCurObj);
137 4412 : if (m_bStoreNativeData)
138 : {
139 905 : m_osJson = "{";
140 905 : m_abFirstMember.push_back(true);
141 : }
142 4412 : m_bStartFeature = true;
143 : }
144 9913 : else if (m_poCurObj)
145 : {
146 9194 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
147 : {
148 1779 : m_osJson += "{";
149 1779 : m_abFirstMember.push_back(true);
150 : }
151 :
152 9194 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE;
153 :
154 9194 : json_object *poNewObj = json_object_new_object();
155 9194 : AppendObject(poNewObj);
156 9194 : m_apoCurObj.push_back(poNewObj);
157 : }
158 719 : else if (m_bFirstPass && m_nDepth == 0)
159 : {
160 332 : m_poRootObj = json_object_new_object();
161 332 : m_apoCurObj.push_back(m_poRootObj);
162 332 : m_poCurObj = m_poRootObj;
163 : }
164 :
165 14325 : m_nDepth++;
166 : }
167 :
168 : /************************************************************************/
169 : /* EndObject() */
170 : /************************************************************************/
171 :
172 14321 : void OGRJSONCollectionStreamingParser::EndObject()
173 : {
174 14321 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
175 : {
176 0 : TooComplex();
177 0 : return;
178 : }
179 :
180 14321 : m_nDepth--;
181 :
182 14321 : if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj)
183 : {
184 4411 : if (m_bStoreNativeData)
185 : {
186 905 : m_abFirstMember.pop_back();
187 905 : m_osJson += "}";
188 905 : m_nTotalOGRFeatureMemEstimate +=
189 905 : m_osJson.size() + strlen("application/vnd.geo+json");
190 : }
191 :
192 : json_object *poObjTypeObj =
193 4411 : CPL_json_object_object_get(m_poCurObj, "type");
194 8822 : if (poObjTypeObj &&
195 4411 : json_object_get_type(poObjTypeObj) == json_type_string)
196 : {
197 4411 : const char *pszObjType = json_object_get_string(poObjTypeObj);
198 4411 : if (strcmp(pszObjType, "Feature") == 0)
199 : {
200 4411 : GotFeature(m_poCurObj, m_bFirstPass, m_osJson);
201 : }
202 : }
203 :
204 4411 : json_object_put(m_poCurObj);
205 4411 : m_poCurObj = nullptr;
206 4411 : m_apoCurObj.clear();
207 4411 : m_nCurObjMemEstimate = 0;
208 4411 : m_bInCoordinates = false;
209 4411 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature);
210 4411 : m_osJson.clear();
211 4411 : m_abFirstMember.clear();
212 4411 : m_bEndFeature = true;
213 : }
214 9910 : else if (m_poCurObj)
215 : {
216 9216 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
217 : {
218 1779 : m_abFirstMember.pop_back();
219 1779 : m_osJson += "}";
220 : }
221 :
222 9216 : m_apoCurObj.pop_back();
223 : }
224 694 : else if (m_nDepth == 1)
225 : {
226 28 : m_bInFeatures = false;
227 : }
228 : }
229 :
230 : /************************************************************************/
231 : /* StartObjectMember() */
232 : /************************************************************************/
233 :
234 35321 : void OGRJSONCollectionStreamingParser::StartObjectMember(const char *pszKey,
235 : size_t nKeyLen)
236 : {
237 35321 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
238 : {
239 0 : TooComplex();
240 0 : return;
241 : }
242 :
243 35321 : if (m_nDepth == 1)
244 : {
245 2059 : m_bInFeatures = strcmp(pszKey, "features") == 0;
246 2059 : m_bCanEasilyAppend = m_bInFeatures;
247 2059 : m_bInType = strcmp(pszKey, "type") == 0;
248 2059 : if (m_bInType || m_bInFeatures)
249 : {
250 1318 : m_poCurObj = nullptr;
251 1318 : m_apoCurObj.clear();
252 1318 : m_nRootObjMemEstimate = m_nCurObjMemEstimate;
253 : }
254 741 : else if (m_poRootObj)
255 : {
256 347 : m_poCurObj = m_poRootObj;
257 347 : m_apoCurObj.clear();
258 347 : m_apoCurObj.push_back(m_poCurObj);
259 347 : m_nCurObjMemEstimate = m_nRootObjMemEstimate;
260 : }
261 : }
262 33262 : else if (m_nDepth == 3 && m_bInFeaturesArray)
263 : {
264 30040 : m_bInCoordinates = strcmp(pszKey, "coordinates") == 0 ||
265 15020 : strcmp(pszKey, "geometries") == 0;
266 : }
267 :
268 35321 : if (m_poCurObj)
269 : {
270 33505 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
271 : {
272 5125 : if (!m_abFirstMember.back())
273 2738 : m_osJson += ",";
274 5125 : m_abFirstMember.back() = false;
275 : m_osJson +=
276 5125 : CPLJSonStreamingParser::GetSerializedString(pszKey) + ":";
277 : }
278 :
279 33505 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE;
280 33505 : m_osCurKey.assign(pszKey, nKeyLen);
281 33505 : m_bKeySet = true;
282 : }
283 : }
284 :
285 : /************************************************************************/
286 : /* StartArray() */
287 : /************************************************************************/
288 :
289 178878 : void OGRJSONCollectionStreamingParser::StartArray()
290 : {
291 178878 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
292 : {
293 6221 : TooComplex();
294 6221 : return;
295 : }
296 :
297 172657 : if (m_nDepth == 1 && m_bInFeatures)
298 : {
299 659 : m_bInFeaturesArray = true;
300 : }
301 171998 : else if (m_poCurObj)
302 : {
303 171900 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
304 : {
305 6374 : m_osJson += "[";
306 6374 : m_abFirstMember.push_back(true);
307 : }
308 :
309 171900 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE;
310 :
311 171900 : json_object *poNewObj = json_object_new_array();
312 171900 : AppendObject(poNewObj);
313 171900 : m_apoCurObj.push_back(poNewObj);
314 : }
315 172657 : m_nDepth++;
316 : }
317 :
318 : /************************************************************************/
319 : /* StartArrayMember() */
320 : /************************************************************************/
321 :
322 514340 : void OGRJSONCollectionStreamingParser::StartArrayMember()
323 : {
324 514340 : if (m_poCurObj)
325 : {
326 509815 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE;
327 :
328 509815 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
329 : {
330 14837 : if (!m_abFirstMember.back())
331 8465 : m_osJson += ",";
332 14837 : m_abFirstMember.back() = false;
333 : }
334 : }
335 514340 : }
336 :
337 : /************************************************************************/
338 : /* EndArray() */
339 : /************************************************************************/
340 :
341 178874 : void OGRJSONCollectionStreamingParser::EndArray()
342 : {
343 178874 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
344 : {
345 6221 : TooComplex();
346 6221 : return;
347 : }
348 :
349 172653 : m_nDepth--;
350 172653 : if (m_nDepth == 1 && m_bInFeaturesArray)
351 : {
352 657 : m_bInFeaturesArray = false;
353 : }
354 171996 : else if (m_poCurObj)
355 : {
356 171898 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
357 : {
358 6374 : m_abFirstMember.pop_back();
359 6374 : m_osJson += "]";
360 : }
361 :
362 171898 : m_apoCurObj.pop_back();
363 : }
364 : }
365 :
366 : /************************************************************************/
367 : /* String() */
368 : /************************************************************************/
369 :
370 13667 : void OGRJSONCollectionStreamingParser::String(const char *pszValue, size_t nLen)
371 : {
372 13667 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
373 : {
374 0 : TooComplex();
375 0 : return;
376 : }
377 :
378 13667 : if (m_nDepth == 1 && m_bInType)
379 : {
380 659 : m_bIsTypeKnown = true;
381 659 : m_bIsFeatureCollection = strcmp(pszValue, "FeatureCollection") == 0;
382 : }
383 13008 : else if (m_poCurObj)
384 : {
385 12586 : if (m_bFirstPass)
386 : {
387 7655 : if (m_bInFeaturesArray)
388 7261 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField) + nLen;
389 :
390 7655 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
391 7655 : m_nCurObjMemEstimate += nLen + sizeof(void *);
392 : }
393 12586 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
394 : {
395 1821 : m_osJson += CPLJSonStreamingParser::GetSerializedString(pszValue);
396 : }
397 12586 : AppendObject(json_object_new_string(pszValue));
398 : }
399 : }
400 :
401 : /************************************************************************/
402 : /* Number() */
403 : /************************************************************************/
404 :
405 341984 : void OGRJSONCollectionStreamingParser::Number(const char *pszValue, size_t nLen)
406 : {
407 341984 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
408 : {
409 12443 : TooComplex();
410 12443 : return;
411 : }
412 :
413 329541 : if (m_poCurObj)
414 : {
415 329523 : if (m_bFirstPass)
416 : {
417 143423 : if (m_bInFeaturesArray)
418 : {
419 143363 : if (m_bInCoordinates)
420 0 : m_nTotalOGRFeatureMemEstimate += sizeof(double);
421 : else
422 143363 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
423 : }
424 :
425 143423 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
426 : }
427 329523 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
428 : {
429 9953 : m_osJson.append(pszValue, nLen);
430 : }
431 :
432 329523 : if (CPLGetValueType(pszValue) == CPL_VALUE_REAL)
433 : {
434 282638 : AppendObject(json_object_new_double(CPLAtof(pszValue)));
435 : }
436 46885 : else if (nLen == strlen("Infinity") && EQUAL(pszValue, "Infinity"))
437 : {
438 2 : AppendObject(json_object_new_double(
439 : std::numeric_limits<double>::infinity()));
440 : }
441 46883 : else if (nLen == strlen("-Infinity") && EQUAL(pszValue, "-Infinity"))
442 : {
443 2 : AppendObject(json_object_new_double(
444 2 : -std::numeric_limits<double>::infinity()));
445 : }
446 46881 : else if (nLen == strlen("NaN") && EQUAL(pszValue, "NaN"))
447 : {
448 2 : AppendObject(json_object_new_double(
449 : std::numeric_limits<double>::quiet_NaN()));
450 : }
451 : else
452 : {
453 46879 : AppendObject(json_object_new_int64(CPLAtoGIntBig(pszValue)));
454 : }
455 : }
456 : }
457 :
458 : /************************************************************************/
459 : /* Boolean() */
460 : /************************************************************************/
461 :
462 150 : void OGRJSONCollectionStreamingParser::Boolean(bool bVal)
463 : {
464 150 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
465 : {
466 0 : TooComplex();
467 0 : return;
468 : }
469 :
470 150 : if (m_poCurObj)
471 : {
472 138 : if (m_bFirstPass)
473 : {
474 76 : if (m_bInFeaturesArray)
475 62 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
476 :
477 76 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
478 : }
479 138 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
480 : {
481 4 : m_osJson += bVal ? "true" : "false";
482 : }
483 :
484 138 : AppendObject(json_object_new_boolean(bVal));
485 : }
486 : }
487 :
488 : /************************************************************************/
489 : /* Null() */
490 : /************************************************************************/
491 :
492 1317 : void OGRJSONCollectionStreamingParser::Null()
493 : {
494 1317 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
495 : {
496 0 : TooComplex();
497 0 : return;
498 : }
499 :
500 1317 : if (m_poCurObj)
501 : {
502 1315 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
503 : {
504 31 : m_osJson += "null";
505 : }
506 :
507 1315 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
508 1315 : AppendObject(nullptr);
509 : }
510 : }
511 :
512 : /************************************************************************/
513 : /* Exception() */
514 : /************************************************************************/
515 :
516 2 : void OGRJSONCollectionStreamingParser::Exception(const char *pszMessage)
517 : {
518 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMessage);
519 2 : }
|