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