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