RaspberrPi project source code
Guo Wenxue
2023-09-08 47098ac0fb3a6bed9bd5c2acfc64d84387302cda
commit | author | age
d6b4a7 1 /*
G 2  * coreJSON v3.2.0
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 /**
26  * @file core_json.c
27  * @brief The source file that implements the user-facing functions in core_json.h.
28  */
29
30 #include <assert.h>
31 #include <limits.h>
32 #include <stddef.h>
33 #include <stdint.h>
34 #include "core_json.h"
35
36 /** @cond DO_NOT_DOCUMENT */
37
38 /* A compromise to satisfy both MISRA and CBMC */
39 typedef union
40 {
41     char c;
42     uint8_t u;
43 } char_;
44
45 #if ( CHAR_MIN == 0 )
46     #define isascii_( x )    ( ( x ) <= '\x7F' )
47 #else
48     #define isascii_( x )    ( ( x ) >= '\0' )
49 #endif
50 #define iscntrl_( x )        ( isascii_( x ) && ( ( x ) < ' ' ) )
51 #define isdigit_( x )        ( ( ( x ) >= '0' ) && ( ( x ) <= '9' ) )
52 /* NB. This is whitespace as defined by the JSON standard (ECMA-404). */
53 #define isspace_( x )                          \
54     ( ( ( x ) == ' ' ) || ( ( x ) == '\t' ) || \
55       ( ( x ) == '\n' ) || ( ( x ) == '\r' ) )
56
57 #define isOpenBracket_( x )           ( ( ( x ) == '{' ) || ( ( x ) == '[' ) )
58 #define isCloseBracket_( x )          ( ( ( x ) == '}' ) || ( ( x ) == ']' ) )
59 #define isCurlyPair_( x, y )          ( ( ( x ) == '{' ) && ( ( y ) == '}' ) )
60 #define isSquarePair_( x, y )         ( ( ( x ) == '[' ) && ( ( y ) == ']' ) )
61 #define isMatchingBracket_( x, y )    ( isCurlyPair_( x, y ) || isSquarePair_( x, y ) )
62 #define isSquareOpen_( x )            ( ( x ) == '[' )
63 #define isSquareClose_( x )           ( ( x ) == ']' )
64
65 /**
66  * @brief Advance buffer index beyond whitespace.
67  *
68  * @param[in] buf  The buffer to parse.
69  * @param[in,out] start  The index at which to begin.
70  * @param[in] max  The size of the buffer.
71  */
72 static void skipSpace( const char * buf,
73                        size_t * start,
74                        size_t max )
75 {
76     size_t i;
77
78     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
79
80     for( i = *start; i < max; i++ )
81     {
82         if( !isspace_( buf[ i ] ) )
83         {
84             break;
85         }
86     }
87
88     *start = i;
89 }
90
91 /**
92  * @brief Count the leading 1s in a byte.
93  *
94  * The high-order 1 bits of the first byte in a UTF-8 encoding
95  * indicate the number of additional bytes to follow.
96  *
97  * @return the count
98  */
99 static size_t countHighBits( uint8_t c )
100 {
101     uint8_t n = c;
102     size_t i = 0;
103
104     while( ( n & 0x80U ) != 0U )
105     {
106         i++;
107         n = ( n & 0x7FU ) << 1U;
108     }
109
110     return i;
111 }
112
113 /**
114  * @brief Is the value a legal Unicode code point and encoded with
115  * the fewest bytes?
116  *
117  * The last Unicode code point is 0x10FFFF.
118  *
119  * Unicode 3.1 disallows UTF-8 interpretation of non-shortest form sequences.
120  * 1 byte encodes 0 through 7 bits
121  * 2 bytes encode 8 through 5+6 = 11 bits
122  * 3 bytes encode 12 through 4+6+6 = 16 bits
123  * 4 bytes encode 17 through 3+6+6+6 = 21 bits
124  *
125  * Unicode 3.2 disallows UTF-8 code point values in the surrogate range,
126  * [U+D800 to U+DFFF].
127  *
128  * @note Disallow ASCII, as this is called only for multibyte sequences.
129  */
130 static bool shortestUTF8( size_t length,
131                           uint32_t value )
132 {
133     bool ret = false;
134     uint32_t min, max;
135
136     assert( ( length >= 2U ) && ( length <= 4U ) );
137
138     switch( length )
139     {
140         case 2:
141             min = ( uint32_t ) 1 << 7U;
142             max = ( ( uint32_t ) 1 << 11U ) - 1U;
143             break;
144
145         case 3:
146             min = ( uint32_t ) 1 << 11U;
147             max = ( ( uint32_t ) 1 << 16U ) - 1U;
148             break;
149
150         default:
151             min = ( uint32_t ) 1 << 16U;
152             max = 0x10FFFFU;
153             break;
154     }
155
156     if( ( value >= min ) && ( value <= max ) &&
157         ( ( value < 0xD800U ) || ( value > 0xDFFFU ) ) )
158     {
159         ret = true;
160     }
161
162     return ret;
163 }
164
165 /**
166  * @brief Advance buffer index beyond a UTF-8 code point.
167  *
168  * @param[in] buf  The buffer to parse.
169  * @param[in,out] start  The index at which to begin.
170  * @param[in] max  The size of the buffer.
171  *
172  * @return true if a valid code point was present;
173  * false otherwise.
174  *
175  * 00-7F    Single-byte character
176  * 80-BF    Trailing byte
177  * C0-DF    Leading byte of two-byte character
178  * E0-EF    Leading byte of three-byte character
179  * F0-F7    Leading byte of four-byte character
180  * F8-FB    Illegal (formerly leading byte of five-byte character)
181  * FC-FD    Illegal (formerly leading byte of six-byte character)
182  * FE-FF    Illegal
183  *
184  * The octet values C0, C1, and F5 to FF are illegal, since C0 and C1
185  * would introduce a non-shortest sequence, and F5 or above would
186  * introduce a value greater than the last code point, 0x10FFFF.
187  */
188 static bool skipUTF8MultiByte( const char * buf,
189                                size_t * start,
190                                size_t max )
191 {
192     bool ret = false;
193     size_t i, bitCount, j;
194     uint32_t value = 0;
195     char_ c;
196
197     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
198
199     i = *start;
200     assert( i < max );
201     assert( !isascii_( buf[ i ] ) );
202
203     c.c = buf[ i ];
204
205     if( ( c.u > 0xC1U ) && ( c.u < 0xF5U ) )
206     {
207         bitCount = countHighBits( c.u );
208         value = ( ( uint32_t ) c.u ) & ( ( ( uint32_t ) 1 << ( 7U - bitCount ) ) - 1U );
209
210         /* The bit count is 1 greater than the number of bytes,
211          * e.g., when j is 2, we skip one more byte. */
212         for( j = bitCount - 1U; j > 0U; j-- )
213         {
214             i++;
215
216             if( i >= max )
217             {
218                 break;
219             }
220
221             c.c = buf[ i ];
222
223             /* Additional bytes must match 10xxxxxx. */
224             if( ( c.u & 0xC0U ) != 0x80U )
225             {
226                 break;
227             }
228
229             value = ( value << 6U ) | ( c.u & 0x3FU );
230         }
231
232         if( ( j == 0U ) && ( shortestUTF8( bitCount, value ) == true ) )
233         {
234             *start = i + 1U;
235             ret = true;
236         }
237     }
238
239     return ret;
240 }
241
242 /**
243  * @brief Advance buffer index beyond an ASCII or UTF-8 code point.
244  *
245  * @param[in] buf  The buffer to parse.
246  * @param[in,out] start  The index at which to begin.
247  * @param[in] max  The size of the buffer.
248  *
249  * @return true if a valid code point was present;
250  * false otherwise.
251  */
252 static bool skipUTF8( const char * buf,
253                       size_t * start,
254                       size_t max )
255 {
256     bool ret = false;
257
258     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
259
260     if( *start < max )
261     {
262         if( isascii_( buf[ *start ] ) )
263         {
264             *start += 1U;
265             ret = true;
266         }
267         else
268         {
269             ret = skipUTF8MultiByte( buf, start, max );
270         }
271     }
272
273     return ret;
274 }
275
276 /**
277  * @brief Convert a hexadecimal character to an integer.
278  *
279  * @param[in] c  The character to convert.
280  *
281  * @return the integer value upon success or NOT_A_HEX_CHAR on failure.
282  */
283 #define NOT_A_HEX_CHAR    ( 0x10U )
284 static uint8_t hexToInt( char c )
285 {
286     char_ n;
287
288     n.c = c;
289
290     if( ( c >= 'a' ) && ( c <= 'f' ) )
291     {
292         n.c -= 'a';
293         n.u += 10U;
294     }
295     else if( ( c >= 'A' ) && ( c <= 'F' ) )
296     {
297         n.c -= 'A';
298         n.u += 10U;
299     }
300     else if( isdigit_( c ) )
301     {
302         n.c -= '0';
303     }
304     else
305     {
306         n.u = NOT_A_HEX_CHAR;
307     }
308
309     return n.u;
310 }
311
312 /**
313  * @brief Advance buffer index beyond a single \u Unicode
314  * escape sequence and output the value.
315  *
316  * @param[in] buf  The buffer to parse.
317  * @param[in,out] start  The index at which to begin.
318  * @param[in] max  The size of the buffer.
319  * @param[out] outValue  The value of the hex digits.
320  *
321  * @return true if a valid escape sequence was present;
322  * false otherwise.
323  *
324  * @note For the sake of security, \u0000 is disallowed.
325  */
326 static bool skipOneHexEscape( const char * buf,
327                               size_t * start,
328                               size_t max,
329                               uint16_t * outValue )
330 {
331     bool ret = false;
332     size_t i, end;
333     uint16_t value = 0;
334
335     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
336     assert( outValue != NULL );
337
338     i = *start;
339 #define HEX_ESCAPE_LENGTH    ( 6U )   /* e.g., \u1234 */
340     /* MISRA Ref 14.3.1 [Configuration dependent invariant] */
341     /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-143 */
342     /* coverity[misra_c_2012_rule_14_3_violation] */
343     end = ( i <= ( SIZE_MAX - HEX_ESCAPE_LENGTH ) ) ? ( i + HEX_ESCAPE_LENGTH ) : SIZE_MAX;
344
345     if( ( end < max ) && ( buf[ i ] == '\\' ) && ( buf[ i + 1U ] == 'u' ) )
346     {
347         for( i += 2U; i < end; i++ )
348         {
349             uint8_t n = hexToInt( buf[ i ] );
350
351             if( n == NOT_A_HEX_CHAR )
352             {
353                 break;
354             }
355
356             value = ( value << 4U ) | n;
357         }
358     }
359
360     if( ( i == end ) && ( value > 0U ) )
361     {
362         ret = true;
363         *outValue = value;
364         *start = i;
365     }
366
367     return ret;
368 }
369
370 /**
371  * @brief Advance buffer index beyond one or a pair of \u Unicode escape sequences.
372  *
373  * @param[in] buf  The buffer to parse.
374  * @param[in,out] start  The index at which to begin.
375  * @param[in] max  The size of the buffer.
376  *
377  * Surrogate pairs are two escape sequences that together denote
378  * a code point outside the Basic Multilingual Plane.  They must
379  * occur as a pair with the first "high" value in [U+D800, U+DBFF],
380  * and the second "low" value in [U+DC00, U+DFFF].
381  *
382  * @return true if a valid escape sequence was present;
383  * false otherwise.
384  *
385  * @note For the sake of security, \u0000 is disallowed.
386  */
387 #define isHighSurrogate( x )    ( ( ( x ) >= 0xD800U ) && ( ( x ) <= 0xDBFFU ) )
388 #define isLowSurrogate( x )     ( ( ( x ) >= 0xDC00U ) && ( ( x ) <= 0xDFFFU ) )
389
390 static bool skipHexEscape( const char * buf,
391                            size_t * start,
392                            size_t max )
393 {
394     bool ret = false;
395     size_t i;
396     uint16_t value;
397
398     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
399
400     i = *start;
401
402     if( skipOneHexEscape( buf, &i, max, &value ) == true )
403     {
404         if( isHighSurrogate( value ) )
405         {
406             if( ( skipOneHexEscape( buf, &i, max, &value ) == true ) &&
407                 ( isLowSurrogate( value ) ) )
408             {
409                 ret = true;
410             }
411         }
412         else if( isLowSurrogate( value ) )
413         {
414             /* premature low surrogate */
415         }
416         else
417         {
418             ret = true;
419         }
420     }
421
422     if( ret == true )
423     {
424         *start = i;
425     }
426
427     return ret;
428 }
429
430 /**
431  * @brief Advance buffer index beyond an escape sequence.
432  *
433  * @param[in] buf  The buffer to parse.
434  * @param[in,out] start  The index at which to begin.
435  * @param[in] max  The size of the buffer.
436  *
437  * @return true if a valid escape sequence was present;
438  * false otherwise.
439  *
440  * @note For the sake of security, \NUL is disallowed.
441  */
442 static bool skipEscape( const char * buf,
443                         size_t * start,
444                         size_t max )
445 {
446     bool ret = false;
447     size_t i;
448
449     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
450
451     i = *start;
452
453     if( ( i < ( max - 1U ) ) && ( buf[ i ] == '\\' ) )
454     {
455         char c = buf[ i + 1U ];
456
457         switch( c )
458         {
459             case '\0':
460                 break;
461
462             case 'u':
463                 ret = skipHexEscape( buf, &i, max );
464                 break;
465
466             case '"':
467             case '\\':
468             case '/':
469             case 'b':
470             case 'f':
471             case 'n':
472             case 'r':
473             case 't':
474                 i += 2U;
475                 ret = true;
476                 break;
477
478             default:
479
480                 /* a control character: (NUL,SPACE) */
481                 if( iscntrl_( c ) )
482                 {
483                     i += 2U;
484                     ret = true;
485                 }
486
487                 break;
488         }
489     }
490
491     if( ret == true )
492     {
493         *start = i;
494     }
495
496     return ret;
497 }
498
499 /**
500  * @brief Advance buffer index beyond a double-quoted string.
501  *
502  * @param[in] buf  The buffer to parse.
503  * @param[in,out] start  The index at which to begin.
504  * @param[in] max  The size of the buffer.
505  *
506  * @return true if a valid string was present;
507  * false otherwise.
508  */
509 static bool skipString( const char * buf,
510                         size_t * start,
511                         size_t max )
512 {
513     bool ret = false;
514     size_t i;
515
516     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
517
518     i = *start;
519
520     if( ( i < max ) && ( buf[ i ] == '"' ) )
521     {
522         i++;
523
524         while( i < max )
525         {
526             if( buf[ i ] == '"' )
527             {
528                 ret = true;
529                 i++;
530                 break;
531             }
532
533             if( buf[ i ] == '\\' )
534             {
535                 if( skipEscape( buf, &i, max ) != true )
536                 {
537                     break;
538                 }
539             }
540             /* An unescaped control character is not allowed. */
541             else if( iscntrl_( buf[ i ] ) )
542             {
543                 break;
544             }
545             else if( skipUTF8( buf, &i, max ) != true )
546             {
547                 break;
548             }
549             else
550             {
551                 /* MISRA 15.7 */
552             }
553         }
554     }
555
556     if( ret == true )
557     {
558         *start = i;
559     }
560
561     return ret;
562 }
563
564 /**
565  * @brief Compare the leading n bytes of two character sequences.
566  *
567  * @param[in] a  first character sequence
568  * @param[in] b  second character sequence
569  * @param[in] n  number of bytes
570  *
571  * @return true if the sequences are the same;
572  * false otherwise
573  */
574 static bool strnEq( const char * a,
575                     const char * b,
576                     size_t n )
577 {
578     size_t i;
579
580     assert( ( a != NULL ) && ( b != NULL ) );
581
582     for( i = 0; i < n; i++ )
583     {
584         if( a[ i ] != b[ i ] )
585         {
586             break;
587         }
588     }
589
590     return ( i == n ) ? true : false;
591 }
592
593 /**
594  * @brief Advance buffer index beyond a literal.
595  *
596  * @param[in] buf  The buffer to parse.
597  * @param[in,out] start  The index at which to begin.
598  * @param[in] max  The size of the buffer.
599  * @param[in] literal  The type of literal.
600  * @param[in] length  The length of the literal.
601  *
602  * @return true if the literal was present;
603  * false otherwise.
604  */
605 static bool skipLiteral( const char * buf,
606                          size_t * start,
607                          size_t max,
608                          const char * literal,
609                          size_t length )
610 {
611     bool ret = false;
612
613     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
614     assert( literal != NULL );
615
616     if( ( *start < max ) && ( length <= ( max - *start ) ) )
617     {
618         ret = strnEq( &buf[ *start ], literal, length );
619     }
620
621     if( ret == true )
622     {
623         *start += length;
624     }
625
626     return ret;
627 }
628
629 /**
630  * @brief Advance buffer index beyond a JSON literal.
631  *
632  * @param[in] buf  The buffer to parse.
633  * @param[in,out] start  The index at which to begin.
634  * @param[in] max  The size of the buffer.
635  *
636  * @return true if a valid literal was present;
637  * false otherwise.
638  */
639 static bool skipAnyLiteral( const char * buf,
640                             size_t * start,
641                             size_t max )
642 {
643     bool ret = false;
644
645 #define skipLit_( x ) \
646     ( skipLiteral( buf, start, max, ( x ), ( sizeof( x ) - 1UL ) ) == true )
647
648     if( skipLit_( "true" ) || skipLit_( "false" ) || skipLit_( "null" ) )
649     {
650         ret = true;
651     }
652
653     return ret;
654 }
655
656 /**
657  * @brief Advance buffer index beyond one or more digits.
658  * Optionally, output the integer value of the digits.
659  *
660  * @param[in] buf  The buffer to parse.
661  * @param[in,out] start  The index at which to begin.
662  * @param[in] max  The size of the buffer.
663  * @param[out] outValue  The integer value of the digits.
664  *
665  * @note outValue may be NULL.  If not NULL, and the output
666  * exceeds ~2 billion, then -1 is output.
667  *
668  * @return true if a digit was present;
669  * false otherwise.
670  */
671 #define MAX_FACTOR    ( MAX_INDEX_VALUE / 10 )
672 static bool skipDigits( const char * buf,
673                         size_t * start,
674                         size_t max,
675                         int32_t * outValue )
676 {
677     bool ret = false;
678     size_t i, saveStart;
679     int32_t value = 0;
680
681     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
682
683     saveStart = *start;
684
685     for( i = *start; i < max; i++ )
686     {
687         if( !isdigit_( buf[ i ] ) )
688         {
689             break;
690         }
691
692         if( ( outValue != NULL ) && ( value > -1 ) )
693         {
694             int8_t n = ( int8_t ) hexToInt( buf[ i ] );
695
696             if( value <= MAX_FACTOR )
697             {
698                 value = ( value * 10 ) + n;
699             }
700             else
701             {
702                 value = -1;
703             }
704         }
705     }
706
707     if( i > saveStart )
708     {
709         ret = true;
710         *start = i;
711
712         if( outValue != NULL )
713         {
714             *outValue = value;
715         }
716     }
717
718     return ret;
719 }
720
721 /**
722  * @brief Advance buffer index beyond the decimal portion of a number.
723  *
724  * @param[in] buf  The buffer to parse.
725  * @param[in,out] start  The index at which to begin.
726  * @param[in] max  The size of the buffer.
727  */
728 static void skipDecimals( const char * buf,
729                           size_t * start,
730                           size_t max )
731 {
732     size_t i;
733
734     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
735
736     i = *start;
737
738     if( ( i < max ) && ( buf[ i ] == '.' ) )
739     {
740         i++;
741
742         if( skipDigits( buf, &i, max, NULL ) == true )
743         {
744             *start = i;
745         }
746     }
747 }
748
749 /**
750  * @brief Advance buffer index beyond the exponent portion of a number.
751  *
752  * @param[in] buf  The buffer to parse.
753  * @param[in,out] start  The index at which to begin.
754  * @param[in] max  The size of the buffer.
755  */
756 static void skipExponent( const char * buf,
757                           size_t * start,
758                           size_t max )
759 {
760     size_t i;
761
762     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
763
764     i = *start;
765
766     if( ( i < max ) && ( ( buf[ i ] == 'e' ) || ( buf[ i ] == 'E' ) ) )
767     {
768         i++;
769
770         if( ( i < max ) && ( ( buf[ i ] == '-' ) || ( buf[ i ] == '+' ) ) )
771         {
772             i++;
773         }
774
775         if( skipDigits( buf, &i, max, NULL ) == true )
776         {
777             *start = i;
778         }
779     }
780 }
781
782 /**
783  * @brief Advance buffer index beyond a number.
784  *
785  * @param[in] buf  The buffer to parse.
786  * @param[in,out] start  The index at which to begin.
787  * @param[in] max  The size of the buffer.
788  *
789  * @return true if a valid number was present;
790  * false otherwise.
791  */
792 static bool skipNumber( const char * buf,
793                         size_t * start,
794                         size_t max )
795 {
796     bool ret = false;
797     size_t i;
798
799     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
800
801     i = *start;
802
803     if( ( i < max ) && ( buf[ i ] == '-' ) )
804     {
805         i++;
806     }
807
808     if( i < max )
809     {
810         /* JSON disallows superfluous leading zeroes, so an
811          * initial zero must either be alone, or followed by
812          * a decimal or exponent.
813          *
814          * Should there be a digit after the zero, that digit
815          * will not be skipped by this function, and later parsing
816          * will judge this an illegal document. */
817         if( buf[ i ] == '0' )
818         {
819             ret = true;
820             i++;
821         }
822         else
823         {
824             ret = skipDigits( buf, &i, max, NULL );
825         }
826     }
827
828     if( ret == true )
829     {
830         skipDecimals( buf, &i, max );
831         skipExponent( buf, &i, max );
832         *start = i;
833     }
834
835     return ret;
836 }
837
838 /**
839  * @brief Advance buffer index beyond a scalar value.
840  *
841  * @param[in] buf  The buffer to parse.
842  * @param[in,out] start  The index at which to begin.
843  * @param[in] max  The size of the buffer.
844  *
845  * @return true if a scalar value was present;
846  * false otherwise.
847  */
848 static bool skipAnyScalar( const char * buf,
849                            size_t * start,
850                            size_t max )
851 {
852     bool ret = false;
853
854     if( ( skipString( buf, start, max ) == true ) ||
855         ( skipAnyLiteral( buf, start, max ) == true ) ||
856         ( skipNumber( buf, start, max ) == true ) )
857     {
858         ret = true;
859     }
860
861     return ret;
862 }
863
864 /**
865  * @brief Advance buffer index beyond a comma separator
866  * and surrounding whitespace.
867  *
868  * JSON uses a comma to separate values in an array and key-value
869  * pairs in an object.  JSON does not permit a trailing comma.
870  *
871  * @param[in] buf  The buffer to parse.
872  * @param[in,out] start  The index at which to begin.
873  * @param[in] max  The size of the buffer.
874  *
875  * @return true if a non-terminal comma was present;
876  * false otherwise.
877  */
878 static bool skipSpaceAndComma( const char * buf,
879                                size_t * start,
880                                size_t max )
881 {
882     bool ret = false;
883     size_t i;
884
885     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
886
887     skipSpace( buf, start, max );
888     i = *start;
889
890     if( ( i < max ) && ( buf[ i ] == ',' ) )
891     {
892         i++;
893         skipSpace( buf, &i, max );
894
895         if( ( i < max ) && !isCloseBracket_( buf[ i ] ) )
896         {
897             ret = true;
898             *start = i;
899         }
900     }
901
902     return ret;
903 }
904
905 /**
906  * @brief Advance buffer index beyond the scalar values of an array.
907  *
908  * @param[in] buf  The buffer to parse.
909  * @param[in,out] start  The index at which to begin.
910  * @param[in] max  The size of the buffer.
911  *
912  * @note Stops advance if a value is an object or array.
913  */
914 static void skipArrayScalars( const char * buf,
915                               size_t * start,
916                               size_t max )
917 {
918     size_t i;
919
920     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
921
922     i = *start;
923
924     while( i < max )
925     {
926         if( skipAnyScalar( buf, &i, max ) != true )
927         {
928             break;
929         }
930
931         if( skipSpaceAndComma( buf, &i, max ) != true )
932         {
933             break;
934         }
935     }
936
937     *start = i;
938 }
939
940 /**
941  * @brief Advance buffer index beyond the scalar key-value pairs
942  * of an object.
943  *
944  * In JSON, objects consist of comma-separated key-value pairs.
945  * A key is always a string (a scalar) while a value may be a
946  * scalar, an object, or an array.  A colon must appear between
947  * each key and value.
948  *
949  * @param[in] buf  The buffer to parse.
950  * @param[in,out] start  The index at which to begin.
951  * @param[in] max  The size of the buffer.
952  *
953  * @note Stops advance if a value is an object or array.
954  */
955 static void skipObjectScalars( const char * buf,
956                                size_t * start,
957                                size_t max )
958 {
959     size_t i;
960     bool comma;
961
962     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
963
964     i = *start;
965
966     while( i < max )
967     {
968         if( skipString( buf, &i, max ) != true )
969         {
970             break;
971         }
972
973         skipSpace( buf, &i, max );
974
975         if( ( i < max ) && ( buf[ i ] != ':' ) )
976         {
977             break;
978         }
979
980         i++;
981         skipSpace( buf, &i, max );
982
983         if( ( i < max ) && isOpenBracket_( buf[ i ] ) )
984         {
985             *start = i;
986             break;
987         }
988
989         if( skipAnyScalar( buf, &i, max ) != true )
990         {
991             break;
992         }
993
994         comma = skipSpaceAndComma( buf, &i, max );
995         *start = i;
996
997         if( comma != true )
998         {
999             break;
1000         }
1001     }
1002 }
1003
1004 /**
1005  * @brief Advance buffer index beyond one or more scalars.
1006  *
1007  * @param[in] buf  The buffer to parse.
1008  * @param[in,out] start  The index at which to begin.
1009  * @param[in] max  The size of the buffer.
1010  * @param[in] mode  The first character of an array '[' or object '{'.
1011  */
1012 static void skipScalars( const char * buf,
1013                          size_t * start,
1014                          size_t max,
1015                          char mode )
1016 {
1017     assert( isOpenBracket_( mode ) );
1018
1019     skipSpace( buf, start, max );
1020
1021     if( mode == '[' )
1022     {
1023         skipArrayScalars( buf, start, max );
1024     }
1025     else
1026     {
1027         skipObjectScalars( buf, start, max );
1028     }
1029 }
1030
1031 /**
1032  * @brief Advance buffer index beyond a collection and handle nesting.
1033  *
1034  * A stack is used to continue parsing the prior collection type
1035  * when a nested collection is finished.
1036  *
1037  * @param[in] buf  The buffer to parse.
1038  * @param[in,out] start  The index at which to begin.
1039  * @param[in] max  The size of the buffer.
1040  *
1041  * @return #JSONSuccess if the buffer contents are a valid JSON collection;
1042  * #JSONIllegalDocument if the buffer contents are NOT valid JSON;
1043  * #JSONMaxDepthExceeded if object and array nesting exceeds a threshold;
1044  * #JSONPartial if the buffer contents are potentially valid but incomplete.
1045  */
1046 #ifndef JSON_MAX_DEPTH
1047     #define JSON_MAX_DEPTH    32
1048 #endif
1049 static JSONStatus_t skipCollection( const char * buf,
1050                                     size_t * start,
1051                                     size_t max )
1052 {
1053     JSONStatus_t ret = JSONPartial;
1054     char c, stack[ JSON_MAX_DEPTH ];
1055     int16_t depth = -1;
1056     size_t i;
1057
1058     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
1059
1060     i = *start;
1061
1062     while( i < max )
1063     {
1064         c = buf[ i ];
1065         i++;
1066
1067         switch( c )
1068         {
1069             case '{':
1070             case '[':
1071                 depth++;
1072
1073                 if( depth == JSON_MAX_DEPTH )
1074                 {
1075                     ret = JSONMaxDepthExceeded;
1076                     break;
1077                 }
1078
1079                 stack[ depth ] = c;
1080                 skipScalars( buf, &i, max, stack[ depth ] );
1081                 break;
1082
1083             case '}':
1084             case ']':
1085
1086                 if( ( depth > 0 ) && isMatchingBracket_( stack[ depth ], c ) )
1087                 {
1088                     depth--;
1089
1090                     if( skipSpaceAndComma( buf, &i, max ) == true )
1091                     {
1092                         skipScalars( buf, &i, max, stack[ depth ] );
1093                     }
1094
1095                     break;
1096                 }
1097
1098                 ret = ( ( depth == 0 ) && isMatchingBracket_( stack[ depth ], c ) ) ?
1099                       JSONSuccess : JSONIllegalDocument;
1100                 break;
1101
1102             default:
1103                 ret = JSONIllegalDocument;
1104                 break;
1105         }
1106
1107         if( ret != JSONPartial )
1108         {
1109             break;
1110         }
1111     }
1112
1113     if( ret == JSONSuccess )
1114     {
1115         *start = i;
1116     }
1117
1118     return ret;
1119 }
1120
1121 /** @endcond */
1122
1123 /**
1124  * See core_json.h for docs.
1125  *
1126  * Verify that the entire buffer contains exactly one scalar
1127  * or collection within optional whitespace.
1128  */
1129 JSONStatus_t JSON_Validate( const char * buf,
1130                             size_t max )
1131 {
1132     JSONStatus_t ret;
1133     size_t i = 0;
1134
1135     if( buf == NULL )
1136     {
1137         ret = JSONNullParameter;
1138     }
1139     else if( max == 0U )
1140     {
1141         ret = JSONBadParameter;
1142     }
1143     else
1144     {
1145         skipSpace( buf, &i, max );
1146
1147         /** @cond DO_NOT_DOCUMENT */
1148         #ifndef JSON_VALIDATE_COLLECTIONS_ONLY
1149             if( skipAnyScalar( buf, &i, max ) == true )
1150             {
1151                 ret = JSONSuccess;
1152             }
1153             else
1154         #endif
1155         /** @endcond */
1156         {
1157             ret = skipCollection( buf, &i, max );
1158         }
1159     }
1160
1161     if( ( ret == JSONSuccess ) && ( i < max ) )
1162     {
1163         skipSpace( buf, &i, max );
1164
1165         if( i != max )
1166         {
1167             ret = JSONIllegalDocument;
1168         }
1169     }
1170
1171     return ret;
1172 }
1173
1174 /** @cond DO_NOT_DOCUMENT */
1175
1176 /**
1177  * @brief Output index and length for the next value.
1178  *
1179  * Also advances the buffer index beyond the value.
1180  * The value may be a scalar or a collection.
1181  * The start index should point to the beginning of the value.
1182  *
1183  * @param[in] buf  The buffer to parse.
1184  * @param[in,out] start  The index at which to begin.
1185  * @param[in] max  The size of the buffer.
1186  * @param[out] value  A pointer to receive the index of the value.
1187  * @param[out] valueLength  A pointer to receive the length of the value.
1188  *
1189  * @return true if a value was present;
1190  * false otherwise.
1191  */
1192 static bool nextValue( const char * buf,
1193                        size_t * start,
1194                        size_t max,
1195                        size_t * value,
1196                        size_t * valueLength )
1197 {
1198     bool ret = true;
1199     size_t i, valueStart;
1200
1201     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
1202     assert( ( value != NULL ) && ( valueLength != NULL ) );
1203
1204     i = *start;
1205     valueStart = i;
1206
1207     if( ( skipAnyScalar( buf, &i, max ) == true ) ||
1208         ( skipCollection( buf, &i, max ) == JSONSuccess ) )
1209     {
1210         *value = valueStart;
1211         *valueLength = i - valueStart;
1212     }
1213     else
1214     {
1215         ret = false;
1216     }
1217
1218     if( ret == true )
1219     {
1220         *start = i;
1221     }
1222
1223     return ret;
1224 }
1225
1226 /**
1227  * @brief Output indexes for the next key-value pair of an object.
1228  *
1229  * Also advances the buffer index beyond the key-value pair.
1230  * The value may be a scalar or a collection.
1231  *
1232  * @param[in] buf  The buffer to parse.
1233  * @param[in,out] start  The index at which to begin.
1234  * @param[in] max  The size of the buffer.
1235  * @param[out] key  A pointer to receive the index of the key.
1236  * @param[out] keyLength  A pointer to receive the length of the key.
1237  * @param[out] value  A pointer to receive the index of the value.
1238  * @param[out] valueLength  A pointer to receive the length of the value.
1239  *
1240  * @return true if a key-value pair was present;
1241  * false otherwise.
1242  */
1243 static bool nextKeyValuePair( const char * buf,
1244                               size_t * start,
1245                               size_t max,
1246                               size_t * key,
1247                               size_t * keyLength,
1248                               size_t * value,
1249                               size_t * valueLength )
1250 {
1251     bool ret = true;
1252     size_t i, keyStart;
1253
1254     assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
1255     assert( ( key != NULL ) && ( keyLength != NULL ) );
1256     assert( ( value != NULL ) && ( valueLength != NULL ) );
1257
1258     i = *start;
1259     keyStart = i;
1260
1261     if( skipString( buf, &i, max ) == true )
1262     {
1263         *key = keyStart + 1U;
1264         *keyLength = i - keyStart - 2U;
1265     }
1266     else
1267     {
1268         ret = false;
1269     }
1270
1271     if( ret == true )
1272     {
1273         skipSpace( buf, &i, max );
1274
1275         if( ( i < max ) && ( buf[ i ] == ':' ) )
1276         {
1277             i++;
1278             skipSpace( buf, &i, max );
1279         }
1280         else
1281         {
1282             ret = false;
1283         }
1284     }
1285
1286     if( ret == true )
1287     {
1288         ret = nextValue( buf, &i, max, value, valueLength );
1289     }
1290
1291     if( ret == true )
1292     {
1293         *start = i;
1294     }
1295
1296     return ret;
1297 }
1298
1299 /**
1300  * @brief Find a key in a JSON object and output a pointer to its value.
1301  *
1302  * @param[in] buf  The buffer to search.
1303  * @param[in] max  size of the buffer.
1304  * @param[in] query  The object keys and array indexes to search for.
1305  * @param[in] queryLength  Length of the key.
1306  * @param[out] outValue  A pointer to receive the index of the value found.
1307  * @param[out] outValueLength  A pointer to receive the length of the value found.
1308  *
1309  * Iterate over the key-value pairs of an object, looking for a matching key.
1310  *
1311  * @return true if the query is matched and the value output;
1312  * false otherwise.
1313  *
1314  * @note Parsing stops upon finding a match.
1315  */
1316 static bool objectSearch( const char * buf,
1317                           size_t max,
1318                           const char * query,
1319                           size_t queryLength,
1320                           size_t * outValue,
1321                           size_t * outValueLength )
1322 {
1323     bool ret = false;
1324
1325     size_t i = 0, key, keyLength, value = 0, valueLength = 0;
1326
1327     assert( ( buf != NULL ) && ( query != NULL ) );
1328     assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
1329
1330     skipSpace( buf, &i, max );
1331
1332     if( ( i < max ) && ( buf[ i ] == '{' ) )
1333     {
1334         i++;
1335         skipSpace( buf, &i, max );
1336
1337         while( i < max )
1338         {
1339             if( nextKeyValuePair( buf, &i, max, &key, &keyLength,
1340                                   &value, &valueLength ) != true )
1341             {
1342                 break;
1343             }
1344
1345             if( ( queryLength == keyLength ) &&
1346                 ( strnEq( query, &buf[ key ], keyLength ) == true ) )
1347             {
1348                 ret = true;
1349                 break;
1350             }
1351
1352             if( skipSpaceAndComma( buf, &i, max ) != true )
1353             {
1354                 break;
1355             }
1356         }
1357     }
1358
1359     if( ret == true )
1360     {
1361         *outValue = value;
1362         *outValueLength = valueLength;
1363     }
1364
1365     return ret;
1366 }
1367
1368 /**
1369  * @brief Find an index in a JSON array and output a pointer to its value.
1370  *
1371  * @param[in] buf  The buffer to search.
1372  * @param[in] max  size of the buffer.
1373  * @param[in] queryIndex  The index to search for.
1374  * @param[out] outValue  A pointer to receive the index of the value found.
1375  * @param[out] outValueLength  A pointer to receive the length of the value found.
1376  *
1377  * Iterate over the values of an array, looking for a matching index.
1378  *
1379  * @return true if the queryIndex is found and the value output;
1380  * false otherwise.
1381  *
1382  * @note Parsing stops upon finding a match.
1383  */
1384 static bool arraySearch( const char * buf,
1385                          size_t max,
1386                          uint32_t queryIndex,
1387                          size_t * outValue,
1388                          size_t * outValueLength )
1389 {
1390     bool ret = false;
1391     size_t i = 0, value = 0, valueLength = 0;
1392     uint32_t currentIndex = 0;
1393
1394     assert( buf != NULL );
1395     assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
1396
1397     skipSpace( buf, &i, max );
1398
1399     if( ( i < max ) && ( buf[ i ] == '[' ) )
1400     {
1401         i++;
1402         skipSpace( buf, &i, max );
1403
1404         while( i < max )
1405         {
1406             if( nextValue( buf, &i, max, &value, &valueLength ) != true )
1407             {
1408                 break;
1409             }
1410
1411             if( currentIndex == queryIndex )
1412             {
1413                 ret = true;
1414                 break;
1415             }
1416
1417             if( skipSpaceAndComma( buf, &i, max ) != true )
1418             {
1419                 break;
1420             }
1421
1422             currentIndex++;
1423         }
1424     }
1425
1426     if( ret == true )
1427     {
1428         *outValue = value;
1429         *outValueLength = valueLength;
1430     }
1431
1432     return ret;
1433 }
1434
1435 /**
1436  * @brief Advance buffer index beyond a query part.
1437  *
1438  * The part is the portion of the query which is not
1439  * a separator or array index.
1440  *
1441  * @param[in] buf  The buffer to parse.
1442  * @param[in,out] start  The index at which to begin.
1443  * @param[in] max  The size of the buffer.
1444  * @param[out] outLength  The length of the query part.
1445  *
1446  * @return true if a valid string was present;
1447  * false otherwise.
1448  */
1449 #ifndef JSON_QUERY_KEY_SEPARATOR
1450     #define JSON_QUERY_KEY_SEPARATOR    '.'
1451 #endif
1452 #define isSeparator_( x )    ( ( x ) == JSON_QUERY_KEY_SEPARATOR )
1453 static bool skipQueryPart( const char * buf,
1454                            size_t * start,
1455                            size_t max,
1456                            size_t * outLength )
1457 {
1458     bool ret = false;
1459     size_t i;
1460
1461     assert( ( buf != NULL ) && ( start != NULL ) && ( outLength != NULL ) );
1462     assert( max > 0U );
1463
1464     i = *start;
1465
1466     while( ( i < max ) &&
1467            !isSeparator_( buf[ i ] ) &&
1468            !isSquareOpen_( buf[ i ] ) )
1469     {
1470         i++;
1471     }
1472
1473     if( i > *start )
1474     {
1475         ret = true;
1476         *outLength = i - *start;
1477         *start = i;
1478     }
1479
1480     return ret;
1481 }
1482
1483 /**
1484  * @brief Handle a nested search by iterating over the parts of the query.
1485  *
1486  * @param[in] buf  The buffer to search.
1487  * @param[in] max  size of the buffer.
1488  * @param[in] query  The object keys and array indexes to search for.
1489  * @param[in] queryLength  Length of the key.
1490  * @param[out] outValue  A pointer to receive the index of the value found.
1491  * @param[out] outValueLength  A pointer to receive the length of the value found.
1492  *
1493  * @return #JSONSuccess if the query is matched and the value output;
1494  * #JSONBadParameter if the query is empty, or any part is empty,
1495  * or an index is too large to convert;
1496  * #JSONNotFound if the query is NOT found.
1497  *
1498  * @note Parsing stops upon finding a match.
1499  */
1500 static JSONStatus_t multiSearch( const char * buf,
1501                                  size_t max,
1502                                  const char * query,
1503                                  size_t queryLength,
1504                                  size_t * outValue,
1505                                  size_t * outValueLength )
1506 {
1507     JSONStatus_t ret = JSONSuccess;
1508     size_t i = 0, start = 0, queryStart = 0, value = 0, length = max;
1509
1510     assert( ( buf != NULL ) && ( query != NULL ) );
1511     assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
1512     assert( ( max > 0U ) && ( queryLength > 0U ) );
1513
1514     while( i < queryLength )
1515     {
1516         bool found = false;
1517
1518         if( isSquareOpen_( query[ i ] ) )
1519         {
1520             int32_t queryIndex = -1;
1521             i++;
1522
1523             ( void ) skipDigits( query, &i, queryLength, &queryIndex );
1524
1525             if( ( queryIndex < 0 ) ||
1526                 ( i >= queryLength ) || !isSquareClose_( query[ i ] ) )
1527             {
1528                 ret = JSONBadParameter;
1529                 break;
1530             }
1531
1532             i++;
1533
1534             found = arraySearch( &buf[ start ], length, ( uint32_t ) queryIndex, &value, &length );
1535         }
1536         else
1537         {
1538             size_t keyLength = 0;
1539
1540             queryStart = i;
1541
1542             if( ( skipQueryPart( query, &i, queryLength, &keyLength ) != true ) ||
1543                 /* catch an empty key part or a trailing separator */
1544                 ( i == ( queryLength - 1U ) ) )
1545             {
1546                 ret = JSONBadParameter;
1547                 break;
1548             }
1549
1550             found = objectSearch( &buf[ start ], length, &query[ queryStart ], keyLength, &value, &length );
1551         }
1552
1553         if( found == false )
1554         {
1555             ret = JSONNotFound;
1556             break;
1557         }
1558
1559         start += value;
1560
1561         if( ( i < queryLength ) && isSeparator_( query[ i ] ) )
1562         {
1563             i++;
1564         }
1565     }
1566
1567     if( ret == JSONSuccess )
1568     {
1569         *outValue = start;
1570         *outValueLength = length;
1571     }
1572
1573     return ret;
1574 }
1575
1576 /**
1577  * @brief Return a JSON type based on a separator character or
1578  * the first character of a value.
1579  *
1580  * @param[in] c  The character to classify.
1581  *
1582  * @return an enum of JSONTypes_t
1583  */
1584 static JSONTypes_t getType( char c )
1585 {
1586     JSONTypes_t t;
1587
1588     switch( c )
1589     {
1590         case '"':
1591             t = JSONString;
1592             break;
1593
1594         case '{':
1595             t = JSONObject;
1596             break;
1597
1598         case '[':
1599             t = JSONArray;
1600             break;
1601
1602         case 't':
1603             t = JSONTrue;
1604             break;
1605
1606         case 'f':
1607             t = JSONFalse;
1608             break;
1609
1610         case 'n':
1611             t = JSONNull;
1612             break;
1613
1614         default:
1615             t = JSONNumber;
1616             break;
1617     }
1618
1619     return t;
1620 }
1621
1622 /** @endcond */
1623
1624 /**
1625  * See core_json.h for docs.
1626  */
1627 JSONStatus_t JSON_SearchConst( const char * buf,
1628                                size_t max,
1629                                const char * query,
1630                                size_t queryLength,
1631                                const char ** outValue,
1632                                size_t * outValueLength,
1633                                JSONTypes_t * outType )
1634 {
1635     JSONStatus_t ret;
1636     size_t value = 0U;
1637
1638     if( ( buf == NULL ) || ( query == NULL ) ||
1639         ( outValue == NULL ) || ( outValueLength == NULL ) )
1640     {
1641         ret = JSONNullParameter;
1642     }
1643     else if( ( max == 0U ) || ( queryLength == 0U ) )
1644     {
1645         ret = JSONBadParameter;
1646     }
1647     else
1648     {
1649         ret = multiSearch( buf, max, query, queryLength, &value, outValueLength );
1650     }
1651
1652     if( ret == JSONSuccess )
1653     {
1654         JSONTypes_t t = getType( buf[ value ] );
1655
1656         if( t == JSONString )
1657         {
1658             /* strip the surrounding quotes */
1659             value++;
1660             *outValueLength -= 2U;
1661         }
1662
1663         *outValue = &buf[ value ];
1664
1665         if( outType != NULL )
1666         {
1667             *outType = t;
1668         }
1669     }
1670
1671     return ret;
1672 }
1673
1674 /**
1675  * See core_json.h for docs.
1676  */
1677 JSONStatus_t JSON_SearchT( char * buf,
1678                            size_t max,
1679                            const char * query,
1680                            size_t queryLength,
1681                            char ** outValue,
1682                            size_t * outValueLength,
1683                            JSONTypes_t * outType )
1684 {
1685     /* MISRA Ref 11.3.1 [Misaligned access] */
1686     /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-113 */
1687     /* coverity[misra_c_2012_rule_11_3_violation] */
1688     return JSON_SearchConst( ( const char * ) buf, max, query, queryLength,
1689                              ( const char ** ) outValue, outValueLength, outType );
1690 }
1691
1692 /** @cond DO_NOT_DOCUMENT */
1693
1694 /**
1695  * @brief Output the next key-value pair or value from a collection.
1696  *
1697  * @param[in] buf  The buffer to search.
1698  * @param[in] max  size of the buffer.
1699  * @param[in] start  The index at which the collection begins.
1700  * @param[in,out] next  The index at which to seek the next value.
1701  * @param[out] outKey  A pointer to receive the index of the value found.
1702  * @param[out] outKeyLength  A pointer to receive the length of the value found.
1703  * @param[out] outValue  A pointer to receive the index of the value found.
1704  * @param[out] outValueLength  A pointer to receive the length of the value found.
1705  *
1706  * @return #JSONSuccess if a value is output;
1707  * #JSONIllegalDocument if the buffer does not begin with '[' or '{';
1708  * #JSONNotFound if there are no further values in the collection.
1709  */
1710 static JSONStatus_t iterate( const char * buf,
1711                              size_t max,
1712                              size_t * start,
1713                              size_t * next,
1714                              size_t * outKey,
1715                              size_t * outKeyLength,
1716                              size_t * outValue,
1717                              size_t * outValueLength )
1718 {
1719     JSONStatus_t ret = JSONNotFound;
1720     bool found = false;
1721
1722     assert( ( buf != NULL ) && ( max > 0U ) );
1723     assert( ( start != NULL ) && ( next != NULL ) );
1724     assert( ( outKey != NULL ) && ( outKeyLength != NULL ) );
1725     assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
1726
1727     if( *start < max )
1728     {
1729         switch( buf[ *start ] )
1730         {
1731             case '[':
1732                 found = nextValue( buf, next, max, outValue, outValueLength );
1733
1734                 if( found == true )
1735                 {
1736                     *outKey = 0;
1737                     *outKeyLength = 0;
1738                 }
1739
1740                 break;
1741
1742             case '{':
1743                 found = nextKeyValuePair( buf, next, max, outKey, outKeyLength,
1744                                           outValue, outValueLength );
1745                 break;
1746
1747             default:
1748                 ret = JSONIllegalDocument;
1749                 break;
1750         }
1751     }
1752
1753     if( found == true )
1754     {
1755         ret = JSONSuccess;
1756         ( void ) skipSpaceAndComma( buf, next, max );
1757     }
1758
1759     return ret;
1760 }
1761
1762 /** @endcond */
1763
1764 /**
1765  * See core_json.h for docs.
1766  */
1767 JSONStatus_t JSON_Iterate( const char * buf,
1768                            size_t max,
1769                            size_t * start,
1770                            size_t * next,
1771                            JSONPair_t * outPair )
1772 {
1773     JSONStatus_t ret;
1774     size_t key, keyLength, value, valueLength;
1775
1776     if( ( buf == NULL ) || ( start == NULL ) || ( next == NULL ) ||
1777         ( outPair == NULL ) )
1778     {
1779         ret = JSONNullParameter;
1780     }
1781     else if( ( max == 0U ) || ( *start >= max ) || ( *next > max ) )
1782     {
1783         ret = JSONBadParameter;
1784     }
1785     else
1786     {
1787         skipSpace( buf, start, max );
1788
1789         if( *next <= *start )
1790         {
1791             *next = *start + 1U;
1792             skipSpace( buf, next, max );
1793         }
1794
1795         ret = iterate( buf, max, start, next, &key, &keyLength,
1796                        &value, &valueLength );
1797     }
1798
1799     if( ret == JSONSuccess )
1800     {
1801         JSONTypes_t t = getType( buf[ value ] );
1802
1803         if( t == JSONString )
1804         {
1805             /* strip the surrounding quotes */
1806             value++;
1807             valueLength -= 2U;
1808         }
1809
1810         outPair->key = ( key == 0U ) ? NULL : &buf[ key ];
1811         outPair->keyLength = keyLength;
1812         outPair->value = &buf[ value ];
1813         outPair->valueLength = valueLength;
1814         outPair->jsonType = t;
1815     }
1816
1817     return ret;
1818 }