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 882 : OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser( 55 882 : bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize) 56 : : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData), 57 882 : m_nMaxObjectSize(nMaxObjectSize) 58 : { 59 882 : } 60 : 61 : /************************************************************************/ 62 : /* ~OGRJSONCollectionStreamingParser() */ 63 : /************************************************************************/ 64 : 65 882 : OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser() 66 : { 67 882 : if (m_poRootObj) 68 3 : json_object_put(m_poRootObj); 69 882 : if (m_poCurObj && m_poCurObj != m_poRootObj) 70 3 : json_object_put(m_poCurObj); 71 882 : } 72 : 73 : /************************************************************************/ 74 : /* StealRootObject() */ 75 : /************************************************************************/ 76 : 77 372 : json_object *OGRJSONCollectionStreamingParser::StealRootObject() 78 : { 79 372 : json_object *poRet = m_poRootObj; 80 372 : if (m_poCurObj == m_poRootObj) 81 25 : m_poCurObj = nullptr; 82 372 : m_poRootObj = nullptr; 83 372 : return poRet; 84 : } 85 : 86 : /************************************************************************/ 87 : /* AppendObject() */ 88 : /************************************************************************/ 89 : 90 693855 : void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj) 91 : { 92 693855 : if (m_bKeySet) 93 : { 94 62939 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object); 95 62939 : json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(), 96 : poNewObj); 97 62939 : m_osCurKey.clear(); 98 62939 : m_bKeySet = false; 99 : } 100 : else 101 : { 102 630916 : CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array); 103 630916 : json_object_array_add(m_apoCurObj.back(), poNewObj); 104 : } 105 693855 : } 106 : 107 : /************************************************************************/ 108 : /* StartObject() */ 109 : /************************************************************************/ 110 : 111 20858 : void OGRJSONCollectionStreamingParser::StartObject() 112 : { 113 20858 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 114 : { 115 0 : TooComplex(); 116 0 : return; 117 : } 118 : 119 20858 : if (m_bInFeaturesArray && m_nDepth == 2) 120 : { 121 6466 : m_poCurObj = json_object_new_object(); 122 6466 : m_apoCurObj.push_back(m_poCurObj); 123 6466 : if (m_bStoreNativeData) 124 : { 125 1293 : m_osJson = "{"; 126 1293 : m_abFirstMember.push_back(true); 127 : } 128 6466 : m_bStartFeature = true; 129 : } 130 14392 : else if (m_poCurObj) 131 : { 132 13486 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 133 : { 134 2547 : m_osJson += "{"; 135 2547 : m_abFirstMember.push_back(true); 136 : } 137 : 138 13486 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE; 139 : 140 13486 : json_object *poNewObj = json_object_new_object(); 141 13486 : AppendObject(poNewObj); 142 13486 : m_apoCurObj.push_back(poNewObj); 143 : } 144 906 : else if (m_bFirstPass && m_nDepth == 0) 145 : { 146 375 : m_poRootObj = json_object_new_object(); 147 375 : m_apoCurObj.push_back(m_poRootObj); 148 375 : m_poCurObj = m_poRootObj; 149 : } 150 : 151 20858 : m_nDepth++; 152 : } 153 : 154 : /************************************************************************/ 155 : /* EndObject() */ 156 : /************************************************************************/ 157 : 158 20848 : void OGRJSONCollectionStreamingParser::EndObject() 159 : { 160 20848 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 161 : { 162 0 : TooComplex(); 163 0 : return; 164 : } 165 : 166 20848 : m_nDepth--; 167 : 168 20848 : if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj) 169 : { 170 6463 : 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 6463 : CPL_json_object_object_get(m_poCurObj, "type"); 180 12926 : if (poObjTypeObj && 181 6463 : json_object_get_type(poObjTypeObj) == json_type_string) 182 : { 183 6463 : const char *pszObjType = json_object_get_string(poObjTypeObj); 184 6463 : if (strcmp(pszObjType, "Feature") == 0) 185 : { 186 6463 : GotFeature(m_poCurObj, m_bFirstPass, m_osJson); 187 : } 188 : } 189 : 190 6463 : json_object_put(m_poCurObj); 191 6463 : m_poCurObj = nullptr; 192 6463 : m_apoCurObj.clear(); 193 6463 : m_nCurObjMemEstimate = 0; 194 6463 : m_bInCoordinates = false; 195 6463 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature); 196 6463 : m_osJson.clear(); 197 6463 : m_abFirstMember.clear(); 198 6463 : m_bEndFeature = true; 199 : } 200 14385 : else if (m_poCurObj) 201 : { 202 13509 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 203 : { 204 2547 : m_abFirstMember.pop_back(); 205 2547 : m_osJson += "}"; 206 : } 207 : 208 13509 : m_apoCurObj.pop_back(); 209 : } 210 876 : else if (m_nDepth == 1) 211 : { 212 66 : m_bInFeatures = false; 213 : } 214 : } 215 : 216 : /************************************************************************/ 217 : /* StartObjectMember() */ 218 : /************************************************************************/ 219 : 220 65183 : void OGRJSONCollectionStreamingParser::StartObjectMember(const char *pszKey, 221 : size_t nKeyLen) 222 : { 223 65183 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 224 : { 225 0 : TooComplex(); 226 0 : return; 227 : } 228 : 229 65183 : if (m_nDepth == 1) 230 : { 231 2420 : m_bInFeatures = strcmp(pszKey, "features") == 0; 232 2420 : m_bCanEasilyAppend = m_bInFeatures; 233 2420 : m_bInType = strcmp(pszKey, "type") == 0; 234 2420 : if (m_bInType || m_bInFeatures) 235 : { 236 1550 : m_poCurObj = nullptr; 237 1550 : m_apoCurObj.clear(); 238 1550 : m_nRootObjMemEstimate = m_nCurObjMemEstimate; 239 : } 240 870 : else if (m_poRootObj) 241 : { 242 380 : m_poCurObj = m_poRootObj; 243 380 : m_apoCurObj.clear(); 244 380 : m_apoCurObj.push_back(m_poCurObj); 245 380 : m_nCurObjMemEstimate = m_nRootObjMemEstimate; 246 : } 247 : } 248 62763 : else if (m_nDepth == 3 && m_bInFeaturesArray) 249 : { 250 45526 : m_bInCoordinates = strcmp(pszKey, "coordinates") == 0 || 251 22763 : strcmp(pszKey, "geometries") == 0; 252 : } 253 : 254 65183 : if (m_poCurObj) 255 : { 256 62939 : 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 62939 : m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE; 266 62939 : m_osCurKey.assign(pszKey, nKeyLen); 267 62939 : m_bKeySet = true; 268 : } 269 : } 270 : 271 : /************************************************************************/ 272 : /* StartArray() */ 273 : /************************************************************************/ 274 : 275 222774 : void OGRJSONCollectionStreamingParser::StartArray() 276 : { 277 222774 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 278 : { 279 0 : TooComplex(); 280 0 : return; 281 : } 282 : 283 222774 : if (m_nDepth == 1 && m_bInFeatures) 284 : { 285 775 : m_bInFeaturesArray = true; 286 : } 287 221999 : else if (m_poCurObj) 288 : { 289 221898 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 290 : { 291 16452 : m_osJson += "["; 292 16452 : m_abFirstMember.push_back(true); 293 : } 294 : 295 221898 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE; 296 : 297 221898 : json_object *poNewObj = json_object_new_array(); 298 221898 : AppendObject(poNewObj); 299 221898 : m_apoCurObj.push_back(poNewObj); 300 : } 301 222774 : m_nDepth++; 302 : } 303 : 304 : /************************************************************************/ 305 : /* StartArrayMember() */ 306 : /************************************************************************/ 307 : 308 637495 : void OGRJSONCollectionStreamingParser::StartArrayMember() 309 : { 310 637495 : if (m_poCurObj) 311 : { 312 630919 : m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE; 313 : 314 630919 : 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 637495 : } 322 : 323 : /************************************************************************/ 324 : /* EndArray() */ 325 : /************************************************************************/ 326 : 327 222762 : void OGRJSONCollectionStreamingParser::EndArray() 328 : { 329 222762 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 330 : { 331 0 : TooComplex(); 332 0 : return; 333 : } 334 : 335 222762 : m_nDepth--; 336 222762 : if (m_nDepth == 1 && m_bInFeaturesArray) 337 : { 338 771 : m_bInFeaturesArray = false; 339 : } 340 221991 : else if (m_poCurObj) 341 : { 342 221890 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 343 : { 344 16452 : m_abFirstMember.pop_back(); 345 16452 : m_osJson += "]"; 346 : } 347 : 348 221890 : m_apoCurObj.pop_back(); 349 : } 350 : } 351 : 352 : /************************************************************************/ 353 : /* String() */ 354 : /************************************************************************/ 355 : 356 23470 : void OGRJSONCollectionStreamingParser::String(const char *pszValue, size_t nLen) 357 : { 358 23470 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 359 : { 360 0 : TooComplex(); 361 0 : return; 362 : } 363 : 364 23470 : if (m_nDepth == 1 && m_bInType) 365 : { 366 775 : m_bIsTypeKnown = true; 367 775 : m_bIsFeatureCollection = strcmp(pszValue, "FeatureCollection") == 0; 368 : } 369 22695 : else if (m_poCurObj) 370 : { 371 22150 : if (m_bFirstPass) 372 : { 373 15662 : if (m_bInFeaturesArray) 374 15227 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField) + nLen; 375 : 376 15662 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 377 15662 : m_nCurObjMemEstimate += nLen + sizeof(void *); 378 : } 379 22150 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 380 : { 381 2969 : m_osJson += CPLJSonStreamingParser::GetSerializedString(pszValue); 382 : } 383 22150 : AppendObject(json_object_new_string(pszValue)); 384 : } 385 : } 386 : 387 : /************************************************************************/ 388 : /* Number() */ 389 : /************************************************************************/ 390 : 391 434817 : void OGRJSONCollectionStreamingParser::Number(const char *pszValue, size_t nLen) 392 : { 393 434817 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 394 : { 395 1 : TooComplex(); 396 1 : return; 397 : } 398 : 399 434816 : if (m_poCurObj) 400 : { 401 434802 : if (m_bFirstPass) 402 : { 403 226940 : if (m_bInFeaturesArray) 404 : { 405 226880 : if (m_bInCoordinates) 406 0 : m_nTotalOGRFeatureMemEstimate += sizeof(double); 407 : else 408 226880 : m_nTotalOGRFeatureMemEstimate += sizeof(OGRField); 409 : } 410 : 411 226940 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 412 : } 413 434802 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 414 : { 415 29349 : m_osJson.append(pszValue, nLen); 416 : } 417 : 418 434802 : if (CPLGetValueType(pszValue) == CPL_VALUE_REAL) 419 : { 420 374679 : AppendObject(json_object_new_double(CPLAtof(pszValue))); 421 : } 422 60123 : else if (nLen == strlen("Infinity") && EQUAL(pszValue, "Infinity")) 423 : { 424 2 : AppendObject(json_object_new_double( 425 : std::numeric_limits<double>::infinity())); 426 : } 427 60121 : 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 60119 : 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 60117 : 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 1383 : void OGRJSONCollectionStreamingParser::Null() 479 : { 480 1383 : if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize) 481 : { 482 0 : TooComplex(); 483 0 : return; 484 : } 485 : 486 1383 : if (m_poCurObj) 487 : { 488 1381 : if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3) 489 : { 490 39 : m_osJson += "null"; 491 : } 492 : 493 1381 : m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE; 494 1381 : 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 : }