LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/geojson - ogrjsoncollectionstreamingparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 192 209 91.9 %
Date: 2024-11-21 22:18:42 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 <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         849 : OGRJSONCollectionStreamingParser::OGRJSONCollectionStreamingParser(
      55         849 :     bool bFirstPass, bool bStoreNativeData, size_t nMaxObjectSize)
      56             :     : m_bFirstPass(bFirstPass), m_bStoreNativeData(bStoreNativeData),
      57         849 :       m_nMaxObjectSize(nMaxObjectSize)
      58             : {
      59         849 : }
      60             : 
      61             : /************************************************************************/
      62             : /*                   ~OGRJSONCollectionStreamingParser()                */
      63             : /************************************************************************/
      64             : 
      65         849 : OGRJSONCollectionStreamingParser::~OGRJSONCollectionStreamingParser()
      66             : {
      67         849 :     if (m_poRootObj)
      68           3 :         json_object_put(m_poRootObj);
      69         849 :     if (m_poCurObj && m_poCurObj != m_poRootObj)
      70           3 :         json_object_put(m_poCurObj);
      71         849 : }
      72             : 
      73             : /************************************************************************/
      74             : /*                          StealRootObject()                           */
      75             : /************************************************************************/
      76             : 
      77         357 : json_object *OGRJSONCollectionStreamingParser::StealRootObject()
      78             : {
      79         357 :     json_object *poRet = m_poRootObj;
      80         357 :     if (m_poCurObj == m_poRootObj)
      81          25 :         m_poCurObj = nullptr;
      82         357 :     m_poRootObj = nullptr;
      83         357 :     return poRet;
      84             : }
      85             : 
      86             : /************************************************************************/
      87             : /*                            AppendObject()                            */
      88             : /************************************************************************/
      89             : 
      90      688384 : void OGRJSONCollectionStreamingParser::AppendObject(json_object *poNewObj)
      91             : {
      92      688384 :     if (m_bKeySet)
      93             :     {
      94       61660 :         CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_object);
      95       61660 :         json_object_object_add(m_apoCurObj.back(), m_osCurKey.c_str(),
      96             :                                poNewObj);
      97       61660 :         m_osCurKey.clear();
      98       61660 :         m_bKeySet = false;
      99             :     }
     100             :     else
     101             :     {
     102      626724 :         CPLAssert(json_object_get_type(m_apoCurObj.back()) == json_type_array);
     103      626724 :         json_object_array_add(m_apoCurObj.back(), poNewObj);
     104             :     }
     105      688384 : }
     106             : 
     107             : /************************************************************************/
     108             : /*                            StartObject()                             */
     109             : /************************************************************************/
     110             : 
     111       20457 : void OGRJSONCollectionStreamingParser::StartObject()
     112             : {
     113       20457 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     114             :     {
     115           0 :         TooComplex();
     116           0 :         return;
     117             :     }
     118             : 
     119       20457 :     if (m_bInFeaturesArray && m_nDepth == 2)
     120             :     {
     121        6398 :         m_poCurObj = json_object_new_object();
     122        6398 :         m_apoCurObj.push_back(m_poCurObj);
     123        6398 :         if (m_bStoreNativeData)
     124             :         {
     125        1293 :             m_osJson = "{";
     126        1293 :             m_abFirstMember.push_back(true);
     127             :         }
     128        6398 :         m_bStartFeature = true;
     129             :     }
     130       14059 :     else if (m_poCurObj)
     131             :     {
     132       13186 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     133             :         {
     134        2547 :             m_osJson += "{";
     135        2547 :             m_abFirstMember.push_back(true);
     136             :         }
     137             : 
     138       13186 :         m_nCurObjMemEstimate += ESTIMATE_OBJECT_SIZE;
     139             : 
     140       13186 :         json_object *poNewObj = json_object_new_object();
     141       13186 :         AppendObject(poNewObj);
     142       13186 :         m_apoCurObj.push_back(poNewObj);
     143             :     }
     144         873 :     else if (m_bFirstPass && m_nDepth == 0)
     145             :     {
     146         360 :         m_poRootObj = json_object_new_object();
     147         360 :         m_apoCurObj.push_back(m_poRootObj);
     148         360 :         m_poCurObj = m_poRootObj;
     149             :     }
     150             : 
     151       20457 :     m_nDepth++;
     152             : }
     153             : 
     154             : /************************************************************************/
     155             : /*                             EndObject()                              */
     156             : /************************************************************************/
     157             : 
     158       20447 : void OGRJSONCollectionStreamingParser::EndObject()
     159             : {
     160       20447 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     161             :     {
     162           0 :         TooComplex();
     163           0 :         return;
     164             :     }
     165             : 
     166       20447 :     m_nDepth--;
     167             : 
     168       20447 :     if (m_bInFeaturesArray && m_nDepth == 2 && m_poCurObj)
     169             :     {
     170        6395 :         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        6395 :             CPL_json_object_object_get(m_poCurObj, "type");
     180       12790 :         if (poObjTypeObj &&
     181        6395 :             json_object_get_type(poObjTypeObj) == json_type_string)
     182             :         {
     183        6395 :             const char *pszObjType = json_object_get_string(poObjTypeObj);
     184        6395 :             if (strcmp(pszObjType, "Feature") == 0)
     185             :             {
     186        6395 :                 GotFeature(m_poCurObj, m_bFirstPass, m_osJson);
     187             :             }
     188             :         }
     189             : 
     190        6395 :         json_object_put(m_poCurObj);
     191        6395 :         m_poCurObj = nullptr;
     192        6395 :         m_apoCurObj.clear();
     193        6395 :         m_nCurObjMemEstimate = 0;
     194        6395 :         m_bInCoordinates = false;
     195        6395 :         m_nTotalOGRFeatureMemEstimate += sizeof(OGRFeature);
     196        6395 :         m_osJson.clear();
     197        6395 :         m_abFirstMember.clear();
     198        6395 :         m_bEndFeature = true;
     199             :     }
     200       14052 :     else if (m_poCurObj)
     201             :     {
     202       13209 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     203             :         {
     204        2547 :             m_abFirstMember.pop_back();
     205        2547 :             m_osJson += "}";
     206             :         }
     207             : 
     208       13209 :         m_apoCurObj.pop_back();
     209             :     }
     210         843 :     else if (m_nDepth == 1)
     211             :     {
     212          66 :         m_bInFeatures = false;
     213             :     }
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                         StartObjectMember()                          */
     218             : /************************************************************************/
     219             : 
     220       63824 : void OGRJSONCollectionStreamingParser::StartObjectMember(const char *pszKey,
     221             :                                                          size_t nKeyLen)
     222             : {
     223       63824 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     224             :     {
     225           0 :         TooComplex();
     226           0 :         return;
     227             :     }
     228             : 
     229       63824 :     if (m_nDepth == 1)
     230             :     {
     231        2334 :         m_bInFeatures = strcmp(pszKey, "features") == 0;
     232        2334 :         m_bCanEasilyAppend = m_bInFeatures;
     233        2334 :         m_bInType = strcmp(pszKey, "type") == 0;
     234        2334 :         if (m_bInType || m_bInFeatures)
     235             :         {
     236        1484 :             m_poCurObj = nullptr;
     237        1484 :             m_apoCurObj.clear();
     238        1484 :             m_nRootObjMemEstimate = m_nCurObjMemEstimate;
     239             :         }
     240         850 :         else if (m_poRootObj)
     241             :         {
     242         374 :             m_poCurObj = m_poRootObj;
     243         374 :             m_apoCurObj.clear();
     244         374 :             m_apoCurObj.push_back(m_poCurObj);
     245         374 :             m_nCurObjMemEstimate = m_nRootObjMemEstimate;
     246             :         }
     247             :     }
     248       61490 :     else if (m_nDepth == 3 && m_bInFeaturesArray)
     249             :     {
     250       45040 :         m_bInCoordinates = strcmp(pszKey, "coordinates") == 0 ||
     251       22520 :                            strcmp(pszKey, "geometries") == 0;
     252             :     }
     253             : 
     254       63824 :     if (m_poCurObj)
     255             :     {
     256       61660 :         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       61660 :         m_nCurObjMemEstimate += ESTIMATE_OBJECT_ELT_SIZE;
     266       61660 :         m_osCurKey.assign(pszKey, nKeyLen);
     267       61660 :         m_bKeySet = true;
     268             :     }
     269             : }
     270             : 
     271             : /************************************************************************/
     272             : /*                             StartArray()                             */
     273             : /************************************************************************/
     274             : 
     275      221221 : void OGRJSONCollectionStreamingParser::StartArray()
     276             : {
     277      221221 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     278             :     {
     279           0 :         TooComplex();
     280           0 :         return;
     281             :     }
     282             : 
     283      221221 :     if (m_nDepth == 1 && m_bInFeatures)
     284             :     {
     285         742 :         m_bInFeaturesArray = true;
     286             :     }
     287      220479 :     else if (m_poCurObj)
     288             :     {
     289      220380 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     290             :         {
     291       16452 :             m_osJson += "[";
     292       16452 :             m_abFirstMember.push_back(true);
     293             :         }
     294             : 
     295      220380 :         m_nCurObjMemEstimate += ESTIMATE_ARRAY_SIZE;
     296             : 
     297      220380 :         json_object *poNewObj = json_object_new_array();
     298      220380 :         AppendObject(poNewObj);
     299      220380 :         m_apoCurObj.push_back(poNewObj);
     300             :     }
     301      221221 :     m_nDepth++;
     302             : }
     303             : 
     304             : /************************************************************************/
     305             : /*                          StartArrayMember()                          */
     306             : /************************************************************************/
     307             : 
     308      633233 : void OGRJSONCollectionStreamingParser::StartArrayMember()
     309             : {
     310      633233 :     if (m_poCurObj)
     311             :     {
     312      626727 :         m_nCurObjMemEstimate += ESTIMATE_ARRAY_ELT_SIZE;
     313             : 
     314      626727 :         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      633233 : }
     322             : 
     323             : /************************************************************************/
     324             : /*                               EndArray()                             */
     325             : /************************************************************************/
     326             : 
     327      221209 : void OGRJSONCollectionStreamingParser::EndArray()
     328             : {
     329      221209 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     330             :     {
     331           0 :         TooComplex();
     332           0 :         return;
     333             :     }
     334             : 
     335      221209 :     m_nDepth--;
     336      221209 :     if (m_nDepth == 1 && m_bInFeaturesArray)
     337             :     {
     338         738 :         m_bInFeaturesArray = false;
     339             :     }
     340      220471 :     else if (m_poCurObj)
     341             :     {
     342      220372 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     343             :         {
     344       16452 :             m_abFirstMember.pop_back();
     345       16452 :             m_osJson += "]";
     346             :         }
     347             : 
     348      220372 :         m_apoCurObj.pop_back();
     349             :     }
     350             : }
     351             : 
     352             : /************************************************************************/
     353             : /*                              String()                                */
     354             : /************************************************************************/
     355             : 
     356       22832 : void OGRJSONCollectionStreamingParser::String(const char *pszValue, size_t nLen)
     357             : {
     358       22832 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     359             :     {
     360           0 :         TooComplex();
     361           0 :         return;
     362             :     }
     363             : 
     364       22832 :     if (m_nDepth == 1 && m_bInType)
     365             :     {
     366         742 :         m_bIsTypeKnown = true;
     367         742 :         m_bIsFeatureCollection = strcmp(pszValue, "FeatureCollection") == 0;
     368             :     }
     369       22090 :     else if (m_poCurObj)
     370             :     {
     371       21559 :         if (m_bFirstPass)
     372             :         {
     373       15441 :             if (m_bInFeaturesArray)
     374       15013 :                 m_nTotalOGRFeatureMemEstimate += sizeof(OGRField) + nLen;
     375             : 
     376       15441 :             m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
     377       15441 :             m_nCurObjMemEstimate += nLen + sizeof(void *);
     378             :         }
     379       21559 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     380             :         {
     381        2969 :             m_osJson += CPLJSonStreamingParser::GetSerializedString(pszValue);
     382             :         }
     383       21559 :         AppendObject(json_object_new_string(pszValue));
     384             :     }
     385             : }
     386             : 
     387             : /************************************************************************/
     388             : /*                              Number()                                */
     389             : /************************************************************************/
     390             : 
     391      431789 : void OGRJSONCollectionStreamingParser::Number(const char *pszValue, size_t nLen)
     392             : {
     393      431789 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     394             :     {
     395           1 :         TooComplex();
     396           1 :         return;
     397             :     }
     398             : 
     399      431788 :     if (m_poCurObj)
     400             :     {
     401      431774 :         if (m_bFirstPass)
     402             :         {
     403      226240 :             if (m_bInFeaturesArray)
     404             :             {
     405      226180 :                 if (m_bInCoordinates)
     406           0 :                     m_nTotalOGRFeatureMemEstimate += sizeof(double);
     407             :                 else
     408      226180 :                     m_nTotalOGRFeatureMemEstimate += sizeof(OGRField);
     409             :             }
     410             : 
     411      226240 :             m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
     412             :         }
     413      431774 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     414             :         {
     415       29349 :             m_osJson.append(pszValue, nLen);
     416             :         }
     417             : 
     418      431774 :         if (CPLGetValueType(pszValue) == CPL_VALUE_REAL)
     419             :         {
     420      371970 :             AppendObject(json_object_new_double(CPLAtof(pszValue)));
     421             :         }
     422       59804 :         else if (nLen == strlen("Infinity") && EQUAL(pszValue, "Infinity"))
     423             :         {
     424           2 :             AppendObject(json_object_new_double(
     425             :                 std::numeric_limits<double>::infinity()));
     426             :         }
     427       59802 :         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       59800 :         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       59798 :             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        1349 : void OGRJSONCollectionStreamingParser::Null()
     479             : {
     480        1349 :     if (m_nMaxObjectSize > 0 && m_nCurObjMemEstimate > m_nMaxObjectSize)
     481             :     {
     482           0 :         TooComplex();
     483           0 :         return;
     484             :     }
     485             : 
     486        1349 :     if (m_poCurObj)
     487             :     {
     488        1347 :         if (m_bInFeaturesArray && m_bStoreNativeData && m_nDepth >= 3)
     489             :         {
     490          39 :             m_osJson += "null";
     491             :         }
     492             : 
     493        1347 :         m_nCurObjMemEstimate += ESTIMATE_BASE_OBJECT_SIZE;
     494        1347 :         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 : }

Generated by: LCOV version 1.14