LCOV - code coverage report
Current view: top level - port - cpl_json_streaming_parser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 411 420 97.9 %
Date: 2024-04-28 18:08:58 Functions: 22 23 95.7 %

          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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : /*! @cond Doxygen_Suppress */
      30             : 
      31             : #include <assert.h>
      32             : #include <ctype.h>   // isdigit...
      33             : #include <stdio.h>   // snprintf
      34             : #include <string.h>  // strlen
      35             : #include <vector>
      36             : #include <string>
      37             : 
      38             : #include "cpl_conv.h"
      39             : #include "cpl_string.h"
      40             : #include "cpl_json_streaming_parser.h"
      41             : 
      42             : /************************************************************************/
      43             : /*                       CPLJSonStreamingParser()                       */
      44             : /************************************************************************/
      45             : 
      46         835 : CPLJSonStreamingParser::CPLJSonStreamingParser()
      47             : {
      48         835 :     m_aState.push_back(INIT);
      49         835 : }
      50             : 
      51             : /************************************************************************/
      52             : /*                      ~CPLJSonStreamingParser()                       */
      53             : /************************************************************************/
      54             : 
      55         835 : CPLJSonStreamingParser::~CPLJSonStreamingParser()
      56             : {
      57         835 : }
      58             : 
      59             : /************************************************************************/
      60             : /*                           SetMaxDepth()                              */
      61             : /************************************************************************/
      62             : 
      63           2 : void CPLJSonStreamingParser::SetMaxDepth(size_t nVal)
      64             : {
      65           2 :     m_nMaxDepth = nVal;
      66           2 : }
      67             : 
      68             : /************************************************************************/
      69             : /*                         SetMaxStringSize()                           */
      70             : /************************************************************************/
      71             : 
      72           1 : void CPLJSonStreamingParser::SetMaxStringSize(size_t nVal)
      73             : {
      74           1 :     m_nMaxStringSize = nVal;
      75           1 : }
      76             : 
      77             : /************************************************************************/
      78             : /*                                Reset()                               */
      79             : /************************************************************************/
      80             : 
      81          18 : void CPLJSonStreamingParser::Reset()
      82             : {
      83          18 :     m_bExceptionOccurred = false;
      84          18 :     m_bElementFound = false;
      85          18 :     m_nLastChar = 0;
      86          18 :     m_nLineCounter = 1;
      87          18 :     m_nCharCounter = 1;
      88          18 :     m_aState.clear();
      89          18 :     m_aState.push_back(INIT);
      90          18 :     m_osToken.clear();
      91          18 :     m_abArrayState.clear();
      92          18 :     m_aeObjectState.clear();
      93          18 :     m_bInStringEscape = false;
      94          18 :     m_bInUnicode = false;
      95          18 :     m_osUnicodeHex.clear();
      96          18 : }
      97             : 
      98             : /************************************************************************/
      99             : /*                              AdvanceChar()                           */
     100             : /************************************************************************/
     101             : 
     102     6837690 : void CPLJSonStreamingParser::AdvanceChar(const char *&pStr, size_t &nLength)
     103             : {
     104     6837690 :     if (*pStr == 13 && m_nLastChar != 10)
     105             :     {
     106           3 :         m_nLineCounter++;
     107           3 :         m_nCharCounter = 0;
     108             :     }
     109     6837690 :     else if (*pStr == 10 && m_nLastChar != 13)
     110             :     {
     111       19894 :         m_nLineCounter++;
     112       19894 :         m_nCharCounter = 0;
     113             :     }
     114     6837690 :     m_nLastChar = *pStr;
     115             : 
     116     6837690 :     pStr++;
     117     6837690 :     nLength--;
     118     6837690 :     m_nCharCounter++;
     119     6837690 : }
     120             : 
     121             : /************************************************************************/
     122             : /*                               SkipSpace()                            */
     123             : /************************************************************************/
     124             : 
     125     2367930 : void CPLJSonStreamingParser::SkipSpace(const char *&pStr, size_t &nLength)
     126             : {
     127     2367930 :     while (nLength > 0 && isspace(static_cast<unsigned char>(*pStr)))
     128             :     {
     129      802238 :         AdvanceChar(pStr, nLength);
     130             :     }
     131     1565700 : }
     132             : 
     133             : /************************************************************************/
     134             : /*                             EmitException()                          */
     135             : /************************************************************************/
     136             : 
     137          53 : bool CPLJSonStreamingParser::EmitException(const char *pszMessage)
     138             : {
     139          53 :     m_bExceptionOccurred = true;
     140          53 :     CPLString osMsg;
     141             :     osMsg.Printf("At line %d, character %d: %s", m_nLineCounter, m_nCharCounter,
     142          53 :                  pszMessage);
     143          53 :     Exception(osMsg.c_str());
     144         106 :     return false;
     145             : }
     146             : 
     147             : /************************************************************************/
     148             : /*                          EmitUnexpectedChar()                        */
     149             : /************************************************************************/
     150             : 
     151          28 : bool CPLJSonStreamingParser::EmitUnexpectedChar(char ch,
     152             :                                                 const char *pszExpecting)
     153             : {
     154             :     char szMessage[64];
     155          28 :     if (pszExpecting)
     156             :     {
     157           7 :         snprintf(szMessage, sizeof(szMessage),
     158             :                  "Unexpected character (%c). Expecting %s", ch, pszExpecting);
     159             :     }
     160             :     else
     161             :     {
     162          21 :         snprintf(szMessage, sizeof(szMessage), "Unexpected character (%c)", ch);
     163             :     }
     164          56 :     return EmitException(szMessage);
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                            IsValidNewToken()                         */
     169             : /************************************************************************/
     170             : 
     171      585788 : static bool IsValidNewToken(char ch)
     172             : {
     173      406888 :     return ch == '[' || ch == '{' || ch == '"' || ch == '-' || ch == '.' ||
     174      272138 :            isdigit(static_cast<unsigned char>(ch)) || ch == 't' || ch == 'f' ||
     175      992676 :            ch == 'n' || ch == 'i' || ch == 'I' || ch == 'N';
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                             StartNewToken()                          */
     180             : /************************************************************************/
     181             : 
     182      585779 : bool CPLJSonStreamingParser::StartNewToken(const char *&pStr, size_t &nLength)
     183             : {
     184      585779 :     char ch = *pStr;
     185      585779 :     if (ch == '{')
     186             :     {
     187       14347 :         if (m_aState.size() == m_nMaxDepth)
     188             :         {
     189           1 :             return EmitException("Too many nested objects and/or arrays");
     190             :         }
     191       14346 :         StartObject();
     192       14346 :         m_aeObjectState.push_back(WAITING_KEY);
     193       14346 :         m_aState.push_back(OBJECT);
     194       14346 :         AdvanceChar(pStr, nLength);
     195             :     }
     196      571432 :     else if (ch == '"')
     197             :     {
     198       49025 :         m_aState.push_back(STRING);
     199       49025 :         AdvanceChar(pStr, nLength);
     200             :     }
     201      522407 :     else if (ch == '[')
     202             :     {
     203      178900 :         if (m_aState.size() == m_nMaxDepth)
     204             :         {
     205           1 :             return EmitException("Too many nested objects and/or arrays");
     206             :         }
     207      178899 :         StartArray();
     208      178899 :         m_abArrayState.push_back(ArrayState::INIT);
     209      178899 :         m_aState.push_back(ARRAY);
     210      178899 :         AdvanceChar(pStr, nLength);
     211             :     }
     212      343507 :     else if (ch == '-' || ch == '.' ||
     213      272129 :              isdigit(static_cast<unsigned char>(ch)) || ch == 'i' ||
     214        1498 :              ch == 'I' || ch == 'N')
     215             :     {
     216      342011 :         m_aState.push_back(NUMBER);
     217             :     }
     218        1496 :     else if (ch == 't')
     219             :     {
     220         154 :         m_aState.push_back(STATE_TRUE);
     221             :     }
     222        1342 :     else if (ch == 'f')
     223             :     {
     224          11 :         m_aState.push_back(STATE_FALSE);
     225             :     }
     226        1331 :     else if (ch == 'n')
     227             :     {
     228        1331 :         m_aState.push_back(STATE_NULL); /* might be nan */
     229             :     }
     230             :     else
     231             :     {
     232           0 :         assert(false);
     233             :     }
     234      585777 :     return true;
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                       CheckAndEmitTrueFalseOrNull()                  */
     239             : /************************************************************************/
     240             : 
     241        1485 : bool CPLJSonStreamingParser::CheckAndEmitTrueFalseOrNull(char ch)
     242             : {
     243        1485 :     State eCurState = currentState();
     244             : 
     245        1485 :     if (eCurState == STATE_TRUE)
     246             :     {
     247         151 :         if (m_osToken == "true")
     248             :         {
     249         150 :             Boolean(true);
     250             :         }
     251             :         else
     252             :         {
     253           1 :             return EmitUnexpectedChar(ch);
     254             :         }
     255             :     }
     256        1334 :     else if (eCurState == STATE_FALSE)
     257             :     {
     258           9 :         if (m_osToken == "false")
     259             :         {
     260           8 :             Boolean(false);
     261             :         }
     262             :         else
     263             :         {
     264           1 :             return EmitUnexpectedChar(ch);
     265             :         }
     266             :     }
     267             :     else /* if( eCurState == STATE_NULL ) */
     268             :     {
     269        1325 :         if (m_osToken == "null")
     270             :         {
     271        1324 :             Null();
     272             :         }
     273             :         else
     274             :         {
     275           1 :             return EmitUnexpectedChar(ch);
     276             :         }
     277             :     }
     278        1482 :     m_aState.pop_back();
     279        1482 :     m_osToken.clear();
     280        1482 :     return true;
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                           CheckStackEmpty()                          */
     285             : /************************************************************************/
     286             : 
     287          26 : bool CPLJSonStreamingParser::CheckStackEmpty()
     288             : {
     289          26 :     if (!m_aeObjectState.empty())
     290             :     {
     291           1 :         return EmitException("Unterminated object");
     292             :     }
     293          25 :     else if (!m_abArrayState.empty())
     294             :     {
     295           1 :         return EmitException("Unterminated array");
     296             :     }
     297          24 :     return true;
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                           IsHighSurrogate()                          */
     302             : /************************************************************************/
     303             : 
     304          21 : static bool IsHighSurrogate(unsigned uc)
     305             : {
     306          21 :     return (uc & 0xFC00) == 0xD800;
     307             : }
     308             : 
     309             : /************************************************************************/
     310             : /*                           IsLowSurrogate()                           */
     311             : /************************************************************************/
     312             : 
     313           8 : static bool IsLowSurrogate(unsigned uc)
     314             : {
     315           8 :     return (uc & 0xFC00) == 0xDC00;
     316             : }
     317             : 
     318             : /************************************************************************/
     319             : /*                         GetSurrogatePair()                           */
     320             : /************************************************************************/
     321             : 
     322           1 : static unsigned GetSurrogatePair(unsigned hi, unsigned lo)
     323             : {
     324           1 :     return ((hi & 0x3FF) << 10) + (lo & 0x3FF) + 0x10000;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                            IsHexDigit()                              */
     329             : /************************************************************************/
     330             : 
     331          69 : static bool IsHexDigit(char ch)
     332             : {
     333          81 :     return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     334          81 :            (ch >= 'A' && ch <= 'F');
     335             : }
     336             : 
     337             : /************************************************************************/
     338             : /*                           HexToDecimal()                             */
     339             : /************************************************************************/
     340             : 
     341         116 : static unsigned HexToDecimal(char ch)
     342             : {
     343         116 :     if (ch >= '0' && ch <= '9')
     344          93 :         return ch - '0';
     345          23 :     if (ch >= 'a' && ch <= 'f')
     346           8 :         return 10 + ch - 'a';
     347             :     // if (ch >= 'A' && ch <= 'F' )
     348          15 :     return 10 + ch - 'A';
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /*                            getUCSChar()                              */
     353             : /************************************************************************/
     354             : 
     355          29 : static unsigned getUCSChar(const std::string &unicode4HexChar)
     356             : {
     357          29 :     return (HexToDecimal(unicode4HexChar[0]) << 12) |
     358          29 :            (HexToDecimal(unicode4HexChar[1]) << 8) |
     359          29 :            (HexToDecimal(unicode4HexChar[2]) << 4) |
     360          29 :            (HexToDecimal(unicode4HexChar[3]));
     361             : }
     362             : 
     363             : /************************************************************************/
     364             : /*                           DecodeUnicode()                            */
     365             : /************************************************************************/
     366             : 
     367          13 : void CPLJSonStreamingParser::DecodeUnicode()
     368             : {
     369          13 :     constexpr char szReplacementUTF8[] = "\xEF\xBF\xBD";
     370             :     unsigned nUCSChar;
     371          13 :     if (m_osUnicodeHex.size() == 8)
     372             :     {
     373           2 :         unsigned nUCSHigh = getUCSChar(m_osUnicodeHex);
     374           2 :         assert(IsHighSurrogate(nUCSHigh));
     375           2 :         unsigned nUCSLow = getUCSChar(m_osUnicodeHex.substr(4));
     376           2 :         if (IsLowSurrogate(nUCSLow))
     377             :         {
     378           1 :             nUCSChar = GetSurrogatePair(nUCSHigh, nUCSLow);
     379             :         }
     380             :         else
     381             :         {
     382             :             /* Invalid code point. Insert the replacement char */
     383           1 :             nUCSChar = 0xFFFFFFFFU;
     384             :         }
     385             :     }
     386             :     else
     387             :     {
     388          11 :         assert(m_osUnicodeHex.size() == 4);
     389          11 :         nUCSChar = getUCSChar(m_osUnicodeHex);
     390             :     }
     391             : 
     392          13 :     if (nUCSChar < 0x80)
     393             :     {
     394           6 :         m_osToken += static_cast<char>(nUCSChar);
     395             :     }
     396           7 :     else if (nUCSChar < 0x800)
     397             :     {
     398           1 :         m_osToken += static_cast<char>(0xC0 | (nUCSChar >> 6));
     399           1 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     400             :     }
     401           6 :     else if (IsLowSurrogate(nUCSChar) || IsHighSurrogate(nUCSChar))
     402             :     {
     403             :         /* Invalid code point. Insert the replacement char */
     404           4 :         m_osToken += szReplacementUTF8;
     405             :     }
     406           2 :     else if (nUCSChar < 0x10000)
     407             :     {
     408           0 :         m_osToken += static_cast<char>(0xE0 | (nUCSChar >> 12));
     409           0 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 6) & 0x3F));
     410           0 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     411             :     }
     412           2 :     else if (nUCSChar < 0x110000)
     413             :     {
     414           1 :         m_osToken += static_cast<char>(0xF0 | ((nUCSChar >> 18) & 0x07));
     415           1 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 12) & 0x3F));
     416           1 :         m_osToken += static_cast<char>(0x80 | ((nUCSChar >> 6) & 0x3F));
     417           1 :         m_osToken += static_cast<char>(0x80 | (nUCSChar & 0x3F));
     418             :     }
     419             :     else
     420             :     {
     421             :         /* Invalid code point. Insert the replacement char */
     422           1 :         m_osToken += szReplacementUTF8;
     423             :     }
     424             : 
     425          13 :     m_bInUnicode = false;
     426          13 :     m_osUnicodeHex.clear();
     427          13 : }
     428             : 
     429             : /************************************************************************/
     430             : /*                              Parse()                                 */
     431             : /************************************************************************/
     432             : 
     433        2841 : bool CPLJSonStreamingParser::Parse(const char *pStr, size_t nLength,
     434             :                                    bool bFinished)
     435             : {
     436        2841 :     if (m_bExceptionOccurred)
     437           0 :         return false;
     438             : 
     439             :     while (true)
     440             :     {
     441     1566930 :         State eCurState = currentState();
     442     1566930 :         if (eCurState == INIT)
     443             :         {
     444        1607 :             SkipSpace(pStr, nLength);
     445        1607 :             if (nLength == 0)
     446         856 :                 return true;
     447         751 :             if (m_bElementFound || !IsValidNewToken(*pStr))
     448             :             {
     449           4 :                 return EmitUnexpectedChar(*pStr);
     450             :             }
     451         747 :             if (!StartNewToken(pStr, nLength))
     452             :             {
     453           2 :                 return false;
     454             :             }
     455         745 :             m_bElementFound = true;
     456             :         }
     457     1565320 :         else if (eCurState == NUMBER)
     458             :         {
     459     5122190 :             while (nLength)
     460             :             {
     461     5122020 :                 char ch = *pStr;
     462     5122020 :                 if (ch == '+' || ch == '-' ||
     463     5050630 :                     isdigit(static_cast<unsigned char>(ch)) || ch == '.' ||
     464      342124 :                     ch == 'e' || ch == 'E')
     465             :                 {
     466     4779900 :                     if (m_osToken.size() == 1024)
     467             :                     {
     468           0 :                         return EmitException("Too many characters in number");
     469             :                     }
     470     4779900 :                     m_osToken += ch;
     471             :                 }
     472      342122 :                 else if (isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
     473       28295 :                          ch == '}' || ch == ']')
     474             :                 {
     475      342011 :                     SkipSpace(pStr, nLength);
     476      342011 :                     break;
     477             :                 }
     478             :                 else
     479             :                 {
     480         111 :                     CPLString extendedToken(m_osToken + ch);
     481         111 :                     if ((STARTS_WITH_CI("Infinity", extendedToken) &&
     482          47 :                          m_osToken.size() + 1 <= strlen("Infinity")) ||
     483          64 :                         (STARTS_WITH_CI("-Infinity", extendedToken) &&
     484         222 :                          m_osToken.size() + 1 <= strlen("-Infinity")) ||
     485          17 :                         (STARTS_WITH_CI("NaN", extendedToken) &&
     486          13 :                          m_osToken.size() + 1 <= strlen("NaN")))
     487             :                     {
     488         107 :                         m_osToken += ch;
     489             :                     }
     490             :                     else
     491             :                     {
     492           4 :                         return EmitUnexpectedChar(ch);
     493             :                     }
     494             :                 }
     495     4780000 :                 AdvanceChar(pStr, nLength);
     496             :             }
     497             : 
     498      342182 :             if (nLength != 0 || bFinished)
     499             :             {
     500      342011 :                 const char firstCh = m_osToken[0];
     501      342011 :                 if (firstCh == 'i' || firstCh == 'I')
     502             :                 {
     503           5 :                     if (!EQUAL(m_osToken.c_str(), "Infinity"))
     504             :                     {
     505           1 :                         return EmitException("Invalid number");
     506             :                     }
     507             :                 }
     508      342006 :                 else if (firstCh == '-')
     509             :                 {
     510       71377 :                     if (m_osToken[1] == 'i' || m_osToken[1] == 'I')
     511             :                     {
     512           5 :                         if (!EQUAL(m_osToken.c_str(), "-Infinity"))
     513             :                         {
     514           1 :                             return EmitException("Invalid number");
     515             :                         }
     516             :                     }
     517             :                 }
     518      270629 :                 else if (firstCh == 'n' || firstCh == 'N')
     519             :                 {
     520           5 :                     if (m_osToken[1] == 'a' || m_osToken[1] == 'A')
     521             :                     {
     522           5 :                         if (!EQUAL(m_osToken.c_str(), "NaN"))
     523             :                         {
     524           1 :                             return EmitException("Invalid number");
     525             :                         }
     526             :                     }
     527             :                 }
     528             : 
     529      342008 :                 Number(m_osToken.c_str(), m_osToken.size());
     530      342008 :                 m_osToken.clear();
     531      342008 :                 m_aState.pop_back();
     532             :             }
     533             : 
     534      342179 :             if (nLength == 0)
     535             :             {
     536         182 :                 if (bFinished)
     537             :                 {
     538          11 :                     return CheckStackEmpty();
     539             :                 }
     540         171 :                 return true;
     541             :             }
     542             :         }
     543     1223140 :         else if (eCurState == STRING)
     544             :         {
     545       49942 :             bool bEOS = false;
     546      422177 :             while (nLength)
     547             :             {
     548      421255 :                 if (m_osToken.size() == m_nMaxStringSize)
     549             :                 {
     550           1 :                     return EmitException("Too many characters in number");
     551             :                 }
     552             : 
     553      421254 :                 char ch = *pStr;
     554      421254 :                 if (m_bInUnicode)
     555             :                 {
     556          81 :                     if (m_osUnicodeHex.size() == 8)
     557             :                     {
     558           2 :                         DecodeUnicode();
     559             :                     }
     560          79 :                     else if (m_osUnicodeHex.size() == 4)
     561             :                     {
     562             :                         /* Start of next surrogate pair ? */
     563          13 :                         if (m_nLastChar == '\\')
     564             :                         {
     565           4 :                             if (ch == 'u')
     566             :                             {
     567           3 :                                 AdvanceChar(pStr, nLength);
     568           3 :                                 continue;
     569             :                             }
     570             :                             else
     571             :                             {
     572             :                                 /* will be replacement character */
     573           1 :                                 DecodeUnicode();
     574           1 :                                 m_bInStringEscape = true;
     575             :                             }
     576             :                         }
     577           9 :                         else if (m_nLastChar == 'u')
     578             :                         {
     579           3 :                             if (IsHexDigit(ch))
     580             :                             {
     581           2 :                                 m_osUnicodeHex += ch;
     582             :                             }
     583             :                             else
     584             :                             {
     585             :                                 char szMessage[64];
     586           1 :                                 snprintf(szMessage, sizeof(szMessage),
     587             :                                          "Illegal character in unicode "
     588             :                                          "sequence (\\%c)",
     589             :                                          ch);
     590           1 :                                 return EmitException(szMessage);
     591             :                             }
     592           2 :                             AdvanceChar(pStr, nLength);
     593           2 :                             continue;
     594             :                         }
     595           6 :                         else if (ch == '\\')
     596             :                         {
     597           4 :                             AdvanceChar(pStr, nLength);
     598           4 :                             continue;
     599             :                         }
     600             :                         else
     601             :                         {
     602             :                             /* will be replacement character */
     603           2 :                             DecodeUnicode();
     604             :                         }
     605             :                     }
     606             :                     else
     607             :                     {
     608          66 :                         if (IsHexDigit(ch))
     609             :                         {
     610          65 :                             m_osUnicodeHex += ch;
     611          79 :                             if (m_osUnicodeHex.size() == 4 &&
     612          14 :                                 !IsHighSurrogate(getUCSChar(m_osUnicodeHex)))
     613             :                             {
     614           8 :                                 DecodeUnicode();
     615             :                             }
     616             :                         }
     617             :                         else
     618             :                         {
     619             :                             char szMessage[64];
     620           1 :                             snprintf(szMessage, sizeof(szMessage),
     621             :                                      "Illegal character in unicode "
     622             :                                      "sequence (\\%c)",
     623             :                                      ch);
     624           1 :                             return EmitException(szMessage);
     625             :                         }
     626          65 :                         AdvanceChar(pStr, nLength);
     627          65 :                         continue;
     628             :                     }
     629             :                 }
     630             : 
     631      421178 :                 if (m_bInStringEscape)
     632             :                 {
     633         227 :                     if (ch == '"' || ch == '\\' || ch == '/')
     634         198 :                         m_osToken += ch;
     635          29 :                     else if (ch == 'b')
     636           2 :                         m_osToken += '\b';
     637          27 :                     else if (ch == 'f')
     638           2 :                         m_osToken += '\f';
     639          25 :                     else if (ch == 'n')
     640           2 :                         m_osToken += '\n';
     641          23 :                     else if (ch == 'r')
     642           2 :                         m_osToken += '\r';
     643          21 :                     else if (ch == 't')
     644           3 :                         m_osToken += '\t';
     645          18 :                     else if (ch == 'u')
     646             :                     {
     647          17 :                         m_bInUnicode = true;
     648             :                     }
     649             :                     else
     650             :                     {
     651             :                         char szMessage[32];
     652           1 :                         snprintf(szMessage, sizeof(szMessage),
     653             :                                  "Illegal escape sequence (\\%c)", ch);
     654           1 :                         return EmitException(szMessage);
     655             :                     }
     656         226 :                     m_bInStringEscape = false;
     657         226 :                     AdvanceChar(pStr, nLength);
     658         226 :                     continue;
     659             :                 }
     660      420951 :                 else if (ch == '\\')
     661             :                 {
     662         227 :                     m_bInStringEscape = true;
     663         227 :                     AdvanceChar(pStr, nLength);
     664         227 :                     continue;
     665             :                 }
     666      420724 :                 else if (ch == '"')
     667             :                 {
     668       49016 :                     bEOS = true;
     669       49016 :                     AdvanceChar(pStr, nLength);
     670       49016 :                     SkipSpace(pStr, nLength);
     671             : 
     672       98024 :                     if (!m_aeObjectState.empty() &&
     673       49008 :                         m_aeObjectState.back() == IN_KEY)
     674             :                     {
     675       35341 :                         StartObjectMember(m_osToken.c_str(), m_osToken.size());
     676             :                     }
     677             :                     else
     678             :                     {
     679       13675 :                         String(m_osToken.c_str(), m_osToken.size());
     680             :                     }
     681       49016 :                     m_osToken.clear();
     682       49016 :                     m_aState.pop_back();
     683             : 
     684       49016 :                     break;
     685             :                 }
     686             : 
     687      371708 :                 m_osToken += ch;
     688      371708 :                 AdvanceChar(pStr, nLength);
     689             :             }
     690             : 
     691       49938 :             if (nLength == 0)
     692             :             {
     693        1060 :                 if (bFinished)
     694             :                 {
     695          14 :                     if (!bEOS)
     696             :                     {
     697           5 :                         return EmitException("Unterminated string");
     698             :                     }
     699           9 :                     return CheckStackEmpty();
     700             :                 }
     701        1046 :                 return true;
     702             :             }
     703             :         }
     704     1173190 :         else if (eCurState == ARRAY)
     705             :         {
     706     1028890 :             SkipSpace(pStr, nLength);
     707     1028890 :             if (nLength == 0)
     708             :             {
     709         112 :                 if (bFinished)
     710             :                 {
     711           2 :                     return EmitException("Unterminated array");
     712             :                 }
     713         110 :                 return true;
     714             :             }
     715             : 
     716     1028780 :             char ch = *pStr;
     717     1028780 :             if (ch == ',')
     718             :             {
     719      335532 :                 if (m_abArrayState.back() != ArrayState::AFTER_VALUE)
     720             :                 {
     721           3 :                     return EmitUnexpectedChar(ch, "','");
     722             :                 }
     723      335529 :                 m_abArrayState.back() = ArrayState::AFTER_COMMA;
     724      335529 :                 AdvanceChar(pStr, nLength);
     725             :             }
     726      693245 :             else if (ch == ']')
     727             :             {
     728      178888 :                 if (m_abArrayState.back() == ArrayState::AFTER_COMMA)
     729             :                 {
     730           1 :                     return EmitException("Missing value");
     731             :                 }
     732             : 
     733      178887 :                 EndArray();
     734      178887 :                 AdvanceChar(pStr, nLength);
     735      178887 :                 m_abArrayState.pop_back();
     736      178887 :                 m_aState.pop_back();
     737             :             }
     738      514357 :             else if (IsValidNewToken(ch))
     739             :             {
     740      514356 :                 if (m_abArrayState.back() == ArrayState::AFTER_VALUE)
     741             :                 {
     742           1 :                     return EmitException(
     743           1 :                         "Unexpected state: ',' or ']' expected");
     744             :                 }
     745      514355 :                 m_abArrayState.back() = ArrayState::AFTER_VALUE;
     746             : 
     747      514355 :                 StartArrayMember();
     748      514355 :                 if (!StartNewToken(pStr, nLength))
     749             :                 {
     750           0 :                     return false;
     751             :                 }
     752             :             }
     753             :             else
     754             :             {
     755           1 :                 return EmitUnexpectedChar(ch);
     756             :             }
     757             :         }
     758      144304 :         else if (eCurState == OBJECT)
     759             :         {
     760      142678 :             SkipSpace(pStr, nLength);
     761      142678 :             if (nLength == 0)
     762             :             {
     763         454 :                 if (bFinished)
     764             :                 {
     765           2 :                     return EmitException("Unterminated object");
     766             :                 }
     767         452 :                 return true;
     768             :             }
     769             : 
     770      142224 :             char ch = *pStr;
     771      142224 :             if (ch == ',')
     772             :             {
     773       21873 :                 if (m_aeObjectState.back() != IN_VALUE)
     774             :                 {
     775           2 :                     return EmitUnexpectedChar(ch, "','");
     776             :                 }
     777             : 
     778       21871 :                 m_aeObjectState.back() = WAITING_KEY;
     779       21871 :                 AdvanceChar(pStr, nLength);
     780             :             }
     781      120351 :             else if (ch == ':')
     782             :             {
     783       35338 :                 if (m_aeObjectState.back() != IN_KEY)
     784             :                 {
     785           1 :                     return EmitUnexpectedChar(ch, "':'");
     786             :                 }
     787       35337 :                 m_aeObjectState.back() = KEY_FINISHED;
     788       35337 :                 AdvanceChar(pStr, nLength);
     789             :             }
     790       85013 :             else if (ch == '}')
     791             :             {
     792       27793 :                 if (m_aeObjectState.back() == WAITING_KEY ||
     793       13461 :                     m_aeObjectState.back() == IN_VALUE)
     794             :                 {
     795             :                     // nothing
     796             :                 }
     797             :                 else
     798             :                 {
     799           1 :                     return EmitException("Missing value");
     800             :                 }
     801             : 
     802       14331 :                 EndObject();
     803       14331 :                 AdvanceChar(pStr, nLength);
     804       14331 :                 m_aeObjectState.pop_back();
     805       14331 :                 m_aState.pop_back();
     806             :             }
     807       70681 :             else if (IsValidNewToken(ch))
     808             :             {
     809       70679 :                 if (m_aeObjectState.back() == WAITING_KEY)
     810             :                 {
     811       35342 :                     if (ch != '"')
     812             :                     {
     813           1 :                         return EmitUnexpectedChar(ch, "'\"'");
     814             :                     }
     815       35341 :                     m_aeObjectState.back() = IN_KEY;
     816             :                 }
     817       35337 :                 else if (m_aeObjectState.back() == KEY_FINISHED)
     818             :                 {
     819       35336 :                     m_aeObjectState.back() = IN_VALUE;
     820             :                 }
     821             :                 else
     822             :                 {
     823           1 :                     return EmitException("Unexpected state");
     824             :                 }
     825       70677 :                 if (!StartNewToken(pStr, nLength))
     826             :                 {
     827           0 :                     return false;
     828             :                 }
     829             :             }
     830             :             else
     831             :             {
     832           2 :                 return EmitUnexpectedChar(ch);
     833             :             }
     834             :         }
     835             :         else /* if( eCurState == STATE_TRUE || eCurState == STATE_FALSE ||
     836             :                     eCurState == STATE_NULL ) */
     837             :         {
     838        7602 :             while (nLength)
     839             :             {
     840        7482 :                 char ch = *pStr;
     841        7486 :                 if (eCurState == STATE_NULL && (ch == 'a' || ch == 'A') &&
     842           4 :                     m_osToken.size() == 1)
     843             :                 {
     844           4 :                     m_aState.back() = NUMBER;
     845           4 :                     break;
     846             :                 }
     847        7478 :                 if (isalpha(static_cast<unsigned char>(ch)))
     848             :                 {
     849        5982 :                     m_osToken += ch;
     850        7211 :                     if (eCurState == STATE_TRUE &&
     851         615 :                         (m_osToken.size() > strlen("true") ||
     852         614 :                          memcmp(m_osToken.c_str(), "true", m_osToken.size()) !=
     853             :                              0))
     854             :                     {
     855           2 :                         return EmitUnexpectedChar(*pStr);
     856             :                     }
     857        6089 :                     else if (eCurState == STATE_FALSE &&
     858          55 :                              (m_osToken.size() > strlen("false") ||
     859          54 :                               memcmp(m_osToken.c_str(), "false",
     860             :                                      m_osToken.size()) != 0))
     861             :                     {
     862           2 :                         return EmitUnexpectedChar(*pStr);
     863             :                     }
     864       16601 :                     else if (eCurState == STATE_NULL &&
     865        5312 :                              (m_osToken.size() > strlen("null") ||
     866        5311 :                               memcmp(m_osToken.c_str(), "null",
     867             :                                      m_osToken.size()) != 0))
     868             :                     {
     869           2 :                         return EmitUnexpectedChar(*pStr);
     870             :                     }
     871             :                 }
     872        1496 :                 else if (isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
     873          51 :                          ch == '}' || ch == ']')
     874             :                 {
     875        1495 :                     SkipSpace(pStr, nLength);
     876        1495 :                     break;
     877             :                 }
     878             :                 else
     879             :                 {
     880           1 :                     return EmitUnexpectedChar(ch);
     881             :                 }
     882        5976 :                 AdvanceChar(pStr, nLength);
     883             :             }
     884        1619 :             if (m_aState.back() == NUMBER)
     885             :             {
     886           4 :                 continue;
     887             :             }
     888        1615 :             if (nLength == 0)
     889             :             {
     890         139 :                 if (bFinished)
     891             :                 {
     892           9 :                     if (!CheckAndEmitTrueFalseOrNull(0))
     893           3 :                         return false;
     894           6 :                     return CheckStackEmpty();
     895             :                 }
     896         130 :                 return true;
     897             :             }
     898             : 
     899        1476 :             if (!CheckAndEmitTrueFalseOrNull(*pStr))
     900           0 :                 return false;
     901             :         }
     902     1564090 :     }
     903             : }
     904             : 
     905             : /************************************************************************/
     906             : /*                       GetSerializedString()                          */
     907             : /************************************************************************/
     908             : 
     909        6954 : std::string CPLJSonStreamingParser::GetSerializedString(const char *pszStr)
     910             : {
     911        6954 :     std::string osStr("\"");
     912       54138 :     for (int i = 0; pszStr[i]; i++)
     913             :     {
     914       47184 :         char ch = pszStr[i];
     915       47184 :         if (ch == '\b')
     916           2 :             osStr += "\\b";
     917       47182 :         else if (ch == '\f')
     918           2 :             osStr += "\\f";
     919       47180 :         else if (ch == '\n')
     920           2 :             osStr += "\\n";
     921       47178 :         else if (ch == '\r')
     922           2 :             osStr += "\\r";
     923       47176 :         else if (ch == '\t')
     924           3 :             osStr += "\\t";
     925       47173 :         else if (ch == '"')
     926           2 :             osStr += "\\\"";
     927       47171 :         else if (ch == '\\')
     928           2 :             osStr += "\\\\";
     929       47169 :         else if (static_cast<unsigned char>(ch) < ' ')
     930           3 :             osStr += CPLSPrintf("\\u%04X", ch);
     931             :         else
     932       47166 :             osStr += ch;
     933             :     }
     934        6954 :     osStr += "\"";
     935        6954 :     return osStr;
     936             : }
     937             : 
     938             : /*! @endcond */

Generated by: LCOV version 1.14