LCOV - code coverage report
Current view: top level - port - cpl_json_streaming_parser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 415 423 98.1 %
Date: 2024-11-21 22:18:42 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        2780 : CPLJSonStreamingParser::CPLJSonStreamingParser()
      31             : {
      32        2780 :     m_aState.push_back(INIT);
      33        2780 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                      ~CPLJSonStreamingParser()                       */
      37             : /************************************************************************/
      38             : 
      39        2780 : CPLJSonStreamingParser::~CPLJSonStreamingParser()
      40             : {
      41        2780 : }
      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           1 : void CPLJSonStreamingParser::SetMaxStringSize(size_t nVal)
      57             : {
      58           1 :     m_nMaxStringSize = nVal;
      59           1 : }
      60             : 
      61             : /************************************************************************/
      62             : /*                                Reset()                               */
      63             : /************************************************************************/
      64             : 
      65          18 : void CPLJSonStreamingParser::Reset()
      66             : {
      67          18 :     m_bExceptionOccurred = false;
      68          18 :     m_bElementFound = false;
      69          18 :     m_nLastChar = 0;
      70          18 :     m_nLineCounter = 1;
      71          18 :     m_nCharCounter = 1;
      72          18 :     m_aState.clear();
      73          18 :     m_aState.push_back(INIT);
      74          18 :     m_osToken.clear();
      75          18 :     m_abArrayState.clear();
      76          18 :     m_aeObjectState.clear();
      77          18 :     m_bInStringEscape = false;
      78          18 :     m_bInUnicode = false;
      79          18 :     m_osUnicodeHex.clear();
      80          18 : }
      81             : 
      82             : /************************************************************************/
      83             : /*                              AdvanceChar()                           */
      84             : /************************************************************************/
      85             : 
      86    11176500 : void CPLJSonStreamingParser::AdvanceChar(const char *&pStr, size_t &nLength)
      87             : {
      88    11176500 :     if (*pStr == 13 && m_nLastChar != 10)
      89             :     {
      90          27 :         m_nLineCounter++;
      91          27 :         m_nCharCounter = 0;
      92             :     }
      93    11176500 :     else if (*pStr == 10 && m_nLastChar != 13)
      94             :     {
      95      198532 :         m_nLineCounter++;
      96      198532 :         m_nCharCounter = 0;
      97             :     }
      98    11176500 :     m_nLastChar = *pStr;
      99             : 
     100    11176500 :     pStr++;
     101    11176500 :     nLength--;
     102    11176500 :     m_nCharCounter++;
     103    11176500 : }
     104             : 
     105             : /************************************************************************/
     106             : /*                               SkipSpace()                            */
     107             : /************************************************************************/
     108             : 
     109     5179200 : void CPLJSonStreamingParser::SkipSpace(const char *&pStr, size_t &nLength)
     110             : {
     111     5179200 :     while (nLength > 0 && isspace(static_cast<unsigned char>(*pStr)))
     112             :     {
     113     3118830 :         AdvanceChar(pStr, nLength);
     114             :     }
     115     2060370 : }
     116             : 
     117             : /************************************************************************/
     118             : /*                             EmitException()                          */
     119             : /************************************************************************/
     120             : 
     121          60 : bool CPLJSonStreamingParser::EmitException(const char *pszMessage)
     122             : {
     123          60 :     m_bExceptionOccurred = true;
     124          60 :     CPLString osMsg;
     125             :     osMsg.Printf("At line %d, character %d: %s", m_nLineCounter, m_nCharCounter,
     126          60 :                  pszMessage);
     127          60 :     Exception(osMsg.c_str());
     128         120 :     return false;
     129             : }
     130             : 
     131             : /************************************************************************/
     132             : /*                             StopParsing()                            */
     133             : /************************************************************************/
     134             : 
     135        1831 : void CPLJSonStreamingParser::StopParsing()
     136             : {
     137        1831 :     m_bStopParsing = true;
     138        1831 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                          EmitUnexpectedChar()                        */
     142             : /************************************************************************/
     143             : 
     144          33 : bool CPLJSonStreamingParser::EmitUnexpectedChar(char ch,
     145             :                                                 const char *pszExpecting)
     146             : {
     147             :     char szMessage[64];
     148          33 :     if (pszExpecting)
     149             :     {
     150           7 :         snprintf(szMessage, sizeof(szMessage),
     151             :                  "Unexpected character (%c). Expecting %s", ch, pszExpecting);
     152             :     }
     153             :     else
     154             :     {
     155          26 :         snprintf(szMessage, sizeof(szMessage), "Unexpected character (%c)", ch);
     156             :     }
     157          66 :     return EmitException(szMessage);
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                            IsValidNewToken()                         */
     162             : /************************************************************************/
     163             : 
     164      768751 : static bool IsValidNewToken(char ch)
     165             : {
     166      547408 :     return ch == '[' || ch == '{' || ch == '"' || ch == '-' || ch == '.' ||
     167      360193 :            isdigit(static_cast<unsigned char>(ch)) || ch == 't' || ch == 'f' ||
     168     1316160 :            ch == 'n' || ch == 'i' || ch == 'I' || ch == 'N';
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                             StartNewToken()                          */
     173             : /************************************************************************/
     174             : 
     175      768737 : bool CPLJSonStreamingParser::StartNewToken(const char *&pStr, size_t &nLength)
     176             : {
     177      768737 :     char ch = *pStr;
     178      768737 :     if (ch == '{')
     179             :     {
     180       22520 :         if (m_aState.size() == m_nMaxDepth)
     181             :         {
     182           1 :             return EmitException("Too many nested objects and/or arrays");
     183             :         }
     184       22519 :         StartObject();
     185       22519 :         m_aeObjectState.push_back(WAITING_KEY);
     186       22519 :         m_aState.push_back(OBJECT);
     187       22519 :         AdvanceChar(pStr, nLength);
     188             :     }
     189      746217 :     else if (ch == '"')
     190             :     {
     191       91316 :         m_aState.push_back(STRING);
     192       91316 :         AdvanceChar(pStr, nLength);
     193             :     }
     194      654901 :     else if (ch == '[')
     195             :     {
     196      221343 :         if (m_aState.size() == m_nMaxDepth)
     197             :         {
     198           1 :             return EmitException("Too many nested objects and/or arrays");
     199             :         }
     200      221342 :         StartArray();
     201      221342 :         m_abArrayState.push_back(ArrayState::INIT);
     202      221342 :         m_aState.push_back(ARRAY);
     203      221342 :         AdvanceChar(pStr, nLength);
     204             :     }
     205      433558 :     else if (ch == '-' || ch == '.' ||
     206      360179 :              isdigit(static_cast<unsigned char>(ch)) || ch == 'i' ||
     207        1541 :              ch == 'I' || ch == 'N')
     208             :     {
     209      432019 :         m_aState.push_back(NUMBER);
     210             :     }
     211        1539 :     else if (ch == 't')
     212             :     {
     213         162 :         m_aState.push_back(STATE_TRUE);
     214             :     }
     215        1377 :     else if (ch == 'f')
     216             :     {
     217          11 :         m_aState.push_back(STATE_FALSE);
     218             :     }
     219        1366 :     else if (ch == 'n')
     220             :     {
     221        1366 :         m_aState.push_back(STATE_NULL); /* might be nan */
     222             :     }
     223             :     else
     224             :     {
     225           0 :         assert(false);
     226             :     }
     227      768735 :     return true;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                       CheckAndEmitTrueFalseOrNull()                  */
     232             : /************************************************************************/
     233             : 
     234        1528 : bool CPLJSonStreamingParser::CheckAndEmitTrueFalseOrNull(char ch)
     235             : {
     236        1528 :     State eCurState = currentState();
     237             : 
     238        1528 :     if (eCurState == STATE_TRUE)
     239             :     {
     240         159 :         if (m_osToken == "true")
     241             :         {
     242         158 :             Boolean(true);
     243             :         }
     244             :         else
     245             :         {
     246           1 :             return EmitUnexpectedChar(ch);
     247             :         }
     248             :     }
     249        1369 :     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        1360 :         if (m_osToken == "null")
     263             :         {
     264        1359 :             Null();
     265             :         }
     266             :         else
     267             :         {
     268           1 :             return EmitUnexpectedChar(ch);
     269             :         }
     270             :     }
     271        1525 :     m_aState.pop_back();
     272        1525 :     m_osToken.clear();
     273        1525 :     return true;
     274             : }
     275             : 
     276             : /************************************************************************/
     277             : /*                           CheckStackEmpty()                          */
     278             : /************************************************************************/
     279             : 
     280          26 : bool CPLJSonStreamingParser::CheckStackEmpty()
     281             : {
     282          26 :     if (!m_aeObjectState.empty())
     283             :     {
     284           1 :         return EmitException("Unterminated object");
     285             :     }
     286          25 :     else if (!m_abArrayState.empty())
     287             :     {
     288           1 :         return EmitException("Unterminated array");
     289             :     }
     290          24 :     return true;
     291             : }
     292             : 
     293             : /************************************************************************/
     294             : /*                           IsHighSurrogate()                          */
     295             : /************************************************************************/
     296             : 
     297          21 : static bool IsHighSurrogate(unsigned uc)
     298             : {
     299          21 :     return (uc & 0xFC00) == 0xD800;
     300             : }
     301             : 
     302             : /************************************************************************/
     303             : /*                           IsLowSurrogate()                           */
     304             : /************************************************************************/
     305             : 
     306           8 : static bool IsLowSurrogate(unsigned uc)
     307             : {
     308           8 :     return (uc & 0xFC00) == 0xDC00;
     309             : }
     310             : 
     311             : /************************************************************************/
     312             : /*                         GetSurrogatePair()                           */
     313             : /************************************************************************/
     314             : 
     315           1 : static unsigned GetSurrogatePair(unsigned hi, unsigned lo)
     316             : {
     317           1 :     return ((hi & 0x3FF) << 10) + (lo & 0x3FF) + 0x10000;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                            IsHexDigit()                              */
     322             : /************************************************************************/
     323             : 
     324          69 : static bool IsHexDigit(char ch)
     325             : {
     326          81 :     return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     327          81 :            (ch >= 'A' && ch <= 'F');
     328             : }
     329             : 
     330             : /************************************************************************/
     331             : /*                           HexToDecimal()                             */
     332             : /************************************************************************/
     333             : 
     334         116 : static unsigned HexToDecimal(char ch)
     335             : {
     336         116 :     if (ch >= '0' && ch <= '9')
     337          93 :         return ch - '0';
     338          23 :     if (ch >= 'a' && ch <= 'f')
     339           8 :         return 10 + ch - 'a';
     340             :     // if (ch >= 'A' && ch <= 'F' )
     341          15 :     return 10 + ch - 'A';
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                            getUCSChar()                              */
     346             : /************************************************************************/
     347             : 
     348          29 : static unsigned getUCSChar(const std::string &unicode4HexChar)
     349             : {
     350          29 :     return (HexToDecimal(unicode4HexChar[0]) << 12) |
     351          29 :            (HexToDecimal(unicode4HexChar[1]) << 8) |
     352          29 :            (HexToDecimal(unicode4HexChar[2]) << 4) |
     353          29 :            (HexToDecimal(unicode4HexChar[3]));
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                           DecodeUnicode()                            */
     358             : /************************************************************************/
     359             : 
     360          13 : void CPLJSonStreamingParser::DecodeUnicode()
     361             : {
     362          13 :     constexpr char szReplacementUTF8[] = "\xEF\xBF\xBD";
     363             :     unsigned nUCSChar;
     364          13 :     if (m_osUnicodeHex.size() == 8)
     365             :     {
     366           2 :         unsigned nUCSHigh = getUCSChar(m_osUnicodeHex);
     367           2 :         assert(IsHighSurrogate(nUCSHigh));
     368           2 :         unsigned nUCSLow = getUCSChar(m_osUnicodeHex.substr(4));
     369           2 :         if (IsLowSurrogate(nUCSLow))
     370             :         {
     371           1 :             nUCSChar = GetSurrogatePair(nUCSHigh, nUCSLow);
     372             :         }
     373             :         else
     374             :         {
     375             :             /* Invalid code point. Insert the replacement char */
     376           1 :             nUCSChar = 0xFFFFFFFFU;
     377             :         }
     378             :     }
     379             :     else
     380             :     {
     381          11 :         assert(m_osUnicodeHex.size() == 4);
     382          11 :         nUCSChar = getUCSChar(m_osUnicodeHex);
     383             :     }
     384             : 
     385          13 :     if (nUCSChar < 0x80)
     386             :     {
     387           6 :         m_osToken += static_cast<char>(nUCSChar);
     388             :     }
     389           7 :     else if (nUCSChar < 0x800)
     390             :     {
     391           1 :         m_osToken += static_cast<char>(0xC0 | (nUCSChar >> 6));
     392           1 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     393             :     }
     394           6 :     else if (IsLowSurrogate(nUCSChar) || IsHighSurrogate(nUCSChar))
     395             :     {
     396             :         /* Invalid code point. Insert the replacement char */
     397           4 :         m_osToken += szReplacementUTF8;
     398             :     }
     399           2 :     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           2 :     else if (nUCSChar < 0x110000)
     406             :     {
     407           1 :         m_osToken += static_cast<char>(0xF0 | ((nUCSChar >> 18) & 0x07));
     408           1 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 12) & 0x3F));
     409           1 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 6) & 0x3F));
     410           1 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     411             :     }
     412             :     else
     413             :     {
     414             :         /* Invalid code point. Insert the replacement char */
     415           1 :         m_osToken += szReplacementUTF8;
     416             :     }
     417             : 
     418          13 :     m_bInUnicode = false;
     419          13 :     m_osUnicodeHex.clear();
     420          13 : }
     421             : 
     422             : /************************************************************************/
     423             : /*                              Parse()                                 */
     424             : /************************************************************************/
     425             : 
     426     2063470 : bool CPLJSonStreamingParser::Parse(const char *pStr, size_t nLength,
     427             :                                    bool bFinished)
     428             : {
     429             :     while (true)
     430             :     {
     431     2063470 :         if (m_bExceptionOccurred || m_bStopParsing)
     432        1832 :             return false;
     433     2061630 :         State eCurState = currentState();
     434     2061630 :         if (eCurState == INIT)
     435             :         {
     436        3686 :             SkipSpace(pStr, nLength);
     437        3686 :             if (nLength == 0)
     438         993 :                 return true;
     439        2693 :             if (m_bElementFound || !IsValidNewToken(*pStr))
     440             :             {
     441           9 :                 return EmitUnexpectedChar(*pStr);
     442             :             }
     443        2684 :             if (!StartNewToken(pStr, nLength))
     444             :             {
     445           2 :                 return false;
     446             :             }
     447        2682 :             m_bElementFound = true;
     448             :         }
     449     2057950 :         else if (eCurState == NUMBER)
     450             :         {
     451     6538420 :             while (nLength)
     452             :             {
     453     6538220 :                 char ch = *pStr;
     454     6538220 :                 if (ch == '+' || ch == '-' ||
     455     6464830 :                     isdigit(static_cast<unsigned char>(ch)) || ch == '.' ||
     456      432140 :                     ch == 'e' || ch == 'E')
     457             :                 {
     458     6106090 :                     if (m_osToken.size() == 1024)
     459             :                     {
     460           0 :                         return EmitException("Too many characters in number");
     461             :                     }
     462     6106090 :                     m_osToken += ch;
     463             :                 }
     464      432138 :                 else if (isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
     465       22104 :                          ch == '}' || ch == ']')
     466             :                 {
     467      432027 :                     SkipSpace(pStr, nLength);
     468      432027 :                     break;
     469             :                 }
     470             :                 else
     471             :                 {
     472         111 :                     CPLString extendedToken(m_osToken + ch);
     473         111 :                     if ((STARTS_WITH_CI("Infinity", extendedToken) &&
     474          47 :                          m_osToken.size() + 1 <= strlen("Infinity")) ||
     475          64 :                         (STARTS_WITH_CI("-Infinity", extendedToken) &&
     476         222 :                          m_osToken.size() + 1 <= strlen("-Infinity")) ||
     477          17 :                         (STARTS_WITH_CI("NaN", extendedToken) &&
     478          13 :                          m_osToken.size() + 1 <= strlen("NaN")))
     479             :                     {
     480         107 :                         m_osToken += ch;
     481             :                     }
     482             :                     else
     483             :                     {
     484           4 :                         return EmitUnexpectedChar(ch);
     485             :                     }
     486             :                 }
     487     6106190 :                 AdvanceChar(pStr, nLength);
     488             :             }
     489             : 
     490      432228 :             if (nLength != 0 || bFinished)
     491             :             {
     492      432017 :                 const char firstCh = m_osToken[0];
     493      432017 :                 if (firstCh == 'i' || firstCh == 'I')
     494             :                 {
     495           5 :                     if (!EQUAL(m_osToken.c_str(), "Infinity"))
     496             :                     {
     497           1 :                         return EmitException("Invalid number");
     498             :                     }
     499             :                 }
     500      432012 :                 else if (firstCh == '-')
     501             :                 {
     502       73378 :                     if (m_osToken[1] == 'i' || m_osToken[1] == 'I')
     503             :                     {
     504           5 :                         if (!EQUAL(m_osToken.c_str(), "-Infinity"))
     505             :                         {
     506           1 :                             return EmitException("Invalid number");
     507             :                         }
     508             :                     }
     509             :                 }
     510      358634 :                 else if (firstCh == 'n' || firstCh == 'N')
     511             :                 {
     512           5 :                     if (m_osToken[1] == 'a' || m_osToken[1] == 'A')
     513             :                     {
     514           5 :                         if (!EQUAL(m_osToken.c_str(), "NaN"))
     515             :                         {
     516           1 :                             return EmitException("Invalid number");
     517             :                         }
     518             :                     }
     519             :                 }
     520             : 
     521      432014 :                 Number(m_osToken.c_str(), m_osToken.size());
     522      432014 :                 m_osToken.clear();
     523      432014 :                 m_aState.pop_back();
     524             :             }
     525             : 
     526      432225 :             if (nLength == 0)
     527             :             {
     528         222 :                 if (bFinished)
     529             :                 {
     530          11 :                     return CheckStackEmpty();
     531             :                 }
     532         211 :                 return true;
     533             :             }
     534             :         }
     535     1625720 :         else if (eCurState == STRING)
     536             :         {
     537       92235 :             bool bEOS = false;
     538      845970 :             while (nLength)
     539             :             {
     540      845046 :                 if (m_osToken.size() == m_nMaxStringSize)
     541             :                 {
     542           1 :                     return EmitException("Too many characters in number");
     543             :                 }
     544             : 
     545      845045 :                 char ch = *pStr;
     546      845045 :                 if (m_bInUnicode)
     547             :                 {
     548          81 :                     if (m_osUnicodeHex.size() == 8)
     549             :                     {
     550           2 :                         DecodeUnicode();
     551             :                     }
     552          79 :                     else if (m_osUnicodeHex.size() == 4)
     553             :                     {
     554             :                         /* Start of next surrogate pair ? */
     555          13 :                         if (m_nLastChar == '\\')
     556             :                         {
     557           4 :                             if (ch == 'u')
     558             :                             {
     559           3 :                                 AdvanceChar(pStr, nLength);
     560           3 :                                 continue;
     561             :                             }
     562             :                             else
     563             :                             {
     564             :                                 /* will be replacement character */
     565           1 :                                 DecodeUnicode();
     566           1 :                                 m_bInStringEscape = true;
     567             :                             }
     568             :                         }
     569           9 :                         else if (m_nLastChar == 'u')
     570             :                         {
     571           3 :                             if (IsHexDigit(ch))
     572             :                             {
     573           2 :                                 m_osUnicodeHex += ch;
     574             :                             }
     575             :                             else
     576             :                             {
     577             :                                 char szMessage[64];
     578           1 :                                 snprintf(szMessage, sizeof(szMessage),
     579             :                                          "Illegal character in unicode "
     580             :                                          "sequence (\\%c)",
     581             :                                          ch);
     582           1 :                                 return EmitException(szMessage);
     583             :                             }
     584           2 :                             AdvanceChar(pStr, nLength);
     585           2 :                             continue;
     586             :                         }
     587           6 :                         else if (ch == '\\')
     588             :                         {
     589           4 :                             AdvanceChar(pStr, nLength);
     590           4 :                             continue;
     591             :                         }
     592             :                         else
     593             :                         {
     594             :                             /* will be replacement character */
     595           2 :                             DecodeUnicode();
     596             :                         }
     597             :                     }
     598             :                     else
     599             :                     {
     600          66 :                         if (IsHexDigit(ch))
     601             :                         {
     602          65 :                             m_osUnicodeHex += ch;
     603          79 :                             if (m_osUnicodeHex.size() == 4 &&
     604          14 :                                 !IsHighSurrogate(getUCSChar(m_osUnicodeHex)))
     605             :                             {
     606           8 :                                 DecodeUnicode();
     607             :                             }
     608             :                         }
     609             :                         else
     610             :                         {
     611             :                             char szMessage[64];
     612           1 :                             snprintf(szMessage, sizeof(szMessage),
     613             :                                      "Illegal character in unicode "
     614             :                                      "sequence (\\%c)",
     615             :                                      ch);
     616           1 :                             return EmitException(szMessage);
     617             :                         }
     618          65 :                         AdvanceChar(pStr, nLength);
     619          65 :                         continue;
     620             :                     }
     621             :                 }
     622             : 
     623      844969 :                 if (m_bInStringEscape)
     624             :                 {
     625         170 :                     if (ch == '"' || ch == '\\' || ch == '/')
     626         141 :                         m_osToken += ch;
     627          29 :                     else if (ch == 'b')
     628           2 :                         m_osToken += '\b';
     629          27 :                     else if (ch == 'f')
     630           2 :                         m_osToken += '\f';
     631          25 :                     else if (ch == 'n')
     632           2 :                         m_osToken += '\n';
     633          23 :                     else if (ch == 'r')
     634           2 :                         m_osToken += '\r';
     635          21 :                     else if (ch == 't')
     636           3 :                         m_osToken += '\t';
     637          18 :                     else if (ch == 'u')
     638             :                     {
     639          17 :                         m_bInUnicode = true;
     640             :                     }
     641             :                     else
     642             :                     {
     643             :                         char szMessage[32];
     644           1 :                         snprintf(szMessage, sizeof(szMessage),
     645             :                                  "Illegal escape sequence (\\%c)", ch);
     646           1 :                         return EmitException(szMessage);
     647             :                     }
     648         169 :                     m_bInStringEscape = false;
     649         169 :                     AdvanceChar(pStr, nLength);
     650         169 :                     continue;
     651             :                 }
     652      844799 :                 else if (ch == '\\')
     653             :                 {
     654         170 :                     m_bInStringEscape = true;
     655         170 :                     AdvanceChar(pStr, nLength);
     656         170 :                     continue;
     657             :                 }
     658      844629 :                 else if (ch == '"')
     659             :                 {
     660       91307 :                     bEOS = true;
     661       91307 :                     AdvanceChar(pStr, nLength);
     662       91307 :                     SkipSpace(pStr, nLength);
     663             : 
     664      182606 :                     if (!m_aeObjectState.empty() &&
     665       91299 :                         m_aeObjectState.back() == IN_KEY)
     666             :                     {
     667       66297 :                         StartObjectMember(m_osToken.c_str(), m_osToken.size());
     668             :                     }
     669             :                     else
     670             :                     {
     671       25010 :                         String(m_osToken.c_str(), m_osToken.size());
     672             :                     }
     673       91307 :                     m_osToken.clear();
     674       91307 :                     m_aState.pop_back();
     675             : 
     676       91307 :                     break;
     677             :                 }
     678             : 
     679      753322 :                 m_osToken += ch;
     680      753322 :                 AdvanceChar(pStr, nLength);
     681             :             }
     682             : 
     683       92231 :             if (nLength == 0)
     684             :             {
     685        1062 :                 if (bFinished)
     686             :                 {
     687          14 :                     if (!bEOS)
     688             :                     {
     689           5 :                         return EmitException("Unterminated string");
     690             :                     }
     691           9 :                     return CheckStackEmpty();
     692             :                 }
     693        1048 :                 return true;
     694             :             }
     695             :         }
     696     1533480 :         else if (eCurState == ARRAY)
     697             :         {
     698     1267150 :             SkipSpace(pStr, nLength);
     699     1267150 :             if (nLength == 0)
     700             :             {
     701         157 :                 if (bFinished)
     702             :                 {
     703           2 :                     return EmitException("Unterminated array");
     704             :                 }
     705         155 :                 return true;
     706             :             }
     707             : 
     708     1266990 :             char ch = *pStr;
     709     1266990 :             if (ch == ',')
     710             :             {
     711      412203 :                 if (m_abArrayState.back() != ArrayState::AFTER_VALUE)
     712             :                 {
     713           3 :                     return EmitUnexpectedChar(ch, "','");
     714             :                 }
     715      412200 :                 m_abArrayState.back() = ArrayState::AFTER_COMMA;
     716      412200 :                 AdvanceChar(pStr, nLength);
     717             :             }
     718      854789 :             else if (ch == ']')
     719             :             {
     720      221319 :                 if (m_abArrayState.back() == ArrayState::AFTER_COMMA)
     721             :                 {
     722           3 :                     return EmitException("Missing value");
     723             :                 }
     724             : 
     725      221316 :                 EndArray();
     726      221316 :                 AdvanceChar(pStr, nLength);
     727      221316 :                 m_abArrayState.pop_back();
     728      221316 :                 m_aState.pop_back();
     729             :             }
     730      633470 :             else if (IsValidNewToken(ch))
     731             :             {
     732      633469 :                 if (m_abArrayState.back() == ArrayState::AFTER_VALUE)
     733             :                 {
     734           1 :                     return EmitException(
     735           1 :                         "Unexpected state: ',' or ']' expected");
     736             :                 }
     737      633468 :                 m_abArrayState.back() = ArrayState::AFTER_VALUE;
     738             : 
     739      633468 :                 StartArrayMember();
     740      633468 :                 if (!StartNewToken(pStr, nLength))
     741             :                 {
     742           0 :                     return false;
     743             :                 }
     744             :             }
     745             :             else
     746             :             {
     747           1 :                 return EmitUnexpectedChar(ch);
     748             :             }
     749             :         }
     750      266332 :         else if (eCurState == OBJECT)
     751             :         {
     752      264663 :             SkipSpace(pStr, nLength);
     753      264663 :             if (nLength == 0)
     754             :             {
     755         464 :                 if (bFinished)
     756             :                 {
     757           2 :                     return EmitException("Unterminated object");
     758             :                 }
     759         462 :                 return true;
     760             :             }
     761             : 
     762      264199 :             char ch = *pStr;
     763      264199 :             if (ch == ',')
     764             :             {
     765       44658 :                 if (m_aeObjectState.back() != IN_VALUE)
     766             :                 {
     767           2 :                     return EmitUnexpectedChar(ch, "','");
     768             :                 }
     769             : 
     770       44656 :                 m_aeObjectState.back() = WAITING_KEY;
     771       44656 :                 AdvanceChar(pStr, nLength);
     772             :             }
     773      219541 :             else if (ch == ':')
     774             :             {
     775       66290 :                 if (m_aeObjectState.back() != IN_KEY)
     776             :                 {
     777           1 :                     return EmitUnexpectedChar(ch, "':'");
     778             :                 }
     779       66289 :                 m_aeObjectState.back() = KEY_FINISHED;
     780       66289 :                 AdvanceChar(pStr, nLength);
     781             :             }
     782      153251 :             else if (ch == '}')
     783             :             {
     784       40451 :                 if (m_aeObjectState.back() == WAITING_KEY ||
     785       19789 :                     m_aeObjectState.back() == IN_VALUE)
     786             :                 {
     787             :                     // nothing
     788             :                 }
     789             :                 else
     790             :                 {
     791           1 :                     return EmitException("Missing value");
     792             :                 }
     793             : 
     794       20661 :                 EndObject();
     795       20661 :                 AdvanceChar(pStr, nLength);
     796       20661 :                 m_aeObjectState.pop_back();
     797       20661 :                 m_aState.pop_back();
     798             :             }
     799      132589 :             else if (IsValidNewToken(ch))
     800             :             {
     801      132587 :                 if (m_aeObjectState.back() == WAITING_KEY)
     802             :                 {
     803       66298 :                     if (ch != '"')
     804             :                     {
     805           1 :                         return EmitUnexpectedChar(ch, "'\"'");
     806             :                     }
     807       66297 :                     m_aeObjectState.back() = IN_KEY;
     808             :                 }
     809       66289 :                 else if (m_aeObjectState.back() == KEY_FINISHED)
     810             :                 {
     811       66288 :                     m_aeObjectState.back() = IN_VALUE;
     812             :                 }
     813             :                 else
     814             :                 {
     815           1 :                     return EmitException("Unexpected state");
     816             :                 }
     817      132585 :                 if (!StartNewToken(pStr, nLength))
     818             :                 {
     819           0 :                     return false;
     820             :                 }
     821             :             }
     822             :             else
     823             :             {
     824           2 :                 return EmitUnexpectedChar(ch);
     825             :             }
     826             :         }
     827             :         else /* if( eCurState == STATE_TRUE || eCurState == STATE_FALSE ||
     828             :                     eCurState == STATE_NULL ) */
     829             :         {
     830        7817 :             while (nLength)
     831             :             {
     832        7697 :                 char ch = *pStr;
     833        7701 :                 if (eCurState == STATE_NULL && (ch == 'a' || ch == 'A') &&
     834           4 :                     m_osToken.size() == 1)
     835             :                 {
     836           4 :                     m_aState.back() = NUMBER;
     837           4 :                     break;
     838             :                 }
     839        7693 :                 if (isalpha(static_cast<unsigned char>(ch)))
     840             :                 {
     841        6154 :                     m_osToken += ch;
     842        7447 :                     if (eCurState == STATE_TRUE &&
     843         647 :                         (m_osToken.size() > strlen("true") ||
     844         646 :                          memcmp(m_osToken.c_str(), "true", m_osToken.size()) !=
     845             :                              0))
     846             :                     {
     847           2 :                         return EmitUnexpectedChar(*pStr);
     848             :                     }
     849        6261 :                     else if (eCurState == STATE_FALSE &&
     850          55 :                              (m_osToken.size() > strlen("false") ||
     851          54 :                               memcmp(m_osToken.c_str(), "false",
     852             :                                      m_osToken.size()) != 0))
     853             :                     {
     854           2 :                         return EmitUnexpectedChar(*pStr);
     855             :                     }
     856       17053 :                     else if (eCurState == STATE_NULL &&
     857        5452 :                              (m_osToken.size() > strlen("null") ||
     858        5451 :                               memcmp(m_osToken.c_str(), "null",
     859             :                                      m_osToken.size()) != 0))
     860             :                     {
     861           2 :                         return EmitUnexpectedChar(*pStr);
     862             :                     }
     863             :                 }
     864        1539 :                 else if (isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
     865          51 :                          ch == '}' || ch == ']')
     866             :                 {
     867        1538 :                     SkipSpace(pStr, nLength);
     868        1538 :                     break;
     869             :                 }
     870             :                 else
     871             :                 {
     872           1 :                     return EmitUnexpectedChar(ch);
     873             :                 }
     874        6148 :                 AdvanceChar(pStr, nLength);
     875             :             }
     876        1662 :             if (m_aState.back() == NUMBER)
     877             :             {
     878           4 :                 continue;
     879             :             }
     880        1658 :             if (nLength == 0)
     881             :             {
     882         139 :                 if (bFinished)
     883             :                 {
     884           9 :                     if (!CheckAndEmitTrueFalseOrNull(0))
     885           3 :                         return false;
     886           6 :                     return CheckStackEmpty();
     887             :                 }
     888         130 :                 return true;
     889             :             }
     890             : 
     891        1519 :             if (!CheckAndEmitTrueFalseOrNull(*pStr))
     892           0 :                 return false;
     893             :         }
     894     2058550 :     }
     895             : }
     896             : 
     897             : /************************************************************************/
     898             : /*                       GetSerializedString()                          */
     899             : /************************************************************************/
     900             : 
     901       11174 : std::string CPLJSonStreamingParser::GetSerializedString(const char *pszStr)
     902             : {
     903       11174 :     std::string osStr("\"");
     904       87542 :     for (int i = 0; pszStr[i]; i++)
     905             :     {
     906       76368 :         char ch = pszStr[i];
     907       76368 :         if (ch == '\b')
     908           2 :             osStr += "\\b";
     909       76366 :         else if (ch == '\f')
     910           2 :             osStr += "\\f";
     911       76364 :         else if (ch == '\n')
     912           2 :             osStr += "\\n";
     913       76362 :         else if (ch == '\r')
     914           2 :             osStr += "\\r";
     915       76360 :         else if (ch == '\t')
     916           3 :             osStr += "\\t";
     917       76357 :         else if (ch == '"')
     918           2 :             osStr += "\\\"";
     919       76355 :         else if (ch == '\\')
     920           2 :             osStr += "\\\\";
     921       76353 :         else if (static_cast<unsigned char>(ch) < ' ')
     922           3 :             osStr += CPLSPrintf("\\u%04X", ch);
     923             :         else
     924       76350 :             osStr += ch;
     925             :     }
     926       11174 :     osStr += "\"";
     927       11174 :     return osStr;
     928             : }
     929             : 
     930             : /*! @endcond */

Generated by: LCOV version 1.14