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 959 : OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser( 58 959 : bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize) 59 : : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData), 60 959 : m_nMaxObjectSize(nMaxObjectSize) 61 : { 62 959 : } 63 : 64 : /************************************************************************/ 65 : /* ~OGRJSONCollectionStreamingParser() */ 66 : /************************************************************************/ 67 : 68 959 : OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser() 69 : { 70 959 : if (m_poRootObj) 71 6 : json_object_put(m_poRootObj); 72 959 : if (m_poCurObj && m_poCurObj != m_poRootObj) 73 5 : json_object_put(m_poCurObj); 74 959 : } 75 : 76 : /************************************************************************/ 77 : /* StealRootObject() */ 78 : /************************************************************************/ 79 : 80 408 : json_object *OGRJSONCollectionStreamingParser::StealRootObject() 81 : { 82 408 : json_object *poRet = m_poRootObj; 83 408 : if (m_poCurObj == m_poRootObj) 84 25 : m_poCurObj = nullptr; 85 408 : m_poRootObj = nullptr; 86 408 : return poRet; 87 : } 88 : 89 : /************************************************************************/ 90 : /* AppendObject() */ 91 : /************************************************************************/ 92 : 93 714838 : void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj) 94 : { 95 714838 : if (m_bKeySet) 96 : { 97 64135 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object); 98 64135 : json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(), 99 : poNewObj); 100 64135 : m_osCurKey.clear(); 101 64135 : m_bKeySet = false; 102 : } 103 : else 104 : { 105 650703 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array); 106 650703 : json_object_array_add(m_apoCurObj.back(), poNewObj); 107 : } 108 714838 : } 109 : 110 : /************************************************************************/ 111 : /* StartObject() */ 112 : /************************************************************************/ 113 : 114 21393 : void OGRJSONCollectionStreamingParser::StartObject() 115 : { 116 21393 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 117 : { 118 0 : TooComplex(); 119 0 : return; 120 : } 121 : 122 21393 : if (m_bInFeaturesArray && m_nDepth == 2) 123 : { 124 6538 : m_poCurObj = json_object_new_object(); 125 6538 : m_apoCurObj.push_back(m_poCurObj); 126 6538 : if (m_bStoreNativeData) 127 : { 128 1293 : m_osJson = "{"; 129 1293 : m_abFirstMember.push_back(true); 130 : } 131 6538 : m_bStartFeature = true; 132 : } 133 14855 : else if (m_poCurObj) 134 : { 135 13870 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 136 : { 137 2547 : m_osJson += "{"; 138 2547 : m_abFirstMember.push_back(true); 139 : } 140 : 141 13870 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE; 142 : 143 13870 : json_object *poNewObj = json_object_new_object(); 144 13870 : AppendObject(poNewObj); 145 13870 : m_apoCurObj.push_back(poNewObj); 146 : } 147 985 : else if (m_bFirstPass && m_nDepth == 0) 148 : { 149 414 : m_poRootObj = json_object_new_object(); 150 414 : m_apoCurObj.push_back(m_poRootObj); 151 414 : m_poCurObj = m_poRootObj; 152 : } 153 : 154 21393 : m_nDepth++; 155 : } 156 : 157 : /************************************************************************/ 158 : /* EndObject() */ 159 : /************************************************************************/ 160 : 161 21374 : void OGRJSONCollectionStreamingParser::EndObject() 162 : { 163 21374 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 164 : { 165 0 : TooComplex(); 166 0 : return; 167 : } 168 : 169 21374 : m_nDepth--; 170 : 171 21374 : if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj) 172 : { 173 6533 : 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 6533 : CPL_json_object_object_get(m_poCurObj, "type"); 183 13066 : if (poObjTypeObj && 184 6533 : json_object_get_type(poObjTypeObj) == json_type_string) 185 : { 186 6533 : const char *pszObjType = json_object_get_string(poObjTypeObj); 187 6533 : if (strcmp(pszObjType, "Feature") == 0) 188 : { 189 6533 : GotFeature(m_poCurObj, m_bFirstPass, m_osJson); 190 : } 191 : } 192 : 193 6533 : json_object_put(m_poCurObj); 194 6533 : m_poCurObj = nullptr; 195 6533 : m_apoCurObj.clear(); 196 6533 : m_nCurObjMemEstimate = 0; 197 6533 : m_bInCoordinates = false; 198 6533 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature); 199 6533 : m_osJson.clear(); 200 6533 : m_abFirstMember.clear(); 201 6533 : m_bEndFeature = true; 202 : } 203 14841 : else if (m_poCurObj) 204 : { 205 13889 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 206 : { 207 2547 : m_abFirstMember.pop_back(); 208 2547 : m_osJson += "}"; 209 : } 210 : 211 13889 : m_apoCurObj.pop_back(); 212 : } 213 952 : 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 66587 : void OGRJSONCollectionStreamingParser::StartObjectMember(std::string_view sKey) 226 : { 227 66587 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 228 : { 229 0 : TooComplex(); 230 0 : return; 231 : } 232 : 233 66587 : if (m_nDepth == 1) 234 : { 235 2663 : m_bInFeatures = sKey == "features"; 236 2663 : m_bInMeasures = sKey == "measures"; 237 2663 : m_bCanEasilyAppend = m_bInFeatures; 238 2663 : m_bInType = sKey == "type"; 239 2663 : if (m_bInType || m_bInFeatures) 240 : { 241 1694 : m_poCurObj = nullptr; 242 1694 : m_apoCurObj.clear(); 243 1694 : m_nRootObjMemEstimate = m_nCurObjMemEstimate; 244 : } 245 969 : else if (m_poRootObj) 246 : { 247 433 : m_poCurObj = m_poRootObj; 248 433 : m_apoCurObj.clear(); 249 433 : m_apoCurObj.push_back(m_poCurObj); 250 433 : m_nCurObjMemEstimate = m_nRootObjMemEstimate; 251 : } 252 : } 253 63924 : else if (m_nDepth == 2 && m_bInMeasures) 254 : { 255 24 : m_bInMeasuresEnabled = sKey == "enabled"; 256 : } 257 63900 : else if (m_nDepth == 3 && m_bInFeaturesArray) 258 : { 259 23272 : m_bInCoordinates = sKey == "coordinates" || sKey == "geometries"; 260 : } 261 : 262 66587 : if (m_poCurObj) 263 : { 264 64138 : 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 64138 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE; 273 64138 : m_osCurKey = sKey; 274 64138 : m_bKeySet = true; 275 : } 276 : } 277 : 278 : /************************************************************************/ 279 : /* StartArray() */ 280 : /************************************************************************/ 281 : 282 229285 : void OGRJSONCollectionStreamingParser::StartArray() 283 : { 284 229285 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 285 : { 286 0 : TooComplex(); 287 0 : return; 288 : } 289 : 290 229285 : if (m_nDepth == 1 && m_bInFeatures) 291 : { 292 847 : m_bInFeaturesArray = true; 293 : } 294 228438 : else if (m_poCurObj) 295 : { 296 228305 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 297 : { 298 16452 : m_osJson += "["; 299 16452 : m_abFirstMember.push_back(true); 300 : } 301 : 302 228305 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE; 303 : 304 228305 : json_object *poNewObj = json_object_new_array(); 305 228305 : AppendObject(poNewObj); 306 228305 : m_apoCurObj.push_back(poNewObj); 307 : } 308 229285 : m_nDepth++; 309 : } 310 : 311 : /************************************************************************/ 312 : /* StartArrayMember() */ 313 : /************************************************************************/ 314 : 315 657527 : void OGRJSONCollectionStreamingParser::StartArrayMember() 316 : { 317 657527 : if (m_poCurObj) 318 : { 319 650704 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE; 320 : 321 650704 : 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 657527 : } 329 : 330 : /************************************************************************/ 331 : /* EndArray() */ 332 : /************************************************************************/ 333 : 334 229270 : void OGRJSONCollectionStreamingParser::EndArray() 335 : { 336 229270 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 337 : { 338 0 : TooComplex(); 339 0 : return; 340 : } 341 : 342 229270 : m_nDepth--; 343 229270 : if (m_nDepth == 1 && m_bInFeaturesArray) 344 : { 345 841 : m_bInFeaturesArray = false; 346 : } 347 228429 : else if (m_poCurObj) 348 : { 349 228296 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 350 : { 351 16452 : m_abFirstMember.pop_back(); 352 16452 : m_osJson += "]"; 353 : } 354 : 355 228296 : m_apoCurObj.pop_back(); 356 : } 357 : } 358 : 359 : /************************************************************************/ 360 : /* String() */ 361 : /************************************************************************/ 362 : 363 24368 : void OGRJSONCollectionStreamingParser::String(std::string_view sValue) 364 : { 365 24368 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 366 : { 367 0 : TooComplex(); 368 0 : return; 369 : } 370 : 371 24368 : if (m_nDepth == 1 && m_bInType) 372 : { 373 847 : m_bIsTypeKnown = true; 374 847 : m_bIsFeatureCollection = sValue == "FeatureCollection"; 375 : } 376 23521 : else if (m_poCurObj) 377 : { 378 22782 : if (m_bFirstPass) 379 : { 380 16102 : if (m_bInFeaturesArray) 381 15426 : m_nTotalOGRFeatureMemEstimate += 382 15426 : sizeof(OGRField) + sValue.size(); 383 : 384 16102 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 385 16102 : m_nCurObjMemEstimate += sValue.size() + sizeof(void *); 386 : } 387 22782 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 388 : { 389 2969 : m_osJson += CPLJSonStreamingParser::GetSerializedString(sValue); 390 : } 391 22782 : if (sValue.size() < static_cast<size_t>(INT_MAX - 1)) 392 22782 : AppendObject(json_object_new_string_len( 393 22782 : 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 448198 : void OGRJSONCollectionStreamingParser::Number(std::string_view sValue) 405 : { 406 448198 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 407 : { 408 1 : TooComplex(); 409 1 : return; 410 : } 411 : 412 448197 : if (m_poCurObj) 413 : { 414 448183 : if (m_bFirstPass) 415 : { 416 233641 : if (m_bInFeaturesArray) 417 : { 418 233566 : if (m_bInCoordinates) 419 0 : m_nTotalOGRFeatureMemEstimate += sizeof(double); 420 : else 421 233566 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField); 422 : } 423 : 424 233641 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 425 : } 426 448183 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 427 : { 428 29349 : m_osJson.append(sValue); 429 : } 430 : 431 463178 : if (sValue.size() == strlen("Infinity") && 432 14995 : EQUALN(sValue.data(), "Infinity", strlen("Infinity"))) 433 : { 434 2 : AppendObject(json_object_new_double( 435 : std::numeric_limits<double>::infinity())); 436 : } 437 492079 : else if (sValue.size() == strlen("-Infinity") && 438 43898 : EQUALN(sValue.data(), "-Infinity", strlen("-Infinity"))) 439 : { 440 2 : AppendObject(json_object_new_double( 441 2 : -std::numeric_limits<double>::infinity())); 442 : } 443 456829 : else if (sValue.size() == strlen("NaN") && 444 8650 : EQUALN(sValue.data(), "NaN", strlen("NaN"))) 445 : { 446 2 : AppendObject(json_object_new_double( 447 : std::numeric_limits<double>::quiet_NaN())); 448 : } 449 508311 : else if (sValue.find_first_of("eE.") != std::string::npos || 450 60134 : sValue.size() >= 20) 451 : { 452 388046 : double dfValue = 0; 453 388046 : const fast_float::parse_options options{ 454 : fast_float::chars_format::general, '.'}; 455 : auto answer = fast_float::from_chars_advanced( 456 388046 : sValue.data(), sValue.data() + sValue.size(), dfValue, options); 457 776092 : if (answer.ec == std::errc() && 458 388046 : answer.ptr == sValue.data() + sValue.size()) 459 : { 460 388044 : AppendObject(json_object_new_double(dfValue)); 461 : } 462 : else 463 : { 464 2 : EmitException( 465 4 : ("Unrecognized number: " + std::string(sValue)).c_str()); 466 : } 467 : } 468 : else 469 : { 470 60131 : GIntBig nValue = 0; 471 : auto answer = std::from_chars( 472 60131 : sValue.data(), sValue.data() + sValue.size(), nValue); 473 120262 : if (answer.ec == std::errc() && 474 60131 : answer.ptr == sValue.data() + sValue.size()) 475 : { 476 60130 : AppendObject(json_object_new_int64(nValue)); 477 : } 478 : else 479 : { 480 1 : EmitException( 481 2 : ("Unrecognized number: " + std::string(sValue)).c_str()); 482 : } 483 : } 484 : } 485 : } 486 : 487 : /************************************************************************/ 488 : /* Boolean() */ 489 : /************************************************************************/ 490 : 491 186 : void OGRJSONCollectionStreamingParser::Boolean(bool bVal) 492 : { 493 186 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 494 : { 495 0 : TooComplex(); 496 0 : return; 497 : } 498 : 499 186 : if (m_bInMeasuresEnabled) 500 8 : m_bHasTopLevelMeasures = bVal; 501 : 502 186 : if (m_poCurObj) 503 : { 504 170 : if (m_bFirstPass) 505 : { 506 94 : if (m_bInFeaturesArray) 507 76 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField); 508 : 509 94 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 510 : } 511 170 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 512 : { 513 4 : m_osJson += bVal ? "true" : "false"; 514 : } 515 : 516 170 : AppendObject(json_object_new_boolean(bVal)); 517 : } 518 : } 519 : 520 : /************************************************************************/ 521 : /* Null() */ 522 : /************************************************************************/ 523 : 524 1533 : void OGRJSONCollectionStreamingParser::Null() 525 : { 526 1533 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 527 : { 528 0 : TooComplex(); 529 0 : return; 530 : } 531 : 532 1533 : if (m_poCurObj) 533 : { 534 1531 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 535 : { 536 39 : m_osJson += "null"; 537 : } 538 : 539 1531 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 540 1531 : AppendObject(nullptr); 541 : } 542 : } 543 : 544 : /************************************************************************/ 545 : /* Exception() */ 546 : /************************************************************************/ 547 : 548 5 : void OGRJSONCollectionStreamingParser::Exception(const char *pszMessage) 549 : { 550 5 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMessage); 551 5 : }