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-10-21 22:35:35 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         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 : }

Generated by: LCOV version 1.14