LCOV - code coverage report
Current view: top level - port - cpl_json_streaming_parser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 440 449 98.0 %
Date: 2025-10-22 13:51:22 Functions: 23 24 95.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  JSon streaming parser
       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             : /*! @cond Doxygen_Suppress */
      14             : 
      15             : #include <assert.h>
      16             : #include <ctype.h>   // isdigit...
      17             : #include <stdio.h>   // snprintf
      18             : #include <string.h>  // strlen
      19             : #include <vector>
      20             : #include <string>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_string.h"
      24             : #include "cpl_json_streaming_parser.h"
      25             : 
      26             : /************************************************************************/
      27             : /*                       CPLJSonStreamingParser()                       */
      28             : /************************************************************************/
      29             : 
      30        3381 : CPLJSonStreamingParser::CPLJSonStreamingParser()
      31             : {
      32        3381 :     m_aState.push_back(INIT);
      33        3381 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                      ~CPLJSonStreamingParser()                       */
      37             : /************************************************************************/
      38             : 
      39        3381 : CPLJSonStreamingParser::~CPLJSonStreamingParser()
      40             : {
      41        3381 : }
      42             : 
      43             : /************************************************************************/
      44             : /*                           SetMaxDepth()                              */
      45             : /************************************************************************/
      46             : 
      47           2 : void CPLJSonStreamingParser::SetMaxDepth(size_t nVal)
      48             : {
      49           2 :     m_nMaxDepth = nVal;
      50           2 : }
      51             : 
      52             : /************************************************************************/
      53             : /*                         SetMaxStringSize()                           */
      54             : /************************************************************************/
      55             : 
      56           2 : void CPLJSonStreamingParser::SetMaxStringSize(size_t nVal)
      57             : {
      58           2 :     m_nMaxStringSize = nVal;
      59           2 : }
      60             : 
      61             : /************************************************************************/
      62             : /*                                Reset()                               */
      63             : /************************************************************************/
      64             : 
      65          23 : void CPLJSonStreamingParser::Reset()
      66             : {
      67          23 :     m_bExceptionOccurred = false;
      68          23 :     m_bElementFound = false;
      69          23 :     m_nLastChar = 0;
      70          23 :     m_nLineCounter = 1;
      71          23 :     m_nCharCounter = 1;
      72          23 :     m_aState.clear();
      73          23 :     m_aState.push_back(INIT);
      74          23 :     m_osToken.clear();
      75          23 :     m_abArrayState.clear();
      76          23 :     m_aeObjectState.clear();
      77          23 :     m_bInStringEscape = false;
      78          23 :     m_bInUnicode = false;
      79          23 :     m_osUnicodeHex.clear();
      80          23 : }
      81             : 
      82             : /************************************************************************/
      83             : /*                              AdvanceChar()                           */
      84             : /************************************************************************/
      85             : 
      86    14956400 : void CPLJSonStreamingParser::AdvanceChar(const char *&pStr, size_t &nLength)
      87             : {
      88    14956400 :     if (*pStr == 13 && m_nLastChar != 10)
      89             :     {
      90          26 :         m_nLineCounter++;
      91          26 :         m_nCharCounter = 0;
      92             :     }
      93    14956400 :     else if (*pStr == 10 && m_nLastChar != 13)
      94             :     {
      95      201213 :         m_nLineCounter++;
      96      201213 :         m_nCharCounter = 0;
      97             :     }
      98    14956400 :     m_nLastChar = *pStr;
      99             : 
     100    14956400 :     pStr++;
     101    14956400 :     nLength--;
     102    14956400 :     m_nCharCounter++;
     103    14956400 : }
     104             : 
     105             : /************************************************************************/
     106             : /*                               SkipSpace()                            */
     107             : /************************************************************************/
     108             : 
     109    15927300 : void CPLJSonStreamingParser::SkipSpace(const char *&pStr, size_t &nLength)
     110             : {
     111    15927300 :     while (nLength > 0 && isspace(static_cast<unsigned char>(*pStr)))
     112             :     {
     113    13801400 :         AdvanceChar(pStr, nLength);
     114             :     }
     115     2125900 : }
     116             : 
     117             : /************************************************************************/
     118             : /*                             EmitException()                          */
     119             : /************************************************************************/
     120             : 
     121          88 : bool CPLJSonStreamingParser::EmitException(const char *pszMessage)
     122             : {
     123          88 :     m_bExceptionOccurred = true;
     124          88 :     CPLString osMsg;
     125             :     osMsg.Printf("At line %d, character %d: %s", m_nLineCounter, m_nCharCounter,
     126          88 :                  pszMessage);
     127          88 :     Exception(osMsg.c_str());
     128         176 :     return false;
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*                             StopParsing()                            */
     133             : /************************************************************************/
     134             : 
     135        2268 : void CPLJSonStreamingParser::StopParsing()
     136             : {
     137        2268 :     m_bStopParsing = true;
     138        2268 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                          EmitUnexpectedChar()                        */
     142             : /************************************************************************/
     143             : 
     144          34 : bool CPLJSonStreamingParser::EmitUnexpectedChar(char ch,
     145             :                                                 const char *pszExpecting)
     146             : {
     147             :     char szMessage[64];
     148          34 :     if (pszExpecting)
     149             :     {
     150           7 :         snprintf(szMessage, sizeof(szMessage),
     151             :                  "Unexpected character (%c). Expecting %s", ch, pszExpecting);
     152             :     }
     153             :     else
     154             :     {
     155          27 :         snprintf(szMessage, sizeof(szMessage), "Unexpected character (%c)", ch);
     156             :     }
     157          68 :     return EmitException(szMessage);
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                            IsValidNewToken()                         */
     162             : /************************************************************************/
     163             : 
     164      793187 : static bool IsValidNewToken(char ch)
     165             : {
     166      566485 :     return ch == '[' || ch == '{' || ch == '"' || ch == '-' || ch == '.' ||
     167      371093 :            isdigit(static_cast<unsigned char>(ch)) || ch == 't' || ch == 'f' ||
     168     1359670 :            ch == 'n' || ch == 'i' || ch == 'I' || ch == 'N';
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                             StartNewToken()                          */
     173             : /************************************************************************/
     174             : 
     175      793173 : bool CPLJSonStreamingParser::StartNewToken(const char *&pStr, size_t &nLength)
     176             : {
     177      793173 :     char ch = *pStr;
     178      793173 :     if (ch == '{')
     179             :     {
     180       24059 :         if (m_aState.size() == m_nMaxDepth)
     181             :         {
     182           1 :             return EmitException("Too many nested objects and/or arrays");
     183             :         }
     184       24058 :         StartObject();
     185       24058 :         m_aeObjectState.push_back(WAITING_KEY);
     186       24058 :         m_aState.push_back(OBJECT);
     187       24058 :         AdvanceChar(pStr, nLength);
     188             :     }
     189      769114 :     else if (ch == '"')
     190             :     {
     191       97354 :         m_aState.push_back(STRING);
     192       97354 :         AdvanceChar(pStr, nLength);
     193             :     }
     194      671760 :     else if (ch == '[')
     195             :     {
     196      226702 :         if (m_aState.size() == m_nMaxDepth)
     197             :         {
     198           1 :             return EmitException("Too many nested objects and/or arrays");
     199             :         }
     200      226701 :         StartArray();
     201      226701 :         m_abArrayState.push_back(ArrayState::INIT);
     202      226701 :         m_aState.push_back(ARRAY);
     203      226701 :         AdvanceChar(pStr, nLength);
     204             :     }
     205      445058 :     else if (ch == '-' || ch == '.' ||
     206      371079 :              isdigit(static_cast<unsigned char>(ch)) || ch == 'i' ||
     207        1793 :              ch == 'I' || ch == 'N')
     208             :     {
     209      443267 :         m_aState.push_back(NUMBER);
     210             :     }
     211        1791 :     else if (ch == 't')
     212             :     {
     213         202 :         m_aState.push_back(STATE_TRUE);
     214             :     }
     215        1589 :     else if (ch == 'f')
     216             :     {
     217          11 :         m_aState.push_back(STATE_FALSE);
     218             :     }
     219        1578 :     else if (ch == 'n')
     220             :     {
     221        1578 :         m_aState.push_back(STATE_NULL); /* might be nan */
     222             :     }
     223             :     else
     224             :     {
     225           0 :         assert(false);
     226             :     }
     227      793171 :     return true;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                       CheckAndEmitTrueFalseOrNull()                  */
     232             : /************************************************************************/
     233             : 
     234        1779 : bool CPLJSonStreamingParser::CheckAndEmitTrueFalseOrNull(char ch)
     235             : {
     236        1779 :     State eCurState = currentState();
     237             : 
     238        1779 :     if (eCurState == STATE_TRUE)
     239             :     {
     240         199 :         if (m_osToken == "true")
     241             :         {
     242         198 :             Boolean(true);
     243             :         }
     244             :         else
     245             :         {
     246           1 :             return EmitUnexpectedChar(ch);
     247             :         }
     248             :     }
     249        1580 :     else if (eCurState == STATE_FALSE)
     250             :     {
     251           9 :         if (m_osToken == "false")
     252             :         {
     253           8 :             Boolean(false);
     254             :         }
     255             :         else
     256             :         {
     257           1 :             return EmitUnexpectedChar(ch);
     258             :         }
     259             :     }
     260             :     else /* if( eCurState == STATE_NULL ) */
     261             :     {
     262        1571 :         if (m_osToken == "null")
     263             :         {
     264        1570 :             Null();
     265             :         }
     266             :         else
     267             :         {
     268           1 :             return EmitUnexpectedChar(ch);
     269             :         }
     270             :     }
     271        1776 :     m_aState.pop_back();
     272        1776 :     m_osToken.clear();
     273        1776 :     return true;
     274             : }
     275             : 
     276             : /************************************************************************/
     277             : /*                           CheckStackEmpty()                          */
     278             : /************************************************************************/
     279             : 
     280          32 : bool CPLJSonStreamingParser::CheckStackEmpty()
     281             : {
     282          32 :     if (!m_aeObjectState.empty())
     283             :     {
     284           1 :         return EmitException("Unterminated object");
     285             :     }
     286          31 :     else if (!m_abArrayState.empty())
     287             :     {
     288           1 :         return EmitException("Unterminated array");
     289             :     }
     290          30 :     return true;
     291             : }
     292             : 
     293             : /************************************************************************/
     294             : /*                           IsHighSurrogate()                          */
     295             : /************************************************************************/
     296             : 
     297          39 : static bool IsHighSurrogate(unsigned uc)
     298             : {
     299          39 :     return (uc & 0xFC00) == 0xD800;
     300             : }
     301             : 
     302             : /************************************************************************/
     303             : /*                           IsLowSurrogate()                           */
     304             : /************************************************************************/
     305             : 
     306          16 : static bool IsLowSurrogate(unsigned uc)
     307             : {
     308          16 :     return (uc & 0xFC00) == 0xDC00;
     309             : }
     310             : 
     311             : /************************************************************************/
     312             : /*                         GetSurrogatePair()                           */
     313             : /************************************************************************/
     314             : 
     315           2 : static unsigned GetSurrogatePair(unsigned hi, unsigned lo)
     316             : {
     317           2 :     return ((hi & 0x3FF) << 10) + (lo & 0x3FF) + 0x10000;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                            IsHexDigit()                              */
     322             : /************************************************************************/
     323             : 
     324         121 : static bool IsHexDigit(char ch)
     325             : {
     326         142 :     return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     327         142 :            (ch >= 'A' && ch <= 'F');
     328             : }
     329             : 
     330             : /************************************************************************/
     331             : /*                           HexToDecimal()                             */
     332             : /************************************************************************/
     333             : 
     334         212 : static unsigned HexToDecimal(char ch)
     335             : {
     336         212 :     if (ch >= '0' && ch <= '9')
     337         167 :         return ch - '0';
     338          45 :     if (ch >= 'a' && ch <= 'f')
     339          16 :         return 10 + ch - 'a';
     340             :     // if (ch >= 'A' && ch <= 'F' )
     341          29 :     return 10 + ch - 'A';
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                            getUCSChar()                              */
     346             : /************************************************************************/
     347             : 
     348          53 : static unsigned getUCSChar(const std::string &unicode4HexChar)
     349             : {
     350          53 :     return (HexToDecimal(unicode4HexChar[0]) << 12) |
     351          53 :            (HexToDecimal(unicode4HexChar[1]) << 8) |
     352          53 :            (HexToDecimal(unicode4HexChar[2]) << 4) |
     353          53 :            (HexToDecimal(unicode4HexChar[3]));
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                           DecodeUnicode()                            */
     358             : /************************************************************************/
     359             : 
     360          24 : void CPLJSonStreamingParser::DecodeUnicode()
     361             : {
     362          24 :     constexpr char szReplacementUTF8[] = "\xEF\xBF\xBD";
     363             :     unsigned nUCSChar;
     364          24 :     if (m_osUnicodeHex.size() == 8)
     365             :     {
     366           4 :         unsigned nUCSHigh = getUCSChar(m_osUnicodeHex);
     367           4 :         assert(IsHighSurrogate(nUCSHigh));
     368           4 :         unsigned nUCSLow = getUCSChar(m_osUnicodeHex.substr(4));
     369           4 :         if (IsLowSurrogate(nUCSLow))
     370             :         {
     371           2 :             nUCSChar = GetSurrogatePair(nUCSHigh, nUCSLow);
     372             :         }
     373             :         else
     374             :         {
     375             :             /* Invalid code point. Insert the replacement char */
     376           2 :             nUCSChar = 0xFFFFFFFFU;
     377             :         }
     378             :     }
     379             :     else
     380             :     {
     381          20 :         assert(m_osUnicodeHex.size() == 4);
     382          20 :         nUCSChar = getUCSChar(m_osUnicodeHex);
     383             :     }
     384             : 
     385          24 :     if (nUCSChar < 0x80)
     386             :     {
     387          10 :         m_osToken += static_cast<char>(nUCSChar);
     388             :     }
     389          14 :     else if (nUCSChar < 0x800)
     390             :     {
     391           2 :         m_osToken += static_cast<char>(0xC0 | (nUCSChar >> 6));
     392           2 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     393             :     }
     394          12 :     else if (IsLowSurrogate(nUCSChar) || IsHighSurrogate(nUCSChar))
     395             :     {
     396             :         /* Invalid code point. Insert the replacement char */
     397           8 :         m_osToken += szReplacementUTF8;
     398             :     }
     399           4 :     else if (nUCSChar < 0x10000)
     400             :     {
     401           0 :         m_osToken += static_cast<char>(0xE0 | (nUCSChar >> 12));
     402           0 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 6) & 0x3F));
     403           0 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     404             :     }
     405           4 :     else if (nUCSChar < 0x110000)
     406             :     {
     407           2 :         m_osToken += static_cast<char>(0xF0 | ((nUCSChar >> 18) & 0x07));
     408           2 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 12) & 0x3F));
     409           2 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 6) & 0x3F));
     410           2 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     411             :     }
     412             :     else
     413             :     {
     414             :         /* Invalid code point. Insert the replacement char */
     415           2 :         m_osToken += szReplacementUTF8;
     416             :     }
     417             : 
     418          24 :     m_bInUnicode = false;
     419          24 :     m_osUnicodeHex.clear();
     420          24 : }
     421             : 
     422             : /************************************************************************/
     423             : /*                              Parse()                                 */
     424             : /************************************************************************/
     425             : 
     426        5609 : bool CPLJSonStreamingParser::Parse(std::string_view sStr, bool bFinished)
     427             : {
     428        5609 :     const char *pStr = sStr.data();
     429        5609 :     size_t nLength = sStr.size();
     430             :     while (true)
     431             :     {
     432     2129530 :         if (m_bExceptionOccurred || m_bStopParsing)
     433        2272 :             return false;
     434     2127260 :         State eCurState = currentState();
     435     2127260 :         if (eCurState == INIT)
     436             :         {
     437        4419 :             SkipSpace(pStr, nLength);
     438        4419 :             if (nLength == 0)
     439        1129 :                 return true;
     440        3290 :             if (m_bElementFound || !IsValidNewToken(*pStr))
     441             :             {
     442           9 :                 return EmitUnexpectedChar(*pStr);
     443             :             }
     444        3281 :             if (!StartNewToken(pStr, nLength))
     445             :             {
     446           2 :                 return false;
     447             :             }
     448        3279 :             m_bElementFound = true;
     449             :         }
     450     2122840 :         else if (eCurState == NUMBER)
     451             :         {
     452      443475 :             if (m_osToken.empty())
     453             :             {
     454             :                 // Optimization to avoid using temporary buffer
     455             :                 auto nPos =
     456      443267 :                     std::string_view(pStr, nLength).find_first_of(" \t\r\n,}]");
     457      443267 :                 if (nPos != std::string::npos)
     458             :                 {
     459      443088 :                     Number(std::string_view(pStr, nPos));
     460      443088 :                     m_aState.pop_back();
     461      443088 :                     pStr += nPos;
     462      443088 :                     nLength -= nPos;
     463      443088 :                     SkipSpace(pStr, nLength);
     464      443088 :                     continue;
     465             :                 }
     466             :             }
     467             : 
     468        2992 :             while (nLength)
     469             :             {
     470        2789 :                 char ch = *pStr;
     471        2789 :                 if (ch == '+' || ch == '-' ||
     472        2753 :                     isdigit(static_cast<unsigned char>(ch)) || ch == '.' ||
     473         256 :                     ch == 'e' || ch == 'E')
     474             :                 {
     475        2535 :                     if (m_osToken.size() == 1024)
     476             :                     {
     477           0 :                         return EmitException("Too many characters in number");
     478             :                     }
     479        2535 :                     m_osToken += ch;
     480             :                 }
     481         254 :                 else if (isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
     482          78 :                          ch == '}' || ch == ']')
     483             :                 {
     484         179 :                     SkipSpace(pStr, nLength);
     485         179 :                     break;
     486             :                 }
     487             :                 else
     488             :                 {
     489          75 :                     CPLString extendedToken(m_osToken + ch);
     490          75 :                     if ((STARTS_WITH_CI("Infinity", extendedToken) &&
     491          31 :                          m_osToken.size() + 1 <= strlen("Infinity")) ||
     492          44 :                         (STARTS_WITH_CI("-Infinity", extendedToken) &&
     493         150 :                          m_osToken.size() + 1 <= strlen("-Infinity")) ||
     494          13 :                         (STARTS_WITH_CI("NaN", extendedToken) &&
     495           8 :                          m_osToken.size() + 1 <= strlen("NaN")))
     496             :                     {
     497          70 :                         m_osToken += ch;
     498             :                     }
     499             :                     else
     500             :                     {
     501           5 :                         return EmitUnexpectedChar(ch);
     502             :                     }
     503             :                 }
     504        2605 :                 AdvanceChar(pStr, nLength);
     505             :             }
     506             : 
     507         382 :             if (nLength != 0 || bFinished)
     508             :             {
     509         179 :                 const char firstCh = m_osToken[0];
     510         179 :                 if (firstCh == 'i' || firstCh == 'I')
     511             :                 {
     512           3 :                     if (!EQUAL(m_osToken.c_str(), "Infinity"))
     513             :                     {
     514           1 :                         return EmitException("Invalid number");
     515             :                     }
     516             :                 }
     517         176 :                 else if (firstCh == '-')
     518             :                 {
     519          33 :                     if (m_osToken[1] == 'i' || m_osToken[1] == 'I')
     520             :                     {
     521           3 :                         if (!EQUAL(m_osToken.c_str(), "-Infinity"))
     522             :                         {
     523           1 :                             return EmitException("Invalid number");
     524             :                         }
     525             :                     }
     526             :                 }
     527         143 :                 else if (firstCh == 'n' || firstCh == 'N')
     528             :                 {
     529           3 :                     if (m_osToken[1] == 'a' || m_osToken[1] == 'A')
     530             :                     {
     531           3 :                         if (!EQUAL(m_osToken.c_str(), "NaN"))
     532             :                         {
     533           1 :                             return EmitException("Invalid number");
     534             :                         }
     535             :                     }
     536             :                 }
     537             : 
     538         176 :                 Number(m_osToken);
     539         176 :                 m_osToken.clear();
     540         176 :                 m_aState.pop_back();
     541             :             }
     542             : 
     543         379 :             if (nLength == 0)
     544             :             {
     545         214 :                 if (bFinished)
     546             :                 {
     547          11 :                     return CheckStackEmpty();
     548             :                 }
     549         203 :                 return true;
     550             :             }
     551             :         }
     552     1679360 :         else if (eCurState == STRING)
     553             :         {
     554       98364 :             bool bEOS = false;
     555             : 
     556       98364 :             if (m_osToken.empty() && !m_bInStringEscape && !m_bInUnicode)
     557             :             {
     558             :                 // Optimization to avoid using temporary buffer
     559             :                 auto nPos =
     560       97491 :                     std::string_view(pStr, nLength).find_first_of("\"\\");
     561       97491 :                 if (nPos != std::string::npos && pStr[nPos] == '"')
     562             :                 {
     563       97122 :                     if (nPos > m_nMaxStringSize)
     564             :                     {
     565           2 :                         return EmitException("Too many characters in number");
     566             :                     }
     567      194237 :                     if (!m_aeObjectState.empty() &&
     568       97117 :                         m_aeObjectState.back() == IN_KEY)
     569             :                     {
     570       69899 :                         StartObjectMember(std::string_view(pStr, nPos));
     571             :                     }
     572             :                     else
     573             :                     {
     574       27221 :                         String(std::string_view(pStr, nPos));
     575             :                     }
     576       97120 :                     m_aState.pop_back();
     577       97120 :                     pStr += nPos + 1;
     578       97120 :                     nLength -= nPos + 1;
     579       97120 :                     SkipSpace(pStr, nLength);
     580       97120 :                     if (nLength != 0)
     581       97116 :                         continue;
     582           4 :                     bEOS = true;
     583             :                 }
     584             :             }
     585             : 
     586        9581 :             while (nLength)
     587             :             {
     588        8562 :                 if (m_osToken.size() == m_nMaxStringSize)
     589             :                 {
     590           0 :                     return EmitException("Too many characters in number");
     591             :                 }
     592             : 
     593        8562 :                 char ch = *pStr;
     594        8562 :                 if (m_bInUnicode)
     595             :                 {
     596         143 :                     if (m_osUnicodeHex.size() == 8)
     597             :                     {
     598           4 :                         DecodeUnicode();
     599             :                     }
     600         139 :                     else if (m_osUnicodeHex.size() == 4)
     601             :                     {
     602             :                         /* Start of next surrogate pair ? */
     603          23 :                         if (m_nLastChar == '\\')
     604             :                         {
     605           7 :                             if (ch == 'u')
     606             :                             {
     607           5 :                                 AdvanceChar(pStr, nLength);
     608           5 :                                 continue;
     609             :                             }
     610             :                             else
     611             :                             {
     612             :                                 /* will be replacement character */
     613           2 :                                 DecodeUnicode();
     614           2 :                                 m_bInStringEscape = true;
     615             :                             }
     616             :                         }
     617          16 :                         else if (m_nLastChar == 'u')
     618             :                         {
     619           5 :                             if (IsHexDigit(ch))
     620             :                             {
     621           4 :                                 m_osUnicodeHex += ch;
     622             :                             }
     623             :                             else
     624             :                             {
     625             :                                 char szMessage[64];
     626           1 :                                 snprintf(szMessage, sizeof(szMessage),
     627             :                                          "Illegal character in unicode "
     628             :                                          "sequence (\\%c)",
     629             :                                          ch);
     630           1 :                                 return EmitException(szMessage);
     631             :                             }
     632           4 :                             AdvanceChar(pStr, nLength);
     633           4 :                             continue;
     634             :                         }
     635          11 :                         else if (ch == '\\')
     636             :                         {
     637           7 :                             AdvanceChar(pStr, nLength);
     638           7 :                             continue;
     639             :                         }
     640             :                         else
     641             :                         {
     642             :                             /* will be replacement character */
     643           4 :                             DecodeUnicode();
     644             :                         }
     645             :                     }
     646             :                     else
     647             :                     {
     648         116 :                         if (IsHexDigit(ch))
     649             :                         {
     650         115 :                             m_osUnicodeHex += ch;
     651         140 :                             if (m_osUnicodeHex.size() == 4 &&
     652          25 :                                 !IsHighSurrogate(getUCSChar(m_osUnicodeHex)))
     653             :                             {
     654          14 :                                 DecodeUnicode();
     655             :                             }
     656             :                         }
     657             :                         else
     658             :                         {
     659             :                             char szMessage[64];
     660           1 :                             snprintf(szMessage, sizeof(szMessage),
     661             :                                      "Illegal character in unicode "
     662             :                                      "sequence (\\%c)",
     663             :                                      ch);
     664           1 :                             return EmitException(szMessage);
     665             :                         }
     666         115 :                         AdvanceChar(pStr, nLength);
     667         115 :                         continue;
     668             :                     }
     669             :                 }
     670             : 
     671        8429 :                 if (m_bInStringEscape)
     672             :                 {
     673         460 :                     if (ch == '"' || ch == '\\' || ch == '/')
     674         419 :                         m_osToken += ch;
     675          41 :                     else if (ch == 'b')
     676           2 :                         m_osToken += '\b';
     677          39 :                     else if (ch == 'f')
     678           2 :                         m_osToken += '\f';
     679          37 :                     else if (ch == 'n')
     680           2 :                         m_osToken += '\n';
     681          35 :                     else if (ch == 'r')
     682           2 :                         m_osToken += '\r';
     683          33 :                     else if (ch == 't')
     684           4 :                         m_osToken += '\t';
     685          29 :                     else if (ch == 'u')
     686             :                     {
     687          28 :                         m_bInUnicode = true;
     688             :                     }
     689             :                     else
     690             :                     {
     691             :                         char szMessage[32];
     692           1 :                         snprintf(szMessage, sizeof(szMessage),
     693             :                                  "Illegal escape sequence (\\%c)", ch);
     694           1 :                         return EmitException(szMessage);
     695             :                     }
     696         459 :                     m_bInStringEscape = false;
     697         459 :                     AdvanceChar(pStr, nLength);
     698         459 :                     continue;
     699             :                 }
     700        7969 :                 else if (ch == '\\')
     701             :                 {
     702         459 :                     m_bInStringEscape = true;
     703         459 :                     AdvanceChar(pStr, nLength);
     704         459 :                     continue;
     705             :                 }
     706        7510 :                 else if (ch == '"')
     707             :                 {
     708         224 :                     bEOS = true;
     709         224 :                     AdvanceChar(pStr, nLength);
     710         224 :                     SkipSpace(pStr, nLength);
     711             : 
     712         437 :                     if (!m_aeObjectState.empty() &&
     713         213 :                         m_aeObjectState.back() == IN_KEY)
     714             :                     {
     715         113 :                         StartObjectMember(m_osToken);
     716             :                     }
     717             :                     else
     718             :                     {
     719         111 :                         String(m_osToken);
     720             :                     }
     721         224 :                     m_osToken.clear();
     722         224 :                     m_aState.pop_back();
     723             : 
     724         224 :                     break;
     725             :                 }
     726             : 
     727        7286 :                 m_osToken += ch;
     728        7286 :                 AdvanceChar(pStr, nLength);
     729             :             }
     730             : 
     731        1243 :             if (nLength == 0)
     732             :             {
     733        1159 :                 if (bFinished)
     734             :                 {
     735          20 :                     if (!bEOS)
     736             :                     {
     737           5 :                         return EmitException("Unterminated string");
     738             :                     }
     739          15 :                     return CheckStackEmpty();
     740             :                 }
     741        1139 :                 return true;
     742             :             }
     743             :         }
     744     1581000 :         else if (eCurState == ARRAY)
     745             :         {
     746     1299950 :             SkipSpace(pStr, nLength);
     747     1299950 :             if (nLength == 0)
     748             :             {
     749         180 :                 if (bFinished)
     750             :                 {
     751          23 :                     return EmitException("Unterminated array");
     752             :                 }
     753         157 :                 return true;
     754             :             }
     755             : 
     756     1299770 :             char ch = *pStr;
     757     1299770 :             if (ch == ',')
     758             :             {
     759      423290 :                 if (m_abArrayState.back() != ArrayState::AFTER_VALUE)
     760             :                 {
     761           3 :                     return EmitUnexpectedChar(ch, "','");
     762             :                 }
     763      423287 :                 m_abArrayState.back() = ArrayState::AFTER_COMMA;
     764      423287 :                 AdvanceChar(pStr, nLength);
     765             :             }
     766      876482 :             else if (ch == ']')
     767             :             {
     768      226601 :                 if (m_abArrayState.back() == ArrayState::AFTER_COMMA)
     769             :                 {
     770           3 :                     return EmitException("Missing value");
     771             :                 }
     772             : 
     773      226598 :                 EndArray();
     774      226598 :                 AdvanceChar(pStr, nLength);
     775      226598 :                 m_abArrayState.pop_back();
     776      226598 :                 m_aState.pop_back();
     777             :             }
     778      649881 :             else if (IsValidNewToken(ch))
     779             :             {
     780      649880 :                 if (m_abArrayState.back() == ArrayState::AFTER_VALUE)
     781             :                 {
     782           1 :                     return EmitException(
     783           1 :                         "Unexpected state: ',' or ']' expected");
     784             :                 }
     785      649879 :                 m_abArrayState.back() = ArrayState::AFTER_VALUE;
     786             : 
     787      649879 :                 StartArrayMember();
     788      649879 :                 if (!StartNewToken(pStr, nLength))
     789             :                 {
     790           0 :                     return false;
     791             :                 }
     792             :             }
     793             :             else
     794             :             {
     795           1 :                 return EmitUnexpectedChar(ch);
     796             :             }
     797             :         }
     798      281047 :         else if (eCurState == OBJECT)
     799             :         {
     800      279126 :             SkipSpace(pStr, nLength);
     801      279126 :             if (nLength == 0)
     802             :             {
     803         469 :                 if (bFinished)
     804             :                 {
     805           4 :                     return EmitException("Unterminated object");
     806             :                 }
     807         465 :                 return true;
     808             :             }
     809             : 
     810      278657 :             char ch = *pStr;
     811      278657 :             if (ch == ',')
     812             :             {
     813       46928 :                 if (m_aeObjectState.back() != IN_VALUE)
     814             :                 {
     815           2 :                     return EmitUnexpectedChar(ch, "','");
     816             :                 }
     817             : 
     818       46926 :                 m_aeObjectState.back() = WAITING_KEY;
     819       46926 :                 AdvanceChar(pStr, nLength);
     820             :             }
     821      231729 :             else if (ch == ':')
     822             :             {
     823       70003 :                 if (m_aeObjectState.back() != IN_KEY)
     824             :                 {
     825           1 :                     return EmitUnexpectedChar(ch, "':'");
     826             :                 }
     827       70002 :                 m_aeObjectState.back() = KEY_FINISHED;
     828       70002 :                 AdvanceChar(pStr, nLength);
     829             :             }
     830      161726 :             else if (ch == '}')
     831             :             {
     832       42453 :                 if (m_aeObjectState.back() == WAITING_KEY ||
     833       20744 :                     m_aeObjectState.back() == IN_VALUE)
     834             :                 {
     835             :                     // nothing
     836             :                 }
     837             :                 else
     838             :                 {
     839           1 :                     return EmitException("Missing value");
     840             :                 }
     841             : 
     842       21708 :                 EndObject();
     843       21708 :                 AdvanceChar(pStr, nLength);
     844       21708 :                 m_aeObjectState.pop_back();
     845       21708 :                 m_aState.pop_back();
     846             :             }
     847      140017 :             else if (IsValidNewToken(ch))
     848             :             {
     849      140015 :                 if (m_aeObjectState.back() == WAITING_KEY)
     850             :                 {
     851       70013 :                     if (ch != '"')
     852             :                     {
     853           1 :                         return EmitUnexpectedChar(ch, "'\"'");
     854             :                     }
     855       70012 :                     m_aeObjectState.back() = IN_KEY;
     856             :                 }
     857       70002 :                 else if (m_aeObjectState.back() == KEY_FINISHED)
     858             :                 {
     859       70001 :                     m_aeObjectState.back() = IN_VALUE;
     860             :                 }
     861             :                 else
     862             :                 {
     863           1 :                     return EmitException("Unexpected state");
     864             :                 }
     865      140013 :                 if (!StartNewToken(pStr, nLength))
     866             :                 {
     867           0 :                     return false;
     868             :                 }
     869             :             }
     870             :             else
     871             :             {
     872           2 :                 return EmitUnexpectedChar(ch);
     873             :             }
     874             :         }
     875             :         else /* if( eCurState == STATE_TRUE || eCurState == STATE_FALSE ||
     876             :                     eCurState == STATE_NULL ) */
     877             :         {
     878        9074 :             while (nLength)
     879             :             {
     880        8954 :                 char ch = *pStr;
     881        8959 :                 if (eCurState == STATE_NULL && (ch == 'a' || ch == 'A') &&
     882           5 :                     m_osToken.size() == 1)
     883             :                 {
     884           5 :                     m_aState.back() = NUMBER;
     885           5 :                     break;
     886             :                 }
     887        8949 :                 if (isalpha(static_cast<unsigned char>(ch)))
     888             :                 {
     889        7159 :                     m_osToken += ch;
     890        8772 :                     if (eCurState == STATE_TRUE &&
     891         807 :                         (m_osToken.size() > strlen("true") ||
     892         806 :                          memcmp(m_osToken.c_str(), "true", m_osToken.size()) !=
     893             :                              0))
     894             :                     {
     895           2 :                         return EmitUnexpectedChar(*pStr);
     896             :                     }
     897        7266 :                     else if (eCurState == STATE_FALSE &&
     898          55 :                              (m_osToken.size() > strlen("false") ||
     899          54 :                               memcmp(m_osToken.c_str(), "false",
     900             :                                      m_osToken.size()) != 0))
     901             :                     {
     902           2 :                         return EmitUnexpectedChar(*pStr);
     903             :                     }
     904       19748 :                     else if (eCurState == STATE_NULL &&
     905        6297 :                              (m_osToken.size() > strlen("null") ||
     906        6296 :                               memcmp(m_osToken.c_str(), "null",
     907             :                                      m_osToken.size()) != 0))
     908             :                     {
     909           2 :                         return EmitUnexpectedChar(*pStr);
     910             :                     }
     911             :                 }
     912        1790 :                 else if (isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
     913          55 :                          ch == '}' || ch == ']')
     914             :                 {
     915        1789 :                     SkipSpace(pStr, nLength);
     916        1789 :                     break;
     917             :                 }
     918             :                 else
     919             :                 {
     920           1 :                     return EmitUnexpectedChar(ch);
     921             :                 }
     922        7153 :                 AdvanceChar(pStr, nLength);
     923             :             }
     924        1914 :             if (m_aState.back() == NUMBER)
     925             :             {
     926           5 :                 continue;
     927             :             }
     928        1909 :             if (nLength == 0)
     929             :             {
     930         139 :                 if (bFinished)
     931             :                 {
     932           9 :                     if (!CheckAndEmitTrueFalseOrNull(0))
     933           3 :                         return false;
     934           6 :                     return CheckStackEmpty();
     935             :                 }
     936         130 :                 return true;
     937             :             }
     938             : 
     939        1770 :             if (!CheckAndEmitTrueFalseOrNull(*pStr))
     940           0 :                 return false;
     941             :         }
     942     2123920 :     }
     943             : }
     944             : 
     945             : /************************************************************************/
     946             : /*                       GetSerializedString()                          */
     947             : /************************************************************************/
     948             : 
     949       11180 : std::string CPLJSonStreamingParser::GetSerializedString(std::string_view s)
     950             : {
     951       11180 :     std::string osStr("\"");
     952       87598 :     for (char ch : s)
     953             :     {
     954       76418 :         if (ch == '\b')
     955           2 :             osStr += "\\b";
     956       76416 :         else if (ch == '\f')
     957           2 :             osStr += "\\f";
     958       76414 :         else if (ch == '\n')
     959           2 :             osStr += "\\n";
     960       76412 :         else if (ch == '\r')
     961           2 :             osStr += "\\r";
     962       76410 :         else if (ch == '\t')
     963           4 :             osStr += "\\t";
     964       76406 :         else if (ch == '"')
     965           2 :             osStr += "\\\"";
     966       76404 :         else if (ch == '\\')
     967           2 :             osStr += "\\\\";
     968       76402 :         else if (static_cast<unsigned char>(ch) < ' ')
     969           4 :             osStr += CPLSPrintf("\\u%04X", ch);
     970             :         else
     971       76398 :             osStr += ch;
     972             :     }
     973       11180 :     osStr += "\"";
     974       11180 :     return osStr;
     975             : }
     976             : 
     977             : /*! @endcond */

Generated by: LCOV version 1.14