LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrjsoncollectionstreamingparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 218 236 92.4 %
Date: 2025-12-03 21:30:40 Functions: 15 16 93.8 %

          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             : // recent libc++ std::from_chars() involve unsigned integer overflow
     405             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
     406      448198 : void OGRJSONCollectionStreamingParser::Number(std::string_view sValue)
     407             : {
     408      448198 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     409             :     {
     410           1 :         TooComplex();
     411           1 :         return;
     412             :     }
     413             : 
     414      448197 :     if (m_poCurObj)
     415             :     {
     416      448183 :         if (m_bFirstPass)
     417             :         {
     418      233641 :             if (m_bInFeaturesArray)
     419             :             {
     420      233566 :                 if (m_bInCoordinates)
     421           0 :                     m_nTotalOGRFeatureMemEstimate += sizeof(double);
     422             :                 else
     423      233566 :                     m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
     424             :             }
     425             : 
     426      233641 :             m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
     427             :         }
     428      448183 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     429             :         {
     430       29349 :             m_osJson.append(sValue);
     431             :         }
     432             : 
     433      463178 :         if (sValue.size() == strlen("Infinity") &&
     434       14995 :             EQUALN(sValue.data(), "Infinity", strlen("Infinity")))
     435             :         {
     436           2 :             AppendObject(json_object_new_double(
     437             :                 std::numeric_limits<double>::infinity()));
     438             :         }
     439      492079 :         else if (sValue.size() == strlen("-Infinity") &&
     440       43898 :                  EQUALN(sValue.data(), "-Infinity", strlen("-Infinity")))
     441             :         {
     442           2 :             AppendObject(json_object_new_double(
     443           2 :                 -std::numeric_limits<double>::infinity()));
     444             :         }
     445      456829 :         else if (sValue.size() == strlen("NaN") &&
     446        8650 :                  EQUALN(sValue.data(), "NaN", strlen("NaN")))
     447             :         {
     448           2 :             AppendObject(json_object_new_double(
     449             :                 std::numeric_limits<double>::quiet_NaN()));
     450             :         }
     451      508311 :         else if (sValue.find_first_of("eE.") != std::string::npos ||
     452       60134 :                  sValue.size() >= 20)
     453             :         {
     454      388046 :             double dfValue = 0;
     455      388046 :             const fast_float::parse_options options{
     456             :                 fast_float::chars_format::general, '.'};
     457             :             auto answer = fast_float::from_chars_advanced(
     458      388046 :                 sValue.data(), sValue.data() + sValue.size(), dfValue, options);
     459      776092 :             if (answer.ec == std::errc() &&
     460      388046 :                 answer.ptr == sValue.data() + sValue.size())
     461             :             {
     462      388044 :                 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       60131 :             GIntBig nValue = 0;
     473             :             auto answer = std::from_chars(
     474       60131 :                 sValue.data(), sValue.data() + sValue.size(), nValue);
     475      120262 :             if (answer.ec == std::errc() &&
     476       60131 :                 answer.ptr == sValue.data() + sValue.size())
     477             :             {
     478       60130 :                 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 : }

Generated by: LCOV version 1.14