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 957 : OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser( 58 957 : bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize) 59 : : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData), 60 957 : m_nMaxObjectSize(nMaxObjectSize) 61 : { 62 957 : } 63 : 64 : /************************************************************************/ 65 : /* ~OGRJSONCollectionStreamingParser() */ 66 : /************************************************************************/ 67 : 68 957 : OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser() 69 : { 70 957 : if (m_poRootObj) 71 6 : json_object_put(m_poRootObj); 72 957 : if (m_poCurObj && m_poCurObj != m_poRootObj) 73 5 : json_object_put(m_poCurObj); 74 957 : } 75 : 76 : /************************************************************************/ 77 : /* StealRootObject() */ 78 : /************************************************************************/ 79 : 80 407 : json_object *OGRJSONCollectionStreamingParser::StealRootObject() 81 : { 82 407 : json_object *poRet = m_poRootObj; 83 407 : if (m_poCurObj == m_poRootObj) 84 25 : m_poCurObj = nullptr; 85 407 : m_poRootObj = nullptr; 86 407 : return poRet; 87 : } 88 : 89 : /************************************************************************/ 90 : /* AppendObject() */ 91 : /************************************************************************/ 92 : 93 706597 : void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj) 94 : { 95 706597 : if (m_bKeySet) 96 : { 97 64120 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object); 98 64120 : json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(), 99 : poNewObj); 100 64120 : m_osCurKey.clear(); 101 64120 : m_bKeySet = false; 102 : } 103 : else 104 : { 105 642477 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array); 106 642477 : json_object_array_add(m_apoCurObj.back(), poNewObj); 107 : } 108 706597 : } 109 : 110 : /************************************************************************/ 111 : /* StartObject() */ 112 : /************************************************************************/ 113 : 114 21381 : void OGRJSONCollectionStreamingParser::StartObject() 115 : { 116 21381 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 117 : { 118 0 : TooComplex(); 119 0 : return; 120 : } 121 : 122 21381 : if (m_bInFeaturesArray && m_nDepth == 2) 123 : { 124 6536 : m_poCurObj = json_object_new_object(); 125 6536 : m_apoCurObj.push_back(m_poCurObj); 126 6536 : if (m_bStoreNativeData) 127 : { 128 1293 : m_osJson = "{"; 129 1293 : m_abFirstMember.push_back(true); 130 : } 131 6536 : m_bStartFeature = true; 132 : } 133 14845 : else if (m_poCurObj) 134 : { 135 13864 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 136 : { 137 2547 : m_osJson += "{"; 138 2547 : m_abFirstMember.push_back(true); 139 : } 140 : 141 13864 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE; 142 : 143 13864 : json_object *poNewObj = json_object_new_object(); 144 13864 : AppendObject(poNewObj); 145 13864 : m_apoCurObj.push_back(poNewObj); 146 : } 147 981 : else if (m_bFirstPass && m_nDepth == 0) 148 : { 149 413 : m_poRootObj = json_object_new_object(); 150 413 : m_apoCurObj.push_back(m_poRootObj); 151 413 : m_poCurObj = m_poRootObj; 152 : } 153 : 154 21381 : m_nDepth++; 155 : } 156 : 157 : /************************************************************************/ 158 : /* EndObject() */ 159 : /************************************************************************/ 160 : 161 21362 : void OGRJSONCollectionStreamingParser::EndObject() 162 : { 163 21362 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 164 : { 165 0 : TooComplex(); 166 0 : return; 167 : } 168 : 169 21362 : m_nDepth--; 170 : 171 21362 : if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj) 172 : { 173 6531 : 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 6531 : CPL_json_object_object_get(m_poCurObj, "type"); 183 13062 : if (poObjTypeObj && 184 6531 : json_object_get_type(poObjTypeObj) == json_type_string) 185 : { 186 6531 : const char *pszObjType = json_object_get_string(poObjTypeObj); 187 6531 : if (strcmp(pszObjType, "Feature") == 0) 188 : { 189 6531 : GotFeature(m_poCurObj, m_bFirstPass, m_osJson); 190 : } 191 : } 192 : 193 6531 : json_object_put(m_poCurObj); 194 6531 : m_poCurObj = nullptr; 195 6531 : m_apoCurObj.clear(); 196 6531 : m_nCurObjMemEstimate = 0; 197 6531 : m_bInCoordinates = false; 198 6531 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature); 199 6531 : m_osJson.clear(); 200 6531 : m_abFirstMember.clear(); 201 6531 : m_bEndFeature = true; 202 : } 203 14831 : else if (m_poCurObj) 204 : { 205 13883 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 206 : { 207 2547 : m_abFirstMember.pop_back(); 208 2547 : m_osJson += "}"; 209 : } 210 : 211 13883 : m_apoCurObj.pop_back(); 212 : } 213 948 : else if (m_nDepth == 1) 214 : { 215 70 : m_bInFeatures = false; 216 70 : m_bInMeasures = false; 217 70 : m_bInMeasuresEnabled = false; 218 : } 219 : } 220 : 221 : /************************************************************************/ 222 : /* StartObjectMember() */ 223 : /************************************************************************/ 224 : 225 66563 : void OGRJSONCollectionStreamingParser::StartObjectMember(std::string_view sKey) 226 : { 227 66563 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 228 : { 229 0 : TooComplex(); 230 0 : return; 231 : } 232 : 233 66563 : if (m_nDepth == 1) 234 : { 235 2655 : m_bInFeatures = sKey == "features"; 236 2655 : m_bInMeasures = sKey == "measures"; 237 2655 : m_bCanEasilyAppend = m_bInFeatures; 238 2655 : m_bInType = sKey == "type"; 239 2655 : if (m_bInType || m_bInFeatures) 240 : { 241 1690 : m_poCurObj = nullptr; 242 1690 : m_apoCurObj.clear(); 243 1690 : m_nRootObjMemEstimate = m_nCurObjMemEstimate; 244 : } 245 965 : else if (m_poRootObj) 246 : { 247 431 : m_poCurObj = m_poRootObj; 248 431 : m_apoCurObj.clear(); 249 431 : m_apoCurObj.push_back(m_poCurObj); 250 431 : m_nCurObjMemEstimate = m_nRootObjMemEstimate; 251 : } 252 : } 253 63908 : else if (m_nDepth == 2 && m_bInMeasures) 254 : { 255 24 : m_bInMeasuresEnabled = sKey == "enabled"; 256 : } 257 63884 : else if (m_nDepth == 3 && m_bInFeaturesArray) 258 : { 259 23266 : m_bInCoordinates = sKey == "coordinates" || sKey == "geometries"; 260 : } 261 : 262 66563 : if (m_poCurObj) 263 : { 264 64123 : 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 64123 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE; 273 64123 : m_osCurKey = sKey; 274 64123 : m_bKeySet = true; 275 : } 276 : } 277 : 278 : /************************************************************************/ 279 : /* StartArray() */ 280 : /************************************************************************/ 281 : 282 226375 : void OGRJSONCollectionStreamingParser::StartArray() 283 : { 284 226375 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 285 : { 286 0 : TooComplex(); 287 0 : return; 288 : } 289 : 290 226375 : if (m_nDepth == 1 && m_bInFeatures) 291 : { 292 845 : m_bInFeaturesArray = true; 293 : } 294 225530 : else if (m_poCurObj) 295 : { 296 225397 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 297 : { 298 16452 : m_osJson += "["; 299 16452 : m_abFirstMember.push_back(true); 300 : } 301 : 302 225397 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE; 303 : 304 225397 : json_object *poNewObj = json_object_new_array(); 305 225397 : AppendObject(poNewObj); 306 225397 : m_apoCurObj.push_back(poNewObj); 307 : } 308 226375 : m_nDepth++; 309 : } 310 : 311 : /************************************************************************/ 312 : /* StartArrayMember() */ 313 : /************************************************************************/ 314 : 315 649299 : void OGRJSONCollectionStreamingParser::StartArrayMember() 316 : { 317 649299 : if (m_poCurObj) 318 : { 319 642478 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE; 320 : 321 642478 : 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 649299 : } 329 : 330 : /************************************************************************/ 331 : /* EndArray() */ 332 : /************************************************************************/ 333 : 334 226360 : void OGRJSONCollectionStreamingParser::EndArray() 335 : { 336 226360 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 337 : { 338 0 : TooComplex(); 339 0 : return; 340 : } 341 : 342 226360 : m_nDepth--; 343 226360 : if (m_nDepth == 1 && m_bInFeaturesArray) 344 : { 345 839 : m_bInFeaturesArray = false; 346 : } 347 225521 : else if (m_poCurObj) 348 : { 349 225388 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 350 : { 351 16452 : m_abFirstMember.pop_back(); 352 16452 : m_osJson += "]"; 353 : } 354 : 355 225388 : m_apoCurObj.pop_back(); 356 : } 357 : } 358 : 359 : /************************************************************************/ 360 : /* String() */ 361 : /************************************************************************/ 362 : 363 24356 : void OGRJSONCollectionStreamingParser::String(std::string_view sValue) 364 : { 365 24356 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 366 : { 367 0 : TooComplex(); 368 0 : return; 369 : } 370 : 371 24356 : if (m_nDepth == 1 && m_bInType) 372 : { 373 845 : m_bIsTypeKnown = true; 374 845 : m_bIsFeatureCollection = sValue == "FeatureCollection"; 375 : } 376 23511 : else if (m_poCurObj) 377 : { 378 22775 : if (m_bFirstPass) 379 : { 380 16097 : if (m_bInFeaturesArray) 381 15424 : m_nTotalOGRFeatureMemEstimate += 382 15424 : sizeof(OGRField) + sValue.size(); 383 : 384 16097 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 385 16097 : m_nCurObjMemEstimate += sValue.size() + sizeof(void *); 386 : } 387 22775 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 388 : { 389 2969 : m_osJson += CPLJSonStreamingParser::GetSerializedString(sValue); 390 : } 391 22775 : if (sValue.size() < static_cast<size_t>(INT_MAX - 1)) 392 22775 : AppendObject(json_object_new_string_len( 393 22775 : 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 442878 : void OGRJSONCollectionStreamingParser::Number(std::string_view sValue) 405 : { 406 442878 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 407 : { 408 1 : TooComplex(); 409 1 : return; 410 : } 411 : 412 442877 : if (m_poCurObj) 413 : { 414 442863 : if (m_bFirstPass) 415 : { 416 230981 : if (m_bInFeaturesArray) 417 : { 418 230906 : if (m_bInCoordinates) 419 0 : m_nTotalOGRFeatureMemEstimate += sizeof(double); 420 : else 421 230906 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField); 422 : } 423 : 424 230981 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 425 : } 426 442863 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 427 : { 428 29349 : m_osJson.append(sValue); 429 : } 430 : 431 457340 : if (sValue.size() == strlen("Infinity") && 432 14477 : EQUALN(sValue.data(), "Infinity", strlen("Infinity"))) 433 : { 434 2 : AppendObject(json_object_new_double( 435 : std::numeric_limits<double>::infinity())); 436 : } 437 482069 : else if (sValue.size() == strlen("-Infinity") && 438 39208 : EQUALN(sValue.data(), "-Infinity", strlen("-Infinity"))) 439 : { 440 2 : AppendObject(json_object_new_double( 441 2 : -std::numeric_limits<double>::infinity())); 442 : } 443 451509 : 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 502991 : else if (sValue.find_first_of("eE.") != std::string::npos || 450 60134 : sValue.size() >= 20) 451 : { 452 382726 : double dfValue = 0; 453 382726 : const fast_float::parse_options options{ 454 : fast_float::chars_format::general, '.'}; 455 : auto answer = fast_float::from_chars_advanced( 456 382726 : sValue.data(), sValue.data() + sValue.size(), dfValue, options); 457 765452 : if (answer.ec == std::errc() && 458 382726 : answer.ptr == sValue.data() + sValue.size()) 459 : { 460 382724 : 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 : }