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 962 : OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser( 58 962 : bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize) 59 : : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData), 60 962 : m_nMaxObjectSize(nMaxObjectSize) 61 : { 62 962 : } 63 : 64 : /************************************************************************/ 65 : /* ~OGRJSONCollectionStreamingParser() */ 66 : /************************************************************************/ 67 : 68 962 : OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser() 69 : { 70 962 : if (m_poRootObj) 71 6 : json_object_put(m_poRootObj); 72 962 : if (m_poCurObj && m_poCurObj != m_poRootObj) 73 5 : json_object_put(m_poCurObj); 74 962 : } 75 : 76 : /************************************************************************/ 77 : /* StealRootObject() */ 78 : /************************************************************************/ 79 : 80 410 : json_object *OGRJSONCollectionStreamingParser::StealRootObject() 81 : { 82 410 : json_object *poRet = m_poRootObj; 83 410 : if (m_poCurObj == m_poRootObj) 84 25 : m_poCurObj = nullptr; 85 410 : m_poRootObj = nullptr; 86 410 : return poRet; 87 : } 88 : 89 : /************************************************************************/ 90 : /* AppendObject() */ 91 : /************************************************************************/ 92 : 93 714896 : void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj) 94 : { 95 714896 : if (m_bKeySet) 96 : { 97 64172 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object); 98 64172 : json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(), 99 : poNewObj); 100 64172 : m_osCurKey.clear(); 101 64172 : m_bKeySet = false; 102 : } 103 : else 104 : { 105 650724 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array); 106 650724 : json_object_array_add(m_apoCurObj.back(), poNewObj); 107 : } 108 714896 : } 109 : 110 : /************************************************************************/ 111 : /* StartObject() */ 112 : /************************************************************************/ 113 : 114 21414 : void OGRJSONCollectionStreamingParser::StartObject() 115 : { 116 21414 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 117 : { 118 0 : TooComplex(); 119 0 : return; 120 : } 121 : 122 21414 : if (m_bInFeaturesArray && m_nDepth == 2) 123 : { 124 6543 : m_poCurObj = json_object_new_object(); 125 6543 : m_apoCurObj.push_back(m_poCurObj); 126 6543 : if (m_bStoreNativeData) 127 : { 128 1293 : m_osJson = "{"; 129 1293 : m_abFirstMember.push_back(true); 130 : } 131 6543 : m_bStartFeature = true; 132 : } 133 14871 : else if (m_poCurObj) 134 : { 135 13883 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 136 : { 137 2547 : m_osJson += "{"; 138 2547 : m_abFirstMember.push_back(true); 139 : } 140 : 141 13883 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE; 142 : 143 13883 : json_object *poNewObj = json_object_new_object(); 144 13883 : AppendObject(poNewObj); 145 13883 : m_apoCurObj.push_back(poNewObj); 146 : } 147 988 : else if (m_bFirstPass && m_nDepth == 0) 148 : { 149 416 : m_poRootObj = json_object_new_object(); 150 416 : m_apoCurObj.push_back(m_poRootObj); 151 416 : m_poCurObj = m_poRootObj; 152 : } 153 : 154 21414 : m_nDepth++; 155 : } 156 : 157 : /************************************************************************/ 158 : /* EndObject() */ 159 : /************************************************************************/ 160 : 161 21395 : void OGRJSONCollectionStreamingParser::EndObject() 162 : { 163 21395 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 164 : { 165 0 : TooComplex(); 166 0 : return; 167 : } 168 : 169 21395 : m_nDepth--; 170 : 171 21395 : if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj) 172 : { 173 6538 : 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 6538 : CPL_json_object_object_get(m_poCurObj, "type"); 183 13076 : if (poObjTypeObj && 184 6538 : json_object_get_type(poObjTypeObj) == json_type_string) 185 : { 186 6538 : const char *pszObjType = json_object_get_string(poObjTypeObj); 187 6538 : if (strcmp(pszObjType, "Feature") == 0) 188 : { 189 6538 : GotFeature(m_poCurObj, m_bFirstPass, m_osJson); 190 : } 191 : } 192 : 193 6538 : json_object_put(m_poCurObj); 194 6538 : m_poCurObj = nullptr; 195 6538 : m_apoCurObj.clear(); 196 6538 : m_nCurObjMemEstimate = 0; 197 6538 : m_bInCoordinates = false; 198 6538 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature); 199 6538 : m_osJson.clear(); 200 6538 : m_abFirstMember.clear(); 201 6538 : m_bEndFeature = true; 202 : } 203 14857 : else if (m_poCurObj) 204 : { 205 13902 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 206 : { 207 2547 : m_abFirstMember.pop_back(); 208 2547 : m_osJson += "}"; 209 : } 210 : 211 13902 : m_apoCurObj.pop_back(); 212 : } 213 955 : 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 66631 : void OGRJSONCollectionStreamingParser::StartObjectMember(std::string_view sKey) 226 : { 227 66631 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 228 : { 229 0 : TooComplex(); 230 0 : return; 231 : } 232 : 233 66631 : if (m_nDepth == 1) 234 : { 235 2672 : m_bInFeatures = sKey == "features"; 236 2672 : m_bInMeasures = sKey == "measures"; 237 2672 : m_bCanEasilyAppend = m_bInFeatures; 238 2672 : m_bInType = sKey == "type"; 239 2672 : if (m_bInType || m_bInFeatures) 240 : { 241 1700 : m_poCurObj = nullptr; 242 1700 : m_apoCurObj.clear(); 243 1700 : m_nRootObjMemEstimate = m_nCurObjMemEstimate; 244 : } 245 972 : else if (m_poRootObj) 246 : { 247 435 : m_poCurObj = m_poRootObj; 248 435 : m_apoCurObj.clear(); 249 435 : m_apoCurObj.push_back(m_poCurObj); 250 435 : m_nCurObjMemEstimate = m_nRootObjMemEstimate; 251 : } 252 : } 253 63959 : else if (m_nDepth == 2 && m_bInMeasures) 254 : { 255 24 : m_bInMeasuresEnabled = sKey == "enabled"; 256 : } 257 63935 : else if (m_nDepth == 3 && m_bInFeaturesArray) 258 : { 259 23287 : m_bInCoordinates = sKey == "coordinates" || sKey == "geometries"; 260 : } 261 : 262 66631 : if (m_poCurObj) 263 : { 264 64175 : 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 64175 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE; 273 64175 : m_osCurKey = sKey; 274 64175 : m_bKeySet = true; 275 : } 276 : } 277 : 278 : /************************************************************************/ 279 : /* StartArray() */ 280 : /************************************************************************/ 281 : 282 229299 : void OGRJSONCollectionStreamingParser::StartArray() 283 : { 284 229299 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 285 : { 286 0 : TooComplex(); 287 0 : return; 288 : } 289 : 290 229299 : if (m_nDepth == 1 && m_bInFeatures) 291 : { 292 850 : m_bInFeaturesArray = true; 293 : } 294 228449 : else if (m_poCurObj) 295 : { 296 228316 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 297 : { 298 16452 : m_osJson += "["; 299 16452 : m_abFirstMember.push_back(true); 300 : } 301 : 302 228316 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE; 303 : 304 228316 : json_object *poNewObj = json_object_new_array(); 305 228316 : AppendObject(poNewObj); 306 228316 : m_apoCurObj.push_back(poNewObj); 307 : } 308 229299 : m_nDepth++; 309 : } 310 : 311 : /************************************************************************/ 312 : /* StartArrayMember() */ 313 : /************************************************************************/ 314 : 315 657553 : void OGRJSONCollectionStreamingParser::StartArrayMember() 316 : { 317 657553 : if (m_poCurObj) 318 : { 319 650725 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE; 320 : 321 650725 : 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 657553 : } 329 : 330 : /************************************************************************/ 331 : /* EndArray() */ 332 : /************************************************************************/ 333 : 334 229284 : void OGRJSONCollectionStreamingParser::EndArray() 335 : { 336 229284 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 337 : { 338 0 : TooComplex(); 339 0 : return; 340 : } 341 : 342 229284 : m_nDepth--; 343 229284 : if (m_nDepth == 1 && m_bInFeaturesArray) 344 : { 345 844 : m_bInFeaturesArray = false; 346 : } 347 228440 : else if (m_poCurObj) 348 : { 349 228307 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 350 : { 351 16452 : m_abFirstMember.pop_back(); 352 16452 : m_osJson += "]"; 353 : } 354 : 355 228307 : m_apoCurObj.pop_back(); 356 : } 357 : } 358 : 359 : /************************************************************************/ 360 : /* String() */ 361 : /************************************************************************/ 362 : 363 24385 : void OGRJSONCollectionStreamingParser::String(std::string_view sValue) 364 : { 365 24385 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 366 : { 367 0 : TooComplex(); 368 0 : return; 369 : } 370 : 371 24385 : if (m_nDepth == 1 && m_bInType) 372 : { 373 850 : m_bIsTypeKnown = true; 374 850 : m_bIsFeatureCollection = sValue == "FeatureCollection"; 375 : } 376 23535 : else if (m_poCurObj) 377 : { 378 22795 : if (m_bFirstPass) 379 : { 380 16111 : if (m_bInFeaturesArray) 381 15432 : m_nTotalOGRFeatureMemEstimate += 382 15432 : sizeof(OGRField) + sValue.size(); 383 : 384 16111 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 385 16111 : m_nCurObjMemEstimate += sValue.size() + sizeof(void *); 386 : } 387 22795 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 388 : { 389 2969 : m_osJson += CPLJSonStreamingParser::GetSerializedString(sValue); 390 : } 391 22795 : if (sValue.size() < static_cast<size_t>(INT_MAX - 1)) 392 22795 : AppendObject(json_object_new_string_len( 393 22795 : 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 448219 : void OGRJSONCollectionStreamingParser::Number(std::string_view sValue) 407 : { 408 448219 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 409 : { 410 1 : TooComplex(); 411 1 : return; 412 : } 413 : 414 448218 : if (m_poCurObj) 415 : { 416 448204 : if (m_bFirstPass) 417 : { 418 233656 : if (m_bInFeaturesArray) 419 : { 420 233581 : if (m_bInCoordinates) 421 0 : m_nTotalOGRFeatureMemEstimate += sizeof(double); 422 : else 423 233581 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField); 424 : } 425 : 426 233656 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 427 : } 428 448204 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 429 : { 430 29349 : m_osJson.append(sValue); 431 : } 432 : 433 463200 : 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 492101 : 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 456865 : else if (sValue.size() == strlen("NaN") && 446 8665 : EQUALN(sValue.data(), "NaN", strlen("NaN"))) 447 : { 448 2 : AppendObject(json_object_new_double( 449 : std::numeric_limits<double>::quiet_NaN())); 450 : } 451 508334 : else if (sValue.find_first_of("eE.") != std::string::npos || 452 60136 : sValue.size() >= 20) 453 : { 454 388065 : double dfValue = 0; 455 388065 : const fast_float::parse_options options{ 456 : fast_float::chars_format::general, '.'}; 457 : auto answer = fast_float::from_chars_advanced( 458 388065 : sValue.data(), sValue.data() + sValue.size(), dfValue, options); 459 776130 : if (answer.ec == std::errc() && 460 388065 : answer.ptr == sValue.data() + sValue.size()) 461 : { 462 388063 : 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 : }