| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890 |
- /*
- * coreJSON
- * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to
- * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- * the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /**
- * @file core_json.c
- * @brief The source file that implements the user-facing functions in core_json.h.
- */
- #include <limits.h>
- #include <stddef.h>
- #include <stdint.h>
- #include "core_json.h"
- /** @cond DO_NOT_DOCUMENT */
- /* A compromise to satisfy both MISRA and CBMC */
- typedef union
- {
- char c;
- uint8_t u;
- } char_;
- #if ( CHAR_MIN == 0 )
- #define isascii_( x ) ( ( x ) <= '\x7F' )
- #else
- #define isascii_( x ) ( ( x ) >= '\0' )
- #endif
- #define iscntrl_( x ) ( isascii_( x ) && ( ( x ) < ' ' ) )
- #define isdigit_( x ) ( ( ( x ) >= '0' ) && ( ( x ) <= '9' ) )
- /* NB. This is whitespace as defined by the JSON standard (ECMA-404). */
- #define isspace_( x ) \
- ( ( ( x ) == ' ' ) || ( ( x ) == '\t' ) || \
- ( ( x ) == '\n' ) || ( ( x ) == '\r' ) )
- #define isOpenBracket_( x ) ( ( ( x ) == '{' ) || ( ( x ) == '[' ) )
- #define isCloseBracket_( x ) ( ( ( x ) == '}' ) || ( ( x ) == ']' ) )
- #define isCurlyPair_( x, y ) ( ( ( x ) == '{' ) && ( ( y ) == '}' ) )
- #define isSquarePair_( x, y ) ( ( ( x ) == '[' ) && ( ( y ) == ']' ) )
- #define isMatchingBracket_( x, y ) ( isCurlyPair_( x, y ) || isSquarePair_( x, y ) )
- #define isSquareOpen_( x ) ( ( x ) == '[' )
- #define isSquareClose_( x ) ( ( x ) == ']' )
- #define isCurlyOpen_( x ) ( ( x ) == '{' )
- #define isCurlyClose_( x ) ( ( x ) == '}' )
- /**
- * @brief Advance buffer index beyond whitespace.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- */
- static void skipSpace( const char * buf,
- size_t * start,
- size_t max )
- {
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- for( i = *start; i < max; i++ )
- {
- if( !isspace_( buf[ i ] ) )
- {
- break;
- }
- }
- *start = i;
- }
- /**
- * @brief Count the leading 1s in a byte.
- *
- * The high-order 1 bits of the first byte in a UTF-8 encoding
- * indicate the number of additional bytes to follow.
- *
- * @return the count
- */
- static size_t countHighBits( uint8_t c )
- {
- uint8_t n = c;
- size_t i = 0;
- while( ( n & 0x80U ) != 0U )
- {
- i++;
- n = ( n & 0x7FU ) << 1U;
- }
- return i;
- }
- /**
- * @brief Is the value a legal Unicode code point and encoded with
- * the fewest bytes?
- *
- * The last Unicode code point is 0x10FFFF.
- *
- * Unicode 3.1 disallows UTF-8 interpretation of non-shortest form sequences.
- * 1 byte encodes 0 through 7 bits
- * 2 bytes encode 8 through 5+6 = 11 bits
- * 3 bytes encode 12 through 4+6+6 = 16 bits
- * 4 bytes encode 17 through 3+6+6+6 = 21 bits
- *
- * Unicode 3.2 disallows UTF-8 code point values in the surrogate range,
- * [U+D800 to U+DFFF].
- *
- * @note Disallow ASCII, as this is called only for multibyte sequences.
- */
- static bool shortestUTF8( size_t length,
- uint32_t value )
- {
- bool ret = false;
- uint32_t min = 0U, max = 0U;
- coreJSON_ASSERT( ( length >= 2U ) && ( length <= 4U ) );
- switch( length )
- {
- case 2:
- min = ( uint32_t ) 1 << 7U;
- max = ( ( uint32_t ) 1 << 11U ) - 1U;
- break;
- case 3:
- min = ( uint32_t ) 1 << 11U;
- max = ( ( uint32_t ) 1 << 16U ) - 1U;
- break;
- default:
- min = ( uint32_t ) 1 << 16U;
- max = 0x10FFFFU;
- break;
- }
- if( ( value >= min ) && ( value <= max ) &&
- ( ( value < 0xD800U ) || ( value > 0xDFFFU ) ) )
- {
- ret = true;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a UTF-8 code point.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid code point was present;
- * false otherwise.
- *
- * 00-7F Single-byte character
- * 80-BF Trailing byte
- * C0-DF Leading byte of two-byte character
- * E0-EF Leading byte of three-byte character
- * F0-F7 Leading byte of four-byte character
- * F8-FB Illegal (formerly leading byte of five-byte character)
- * FC-FD Illegal (formerly leading byte of six-byte character)
- * FE-FF Illegal
- *
- * The octet values C0, C1, and F5 to FF are illegal, since C0 and C1
- * would introduce a non-shortest sequence, and F5 or above would
- * introduce a value greater than the last code point, 0x10FFFF.
- */
- static bool skipUTF8MultiByte( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- size_t i = 0U, bitCount = 0U, j = 0U;
- uint32_t value = 0U;
- char_ c;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- coreJSON_ASSERT( i < max );
- coreJSON_ASSERT( !isascii_( buf[ i ] ) );
- c.c = buf[ i ];
- if( ( c.u > 0xC1U ) && ( c.u < 0xF5U ) )
- {
- bitCount = countHighBits( c.u );
- value = ( ( uint32_t ) c.u ) & ( ( ( uint32_t ) 1 << ( 7U - bitCount ) ) - 1U );
- /* The bit count is 1 greater than the number of bytes,
- * e.g., when j is 2, we skip one more byte. */
- for( j = bitCount - 1U; j > 0U; j-- )
- {
- i++;
- if( i >= max )
- {
- break;
- }
- c.c = buf[ i ];
- /* Additional bytes must match 10xxxxxx. */
- if( ( c.u & 0xC0U ) != 0x80U )
- {
- break;
- }
- value = ( value << 6U ) | ( c.u & 0x3FU );
- }
- if( ( j == 0U ) && ( shortestUTF8( bitCount, value ) == true ) )
- {
- *start = i + 1U;
- ret = true;
- }
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond an ASCII or UTF-8 code point.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid code point was present;
- * false otherwise.
- */
- static bool skipUTF8( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- if( *start < max )
- {
- if( isascii_( buf[ *start ] ) )
- {
- *start += 1U;
- ret = true;
- }
- else
- {
- ret = skipUTF8MultiByte( buf, start, max );
- }
- }
- return ret;
- }
- /**
- * @brief Convert a hexadecimal character to an integer.
- *
- * @param[in] c The character to convert.
- *
- * @return the integer value upon success or NOT_A_HEX_CHAR on failure.
- */
- #define NOT_A_HEX_CHAR ( 0x10U )
- static uint8_t hexToInt( char c )
- {
- char_ n;
- n.c = c;
- if( ( c >= 'a' ) && ( c <= 'f' ) )
- {
- n.c -= 'a';
- n.u += 10U;
- }
- else if( ( c >= 'A' ) && ( c <= 'F' ) )
- {
- n.c -= 'A';
- n.u += 10U;
- }
- else if( isdigit_( c ) )
- {
- n.c -= '0';
- }
- else
- {
- n.u = NOT_A_HEX_CHAR;
- }
- return n.u;
- }
- /**
- * @brief Advance buffer index beyond a single \u Unicode
- * escape sequence and output the value.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[out] outValue The value of the hex digits.
- *
- * @return true if a valid escape sequence was present;
- * false otherwise.
- */
- static bool skipOneHexEscape( const char * buf,
- size_t * start,
- size_t max,
- uint16_t * outValue )
- {
- bool ret = false;
- size_t i = 0U, end = 0U;
- uint16_t value = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) && ( outValue != NULL ) );
- i = *start;
- #define HEX_ESCAPE_LENGTH ( 6U ) /* e.g., \u1234 */
- /* MISRA Ref 14.3.1 [Configuration dependent invariant] */
- /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-143 */
- /* coverity[misra_c_2012_rule_14_3_violation] */
- end = i + HEX_ESCAPE_LENGTH;
- if( ( end > i ) && ( end < max ) && ( buf[ i ] == '\\' ) && ( buf[ i + 1U ] == 'u' ) )
- {
- for( i += 2U; i < end; i++ )
- {
- uint8_t n = hexToInt( buf[ i ] );
- if( n == NOT_A_HEX_CHAR )
- {
- break;
- }
- value = ( value << 4U ) | n;
- }
- }
- if( i == end )
- {
- ret = true;
- *outValue = value;
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond one or a pair of \u Unicode escape sequences.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * Surrogate pairs are two escape sequences that together denote
- * a code point outside the Basic Multilingual Plane. They must
- * occur as a pair with the first "high" value in [U+D800, U+DBFF],
- * and the second "low" value in [U+DC00, U+DFFF].
- *
- * @return true if a valid escape sequence was present;
- * false otherwise.
- */
- #define isHighSurrogate( x ) ( ( ( x ) >= 0xD800U ) && ( ( x ) <= 0xDBFFU ) )
- #define isLowSurrogate( x ) ( ( ( x ) >= 0xDC00U ) && ( ( x ) <= 0xDFFFU ) )
- static bool skipHexEscape( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- size_t i = 0U;
- uint16_t value = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- if( skipOneHexEscape( buf, &i, max, &value ) == true )
- {
- if( isHighSurrogate( value ) )
- {
- if( ( skipOneHexEscape( buf, &i, max, &value ) == true ) &&
- ( isLowSurrogate( value ) ) )
- {
- ret = true;
- }
- }
- else if( isLowSurrogate( value ) )
- {
- /* premature low surrogate */
- }
- else
- {
- ret = true;
- }
- }
- if( ret == true )
- {
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond an escape sequence.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid escape sequence was present;
- * false otherwise.
- */
- static bool skipEscape( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- if( ( i < ( max - 1U ) ) && ( buf[ i ] == '\\' ) )
- {
- char c = buf[ i + 1U ];
- switch( c )
- {
- case 'u':
- ret = skipHexEscape( buf, &i, max );
- break;
- case '"':
- case '\\':
- case '/':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- i += 2U;
- ret = true;
- break;
- default:
- break;
- }
- }
- if( ret == true )
- {
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a double-quoted string.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid string was present;
- * false otherwise.
- */
- static bool skipString( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- size_t i = 0;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- if( ( i < max ) && ( buf[ i ] == '"' ) )
- {
- i++;
- while( i < max )
- {
- if( buf[ i ] == '"' )
- {
- ret = true;
- i++;
- break;
- }
- if( buf[ i ] == '\\' )
- {
- if( skipEscape( buf, &i, max ) != true )
- {
- break;
- }
- }
- /* An unescaped control character is not allowed. */
- else if( iscntrl_( buf[ i ] ) )
- {
- break;
- }
- else if( skipUTF8( buf, &i, max ) != true )
- {
- break;
- }
- else
- {
- /* MISRA 15.7 */
- }
- }
- }
- if( ret == true )
- {
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Compare the leading n bytes of two character sequences.
- *
- * @param[in] a first character sequence
- * @param[in] b second character sequence
- * @param[in] n number of bytes
- *
- * @return true if the sequences are the same;
- * false otherwise
- */
- static bool strnEq( const char * a,
- const char * b,
- size_t n )
- {
- size_t i = 0U;
- coreJSON_ASSERT( ( a != NULL ) && ( b != NULL ) );
- for( i = 0; i < n; i++ )
- {
- if( a[ i ] != b[ i ] )
- {
- break;
- }
- }
- return ( i == n ) ? true : false;
- }
- /**
- * @brief Advance buffer index beyond a literal.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[in] literal The type of literal.
- * @param[in] length The length of the literal.
- *
- * @return true if the literal was present;
- * false otherwise.
- */
- static bool skipLiteral( const char * buf,
- size_t * start,
- size_t max,
- const char * literal,
- size_t length )
- {
- bool ret = false;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- coreJSON_ASSERT( literal != NULL );
- if( ( *start < max ) && ( length <= ( max - *start ) ) )
- {
- ret = strnEq( &buf[ *start ], literal, length );
- }
- if( ret == true )
- {
- *start += length;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a JSON literal.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid literal was present;
- * false otherwise.
- */
- static bool skipAnyLiteral( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- #define skipLit_( x ) \
- ( skipLiteral( buf, start, max, ( x ), ( sizeof( x ) - 1UL ) ) == true )
- if( skipLit_( "true" ) )
- {
- ret = true;
- }
- else if( skipLit_( "false" ) )
- {
- ret = true;
- }
- else if( skipLit_( "null" ) )
- {
- ret = true;
- }
- else
- {
- ret = false;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond one or more digits.
- * Optionally, output the integer value of the digits.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[out] outValue The integer value of the digits.
- *
- * @note outValue may be NULL. If not NULL, and the output
- * exceeds ~2 billion, then -1 is output.
- *
- * @return true if a digit was present;
- * false otherwise.
- */
- #define MAX_FACTOR ( MAX_INDEX_VALUE / 10 )
- static bool skipDigits( const char * buf,
- size_t * start,
- size_t max,
- int32_t * outValue )
- {
- bool ret = false;
- size_t i = 0U, saveStart = 0U;
- int32_t value = 0;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- saveStart = *start;
- for( i = *start; i < max; i++ )
- {
- if( !isdigit_( buf[ i ] ) )
- {
- break;
- }
- if( ( outValue != NULL ) && ( value > -1 ) )
- {
- int8_t n = ( int8_t ) hexToInt( buf[ i ] );
- if( value <= MAX_FACTOR )
- {
- value = ( value * 10 ) + n;
- }
- else
- {
- value = -1;
- }
- }
- }
- if( i > saveStart )
- {
- ret = true;
- *start = i;
- if( outValue != NULL )
- {
- *outValue = value;
- }
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond the decimal portion of a number.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- */
- static void skipDecimals( const char * buf,
- size_t * start,
- size_t max )
- {
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- if( ( i < max ) && ( buf[ i ] == '.' ) )
- {
- i++;
- if( skipDigits( buf, &i, max, NULL ) == true )
- {
- *start = i;
- }
- }
- }
- /**
- * @brief Advance buffer index beyond the exponent portion of a number.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- */
- static void skipExponent( const char * buf,
- size_t * start,
- size_t max )
- {
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- if( ( i < max ) && ( ( buf[ i ] == 'e' ) || ( buf[ i ] == 'E' ) ) )
- {
- i++;
- if( ( i < max ) && ( ( buf[ i ] == '-' ) || ( buf[ i ] == '+' ) ) )
- {
- i++;
- }
- if( skipDigits( buf, &i, max, NULL ) == true )
- {
- *start = i;
- }
- }
- }
- /**
- * @brief Advance buffer index beyond a number.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid number was present;
- * false otherwise.
- */
- static bool skipNumber( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- if( ( i < max ) && ( buf[ i ] == '-' ) )
- {
- i++;
- }
- if( i < max )
- {
- /* JSON disallows superfluous leading zeroes, so an
- * initial zero must either be alone, or followed by
- * a decimal or exponent.
- *
- * Should there be a digit after the zero, that digit
- * will not be skipped by this function, and later parsing
- * will judge this an illegal document. */
- if( buf[ i ] == '0' )
- {
- ret = true;
- i++;
- }
- else
- {
- ret = skipDigits( buf, &i, max, NULL );
- }
- }
- if( ret == true )
- {
- skipDecimals( buf, &i, max );
- skipExponent( buf, &i, max );
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a scalar value.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a scalar value was present;
- * false otherwise.
- */
- static bool skipAnyScalar( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- if( skipString( buf, start, max ) == true )
- {
- ret = true;
- }
- else if( skipAnyLiteral( buf, start, max ) == true )
- {
- ret = true;
- }
- else if( skipNumber( buf, start, max ) == true )
- {
- ret = true;
- }
- else
- {
- ret = false;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a comma separator
- * and surrounding whitespace.
- *
- * JSON uses a comma to separate values in an array and key-value
- * pairs in an object. JSON does not permit a trailing comma.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a non-terminal comma was present;
- * false otherwise.
- */
- static bool skipSpaceAndComma( const char * buf,
- size_t * start,
- size_t max )
- {
- bool ret = false;
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- skipSpace( buf, start, max );
- i = *start;
- if( ( i < max ) && ( buf[ i ] == ',' ) )
- {
- i++;
- skipSpace( buf, &i, max );
- if( ( i < max ) && !isCloseBracket_( buf[ i ] ) )
- {
- ret = true;
- *start = i;
- }
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond the scalar values of an array.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @note Stops advance if a value is an object or array.
- */
- static bool skipArrayScalars( const char * buf,
- size_t * start,
- size_t max )
- {
- size_t i = 0U;
- bool ret = true;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- while( i < max )
- {
- if( skipAnyScalar( buf, &i, max ) != true )
- {
- break;
- }
- if( skipSpaceAndComma( buf, &i, max ) != true )
- {
- /* After parsing a scalar, we must either have a comma (followed by more content)
- * or be at a closing bracket. If neither, the array is malformed. */
- if( ( i >= max ) || !isSquareClose_( buf[ i ] ) )
- {
- ret = false;
- }
- break;
- }
- }
- *start = i;
- return ret;
- }
- /**
- * @brief Advance buffer index beyond the scalar key-value pairs
- * of an object.
- *
- * In JSON, objects consist of comma-separated key-value pairs.
- * A key is always a string (a scalar) while a value may be a
- * scalar, an object, or an array. A colon must appear between
- * each key and value.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return true if a valid scalar key-value pairs were present;
- * false otherwise.
- *
- * @note Stops advance if a value is an object or array.
- */
- static bool skipObjectScalars( const char * buf,
- size_t * start,
- size_t max )
- {
- size_t i = 0U;
- bool comma = false;
- bool ret = true;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- while( i < max )
- {
- if( skipString( buf, &i, max ) != true )
- {
- ret = false;
- break;
- }
- skipSpace( buf, &i, max );
- if( ( i < max ) && ( buf[ i ] != ':' ) )
- {
- ret = false;
- break;
- }
- i++;
- skipSpace( buf, &i, max );
- if( ( i < max ) && isOpenBracket_( buf[ i ] ) )
- {
- *start = i;
- break;
- }
- if( skipAnyScalar( buf, &i, max ) != true )
- {
- ret = false;
- break;
- }
- comma = skipSpaceAndComma( buf, &i, max );
- *start = i;
- if( comma != true )
- {
- break;
- }
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond one or more scalars.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[in] mode The first character of an array '[' or object '{'.
- *
- * @return true if a valid scalers were present;
- * false otherwise.
- */
- static bool skipScalars( const char * buf,
- size_t * start,
- size_t max,
- char mode )
- {
- size_t i = 0U;
- bool modeIsOpenBracket = ( bool ) isOpenBracket_( mode );
- bool ret = true;
- /* assert function may be implemented in macro using a # or ## operator.
- * Using a local variable here to prevent macro replacement is subjected
- * to macro itself. */
- coreJSON_ASSERT( modeIsOpenBracket != false );
- /* Adding this line to avoid unused variable warning in release mode. */
- ( void ) modeIsOpenBracket;
- skipSpace( buf, start, max );
- i = *start;
- if( i < max )
- {
- if( mode == '[' )
- {
- if( !isSquareClose_( buf[ i ] ) )
- {
- ret = skipArrayScalars( buf, start, max );
- }
- }
- else
- {
- if( !isCurlyClose_( buf[ i ] ) )
- {
- ret = skipObjectScalars( buf, start, max );
- }
- }
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a collection and handle nesting.
- *
- * A stack is used to continue parsing the prior collection type
- * when a nested collection is finished.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- *
- * @return #JSONSuccess if the buffer contents are a valid JSON collection;
- * #JSONIllegalDocument if the buffer contents are NOT valid JSON;
- * #JSONMaxDepthExceeded if object and array nesting exceeds a threshold;
- * #JSONPartial if the buffer contents are potentially valid but incomplete.
- */
- #ifndef JSON_MAX_DEPTH
- #define JSON_MAX_DEPTH 32
- #endif
- static JSONStatus_t skipCollection( const char * buf,
- size_t * start,
- size_t max )
- {
- JSONStatus_t ret = JSONPartial;
- char c, stack[ JSON_MAX_DEPTH ];
- int16_t depth = -1;
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- i = *start;
- while( i < max )
- {
- c = buf[ i ];
- i++;
- switch( c )
- {
- case '{':
- case '[':
- depth++;
- if( depth >= JSON_MAX_DEPTH )
- {
- ret = JSONMaxDepthExceeded;
- break;
- }
- stack[ depth ] = c;
- if( skipScalars( buf, &i, max, stack[ depth ] ) != true )
- {
- ret = JSONIllegalDocument;
- }
- break;
- case '}':
- case ']':
- if( ( depth > 0 ) && ( depth < JSON_MAX_DEPTH ) &&
- isMatchingBracket_( stack[ depth ], c ) )
- {
- depth--;
- if( skipSpaceAndComma( buf, &i, max ) == true )
- {
- if( skipScalars( buf, &i, max, stack[ depth ] ) != true )
- {
- ret = JSONIllegalDocument;
- }
- }
- else
- {
- /* After closing a nested collection, if there is no comma found when calling
- * skipSpaceAndComma, then we must be at the end of the parent collection. */
- if( ( i < max ) && !isMatchingBracket_( stack[ depth ], buf[ i ] ) )
- {
- ret = JSONIllegalDocument;
- }
- }
- break;
- }
- ret = ( ( depth == 0 ) && isMatchingBracket_( stack[ depth ], c ) ) ?
- JSONSuccess : JSONIllegalDocument;
- break;
- default:
- ret = JSONIllegalDocument;
- break;
- }
- if( ret != JSONPartial )
- {
- break;
- }
- }
- if( ret == JSONSuccess )
- {
- *start = i;
- }
- return ret;
- }
- /** @endcond */
- /**
- * See core_json.h for docs.
- *
- * Verify that the entire buffer contains exactly one scalar
- * or collection within optional whitespace.
- */
- JSONStatus_t JSON_Validate( const char * buf,
- size_t max )
- {
- JSONStatus_t ret;
- size_t i = 0U;
- if( buf == NULL )
- {
- ret = JSONNullParameter;
- }
- else if( max == 0U )
- {
- ret = JSONBadParameter;
- }
- else
- {
- skipSpace( buf, &i, max );
- /** @cond DO_NOT_DOCUMENT */
- #ifndef JSON_VALIDATE_COLLECTIONS_ONLY
- if( skipAnyScalar( buf, &i, max ) == true )
- {
- ret = JSONSuccess;
- }
- else
- #endif
- /** @endcond */
- {
- ret = skipCollection( buf, &i, max );
- }
- }
- if( ( ret == JSONSuccess ) && ( i < max ) )
- {
- skipSpace( buf, &i, max );
- if( i != max )
- {
- ret = JSONIllegalDocument;
- }
- }
- return ret;
- }
- /** @cond DO_NOT_DOCUMENT */
- /**
- * @brief Output index and length for the next value.
- *
- * Also advances the buffer index beyond the value.
- * The value may be a scalar or a collection.
- * The start index should point to the beginning of the value.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[out] value A pointer to receive the index of the value.
- * @param[out] valueLength A pointer to receive the length of the value.
- *
- * @return true if a value was present;
- * false otherwise.
- */
- static bool nextValue( const char * buf,
- size_t * start,
- size_t max,
- size_t * value,
- size_t * valueLength )
- {
- bool ret = true;
- size_t i = 0U, valueStart = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- coreJSON_ASSERT( ( value != NULL ) && ( valueLength != NULL ) );
- i = *start;
- valueStart = i;
- if( skipAnyScalar( buf, &i, max ) == true )
- {
- *value = valueStart;
- *valueLength = i - valueStart;
- }
- else if( skipCollection( buf, &i, max ) == JSONSuccess )
- {
- *value = valueStart;
- *valueLength = i - valueStart;
- }
- else
- {
- ret = false;
- }
- if( ret == true )
- {
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Output indexes for the next key-value pair of an object.
- *
- * Also advances the buffer index beyond the key-value pair.
- * The value may be a scalar or a collection.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[out] key A pointer to receive the index of the key.
- * @param[out] keyLength A pointer to receive the length of the key.
- * @param[out] value A pointer to receive the index of the value.
- * @param[out] valueLength A pointer to receive the length of the value.
- *
- * @return true if a key-value pair was present;
- * false otherwise.
- */
- static bool nextKeyValuePair( const char * buf,
- size_t * start,
- size_t max,
- size_t * key,
- size_t * keyLength,
- size_t * value,
- size_t * valueLength )
- {
- bool ret = true;
- size_t i = 0U, keyStart = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
- coreJSON_ASSERT( ( key != NULL ) && ( keyLength != NULL ) );
- coreJSON_ASSERT( ( value != NULL ) && ( valueLength != NULL ) );
- i = *start;
- keyStart = i;
- if( skipString( buf, &i, max ) == true )
- {
- *key = keyStart + 1U;
- *keyLength = i - keyStart - 2U;
- }
- else
- {
- ret = false;
- }
- if( ret == true )
- {
- skipSpace( buf, &i, max );
- if( ( i < max ) && ( buf[ i ] == ':' ) )
- {
- i++;
- skipSpace( buf, &i, max );
- }
- else
- {
- ret = false;
- }
- }
- if( ret == true )
- {
- ret = nextValue( buf, &i, max, value, valueLength );
- }
- if( ret == true )
- {
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Find a key in a JSON object and output a pointer to its value.
- *
- * @param[in] buf The buffer to search.
- * @param[in] max size of the buffer.
- * @param[in] query The object keys and array indexes to search for.
- * @param[in] queryLength Length of the key.
- * @param[out] outValue A pointer to receive the index of the value found.
- * @param[out] outValueLength A pointer to receive the length of the value found.
- *
- * Iterate over the key-value pairs of an object, looking for a matching key.
- *
- * @return true if the query is matched and the value output;
- * false otherwise.
- *
- * @note Parsing stops upon finding a match.
- */
- static bool objectSearch( const char * buf,
- size_t max,
- const char * query,
- size_t queryLength,
- size_t * outValue,
- size_t * outValueLength )
- {
- bool ret = false;
- size_t i = 0U, key = 0U, keyLength = 0U, value = 0U, valueLength = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( query != NULL ) );
- coreJSON_ASSERT( ( outValue != NULL ) && ( outValueLength != NULL ) );
- skipSpace( buf, &i, max );
- if( ( i < max ) && ( buf[ i ] == '{' ) )
- {
- i++;
- skipSpace( buf, &i, max );
- while( i < max )
- {
- if( nextKeyValuePair( buf, &i, max, &key, &keyLength,
- &value, &valueLength ) != true )
- {
- break;
- }
- if( ( queryLength == keyLength ) &&
- ( strnEq( query, &buf[ key ], keyLength ) == true ) )
- {
- ret = true;
- break;
- }
- if( skipSpaceAndComma( buf, &i, max ) != true )
- {
- break;
- }
- }
- }
- if( ret == true )
- {
- *outValue = value;
- *outValueLength = valueLength;
- }
- return ret;
- }
- /**
- * @brief Find an index in a JSON array and output a pointer to its value.
- *
- * @param[in] buf The buffer to search.
- * @param[in] max size of the buffer.
- * @param[in] queryIndex The index to search for.
- * @param[out] outValue A pointer to receive the index of the value found.
- * @param[out] outValueLength A pointer to receive the length of the value found.
- *
- * Iterate over the values of an array, looking for a matching index.
- *
- * @return true if the queryIndex is found and the value output;
- * false otherwise.
- *
- * @note Parsing stops upon finding a match.
- */
- static bool arraySearch( const char * buf,
- size_t max,
- uint32_t queryIndex,
- size_t * outValue,
- size_t * outValueLength )
- {
- bool ret = false;
- size_t i = 0U, value = 0U, valueLength = 0U;
- uint32_t currentIndex = 0U;
- coreJSON_ASSERT( buf != NULL );
- coreJSON_ASSERT( ( outValue != NULL ) && ( outValueLength != NULL ) );
- skipSpace( buf, &i, max );
- if( ( i < max ) && ( buf[ i ] == '[' ) )
- {
- i++;
- skipSpace( buf, &i, max );
- while( i < max )
- {
- if( nextValue( buf, &i, max, &value, &valueLength ) != true )
- {
- break;
- }
- if( currentIndex == queryIndex )
- {
- ret = true;
- break;
- }
- if( ( skipSpaceAndComma( buf, &i, max ) != true ) ||
- ( currentIndex == UINT32_MAX ) )
- {
- break;
- }
- currentIndex++;
- }
- }
- if( ret == true )
- {
- *outValue = value;
- *outValueLength = valueLength;
- }
- return ret;
- }
- /**
- * @brief Advance buffer index beyond a query part.
- *
- * The part is the portion of the query which is not
- * a separator or array index.
- *
- * @param[in] buf The buffer to parse.
- * @param[in,out] start The index at which to begin.
- * @param[in] max The size of the buffer.
- * @param[out] outLength The length of the query part.
- *
- * @return true if a valid string was present;
- * false otherwise.
- */
- #ifndef JSON_QUERY_KEY_SEPARATOR
- #define JSON_QUERY_KEY_SEPARATOR '.'
- #endif
- #define isSeparator_( x ) ( ( x ) == JSON_QUERY_KEY_SEPARATOR )
- static bool skipQueryPart( const char * buf,
- size_t * start,
- size_t max,
- size_t * outLength )
- {
- bool ret = false;
- size_t i = 0U;
- coreJSON_ASSERT( ( buf != NULL ) && ( start != NULL ) && ( outLength != NULL ) );
- coreJSON_ASSERT( max > 0U );
- i = *start;
- while( ( i < max ) &&
- !isSeparator_( buf[ i ] ) &&
- !isSquareOpen_( buf[ i ] ) )
- {
- i++;
- }
- if( i > *start )
- {
- ret = true;
- *outLength = i - *start;
- *start = i;
- }
- return ret;
- }
- /**
- * @brief Handle a nested search by iterating over the parts of the query.
- *
- * @param[in] buf The buffer to search.
- * @param[in] max size of the buffer.
- * @param[in] query The object keys and array indexes to search for.
- * @param[in] queryLength Length of the key.
- * @param[out] outValue A pointer to receive the index of the value found.
- * @param[out] outValueLength A pointer to receive the length of the value found.
- *
- * @return #JSONSuccess if the query is matched and the value output;
- * #JSONBadParameter if the query is empty, or any part is empty,
- * or an index is too large to convert;
- * #JSONNotFound if the query is NOT found.
- *
- * @note Parsing stops upon finding a match.
- */
- static JSONStatus_t multiSearch( const char * buf,
- size_t max,
- const char * query,
- size_t queryLength,
- size_t * outValue,
- size_t * outValueLength )
- {
- JSONStatus_t ret = JSONSuccess;
- size_t i = 0U, start = 0U, queryStart = 0U, value = 0U, length = max;
- coreJSON_ASSERT( ( buf != NULL ) && ( query != NULL ) );
- coreJSON_ASSERT( ( outValue != NULL ) && ( outValueLength != NULL ) );
- coreJSON_ASSERT( ( max > 0U ) && ( queryLength > 0U ) );
- while( i < queryLength )
- {
- bool found = false;
- if( isSquareOpen_( query[ i ] ) )
- {
- int32_t queryIndex = -1;
- i++;
- ( void ) skipDigits( query, &i, queryLength, &queryIndex );
- if( ( queryIndex < 0 ) ||
- ( i >= queryLength ) || !isSquareClose_( query[ i ] ) )
- {
- ret = JSONBadParameter;
- break;
- }
- i++;
- found = arraySearch( &buf[ start ], length, ( uint32_t ) queryIndex, &value, &length );
- }
- else
- {
- size_t keyLength = 0;
- queryStart = i;
- if( ( skipQueryPart( query, &i, queryLength, &keyLength ) != true ) ||
- /* catch an empty key part or a trailing separator */
- ( i == ( queryLength - 1U ) ) )
- {
- ret = JSONBadParameter;
- break;
- }
- found = objectSearch( &buf[ start ], length, &query[ queryStart ], keyLength, &value, &length );
- }
- if( found == false )
- {
- ret = JSONNotFound;
- break;
- }
- start += value;
- if( ( i < queryLength ) && isSeparator_( query[ i ] ) )
- {
- i++;
- }
- }
- if( ret == JSONSuccess )
- {
- *outValue = start;
- *outValueLength = length;
- }
- return ret;
- }
- /**
- * @brief Return a JSON type based on a separator character or
- * the first character of a value.
- *
- * @param[in] c The character to classify.
- *
- * @return an enum of JSONTypes_t
- */
- static JSONTypes_t getType( char c )
- {
- JSONTypes_t t;
- switch( c )
- {
- case '"':
- t = JSONString;
- break;
- case '{':
- t = JSONObject;
- break;
- case '[':
- t = JSONArray;
- break;
- case 't':
- t = JSONTrue;
- break;
- case 'f':
- t = JSONFalse;
- break;
- case 'n':
- t = JSONNull;
- break;
- default:
- t = JSONNumber;
- break;
- }
- return t;
- }
- /** @endcond */
- /**
- * See core_json.h for docs.
- */
- JSONStatus_t JSON_SearchConst( const char * buf,
- size_t max,
- const char * query,
- size_t queryLength,
- const char ** outValue,
- size_t * outValueLength,
- JSONTypes_t * outType )
- {
- JSONStatus_t ret;
- size_t value = 0U;
- if( ( buf == NULL ) || ( query == NULL ) ||
- ( outValue == NULL ) || ( outValueLength == NULL ) )
- {
- ret = JSONNullParameter;
- }
- else if( ( max == 0U ) || ( queryLength == 0U ) )
- {
- ret = JSONBadParameter;
- }
- else
- {
- ret = multiSearch( buf, max, query, queryLength, &value, outValueLength );
- }
- if( ret == JSONSuccess )
- {
- JSONTypes_t t = getType( buf[ value ] );
- if( t == JSONString )
- {
- /* strip the surrounding quotes */
- value++;
- *outValueLength -= 2U;
- }
- *outValue = &buf[ value ];
- if( outType != NULL )
- {
- *outType = t;
- }
- }
- return ret;
- }
- /**
- * See core_json.h for docs.
- */
- JSONStatus_t JSON_SearchT( char * buf,
- size_t max,
- const char * query,
- size_t queryLength,
- char ** outValue,
- size_t * outValueLength,
- JSONTypes_t * outType )
- {
- /* MISRA Ref 11.3.1 [Pointer conversion] */
- /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-113 */
- /* coverity[misra_c_2012_rule_11_3_violation] */
- return JSON_SearchConst( ( const char * ) buf, max, query, queryLength, ( const char ** ) outValue, outValueLength, outType );
- }
- /** @cond DO_NOT_DOCUMENT */
- /**
- * @brief Output the next key-value pair or value from a collection.
- *
- * @param[in] buf The buffer to search.
- * @param[in] max size of the buffer.
- * @param[in] start The index at which the collection begins.
- * @param[in,out] next The index at which to seek the next value.
- * @param[out] outKey A pointer to receive the index of the value found.
- * @param[out] outKeyLength A pointer to receive the length of the value found.
- * @param[out] outValue A pointer to receive the index of the value found.
- * @param[out] outValueLength A pointer to receive the length of the value found.
- *
- * @return #JSONSuccess if a value is output;
- * #JSONIllegalDocument if the buffer does not begin with '[' or '{';
- * #JSONNotFound if there are no further values in the collection.
- */
- static JSONStatus_t iterate( const char * buf,
- size_t max,
- size_t * start,
- size_t * next,
- size_t * outKey,
- size_t * outKeyLength,
- size_t * outValue,
- size_t * outValueLength )
- {
- JSONStatus_t ret = JSONNotFound;
- bool found = false;
- coreJSON_ASSERT( ( buf != NULL ) && ( max > 0U ) );
- coreJSON_ASSERT( ( start != NULL ) && ( next != NULL ) );
- coreJSON_ASSERT( ( outKey != NULL ) && ( outKeyLength != NULL ) );
- coreJSON_ASSERT( ( outValue != NULL ) && ( outValueLength != NULL ) );
- if( *start < max )
- {
- switch( buf[ *start ] )
- {
- case '[':
- found = nextValue( buf, next, max, outValue, outValueLength );
- if( found == true )
- {
- *outKey = 0;
- *outKeyLength = 0;
- }
- break;
- case '{':
- found = nextKeyValuePair( buf, next, max, outKey, outKeyLength,
- outValue, outValueLength );
- break;
- default:
- ret = JSONIllegalDocument;
- break;
- }
- }
- if( found == true )
- {
- ret = JSONSuccess;
- ( void ) skipSpaceAndComma( buf, next, max );
- }
- return ret;
- }
- /** @endcond */
- /**
- * See core_json.h for docs.
- */
- JSONStatus_t JSON_Iterate( const char * buf,
- size_t max,
- size_t * start,
- size_t * next,
- JSONPair_t * outPair )
- {
- JSONStatus_t ret;
- size_t key = 0U, keyLength = 0U, value = 0U, valueLength = 0U;
- if( ( buf == NULL ) || ( start == NULL ) || ( next == NULL ) ||
- ( outPair == NULL ) )
- {
- ret = JSONNullParameter;
- }
- else if( ( max == 0U ) || ( *start >= max ) || ( *next > max ) )
- {
- ret = JSONBadParameter;
- }
- else
- {
- skipSpace( buf, start, max );
- if( *next <= *start )
- {
- *next = *start + 1U;
- skipSpace( buf, next, max );
- }
- ret = iterate( buf, max, start, next, &key, &keyLength,
- &value, &valueLength );
- }
- if( ret == JSONSuccess )
- {
- JSONTypes_t t = getType( buf[ value ] );
- if( t == JSONString )
- {
- /* strip the surrounding quotes */
- value++;
- valueLength -= 2U;
- }
- outPair->key = ( key == 0U ) ? NULL : &buf[ key ];
- outPair->keyLength = keyLength;
- outPair->value = &buf[ value ];
- outPair->valueLength = valueLength;
- outPair->jsonType = t;
- }
- return ret;
- }
|