1- /* json.h - JSON/SJSON parser - Public Domain - 2016 Mattias Jansson / Rampant Pixels
1+ /* json.h - JSON/SJSON parser - Public Domain
22 *
33 * This library provides a in-place JSON/SJSON parser in C99.
44 * The latest source code is always available at:
77 *
88 * This library is put in the public domain; you can redistribute
99 * it and/or modify it without any restrictions.
10+ *
11+ * Author: Mattias Jansson / Rampant Pixels (2016-2018)
12+ *
13+ * Please consider our Patreon - https://patreon.com/rampantpixels
1014 */
1115
12- #pragma once
13-
1416/*! \file json.h
1517\brief JSON/SJSON parser
1618
@@ -32,10 +34,24 @@ the key to contain either whitespace or the equal sign =
3234You can think of this as an implicit set of curly quotes { ... } that surround
3335the contents of the file
3436
37+ Requires size_t, bool and memcmp to be declared prior to including this
38+ header. Headers are only included when compiled as the minimal test case.
39+
40+ To compile the minimal test case, use
41+ gcc -D JSON_TEST -x c --std=c99 json.h
42+
3543Kudos to Niklas Gray for SJSON syntax,
3644http://bitsquid.blogspot.se/2009/10/simplified-json-notation.html
3745*/
3846
47+ #ifdef JSON_TEST
48+ # include <stdbool.h>
49+ # include <stdint.h>
50+ # include <string.h>
51+ #else
52+ # pragma once
53+ #endif
54+
3955// Types
4056
4157/*! Base size type. Change this to reduce token storage footprint. */
@@ -154,7 +170,7 @@ json_is_valid_token(struct json_token_t* tokens, json_size_t capacity, json_size
154170
155171static void
156172json_set_token_primitive (struct json_token_t * tokens , json_size_t capacity , json_size_t current ,
157- json_type_t type , json_size_t value , json_size_t value_length ) {
173+ enum json_type_t type , json_size_t value , json_size_t value_length ) {
158174 struct json_token_t * token = json_get_token (tokens , capacity , current );
159175 if (token ) {
160176 token -> type = type ;
@@ -167,7 +183,7 @@ json_set_token_primitive(struct json_token_t* tokens, json_size_t capacity, json
167183
168184static struct json_token_t *
169185json_set_token_complex (struct json_token_t * tokens , json_size_t capacity , json_size_t current ,
170- json_type_t type , json_size_t pos ) {
186+ enum json_type_t type , json_size_t pos ) {
171187 struct json_token_t * token = json_get_token (tokens , capacity , current );
172188 if (token ) {
173189 token -> type = type ;
@@ -455,9 +471,10 @@ json_parse_value(const char* buffer, json_size_t length, json_size_t pos,
455471
456472 case 't' :
457473 case 'f' :
474+ case 'n' :
458475 if ((c == 't' ) && (length - pos >= 4 ) &&
459476 (buffer [pos ] == 'r' ) && (buffer [pos + 1 ] == 'u' ) && (buffer [pos + 2 ] == 'e' ) &&
460- json_is_token_delimiter (buffer [pos + 3 ])) {
477+ json_is_token_delimiter (buffer [pos + 3 ])) {
461478 json_set_token_primitive (tokens , capacity , * current , JSON_PRIMITIVE , pos - 1 , 4 );
462479 ++ (* current );
463480 return pos + 3 ;
@@ -469,11 +486,17 @@ json_parse_value(const char* buffer, json_size_t length, json_size_t pos,
469486 ++ (* current );
470487 return pos + 4 ;
471488 }
489+ if ((c == 'n' ) && (length - pos >= 4 ) &&
490+ (buffer [pos ] == 'u' ) && (buffer [pos + 1 ] == 'l' ) && (buffer [pos + 2 ] == 'l' ) &&
491+ json_is_token_delimiter (buffer [pos + 3 ])) {
492+ json_set_token_primitive (tokens , capacity , * current , JSON_PRIMITIVE , pos - 1 , 4 );
493+ ++ (* current );
494+ return pos + 3 ;
495+ }
472496 if (!simple )
473497 return JSON_INVALID_POS ;
474498 //Fall through to string handling
475499
476- case 'n' :
477500 case '"' :
478501 default :
479502 if (!simple && (c == 'n' ) && (length - pos >= 4 ) &&
@@ -525,10 +548,12 @@ sjson_parse(const char* buffer, json_size_t size, struct json_token_t* tokens,
525548 json_size_t pos = json_skip_whitespace (buffer , size , 0 );
526549 if ((pos < size ) && (buffer [pos ] != '{' )) {
527550 json_set_token_id (tokens , capacity , current , 0 , 0 );
528- json_set_token_complex (tokens , capacity , current , JSON_OBJECT , 0 );
551+ json_set_token_complex (tokens , capacity , current , JSON_OBJECT , pos );
529552 ++ current ;
530553 if (json_parse_object (buffer , size , pos , tokens , capacity , & current , true) == JSON_INVALID_POS )
531554 return 0 ;
555+ if (capacity )
556+ tokens [0 ].value_length = size - tokens [0 ].value ;
532557 return current ;
533558 }
534559 if (json_parse_value (buffer , size , pos , tokens , capacity , & current , true) == JSON_INVALID_POS )
@@ -677,12 +702,149 @@ json_unescape(char* buffer, json_size_t capacity, const char* string, json_size_
677702 return outlength ;
678703}
679704
680- #include <string.h>
681-
682705static bool
683706json_string_equal (const char * rhs , size_t rhs_length , const char * lhs , size_t lhs_length ) {
684707 if (rhs_length && (lhs_length == rhs_length )) {
685708 return (memcmp (rhs , lhs , rhs_length ) == 0 );
686709 }
687710 return (!rhs_length && !lhs_length );
688711}
712+
713+
714+ #ifdef JSON_TEST
715+
716+ #include <stdio.h>
717+
718+ #define VERIFY_TOKEN (idx , type_ , id_ , id_len_ , val_ , val_len_ ) \
719+ if ((tokens[idx].type != (type_)) || (tokens[idx].id != (id_)) || (tokens[idx].id_length != (id_len_)) || \
720+ (tokens[idx].value != (val_)) || (tokens[idx].value_length != (val_len_))) { \
721+ printf("Invalid token %d (%d, %d, %d, %d, %d)\n", idx, tokens[idx].type, tokens[idx].id, \
722+ tokens[idx].id_length, tokens[idx].value, tokens[idx].value_length); \
723+ return -1; \
724+ } else do {} while(0)
725+
726+ #define VERIFY_TOKEN_ID (idx , type_ , idstr , val_ , val_len_ ) \
727+ if ((tokens[idx].type != (type_)) || \
728+ !json_string_equal(input.str + tokens[idx].id, tokens[idx].id_length, JSON_STRING_CONST(idstr)) || \
729+ (tokens[idx].value != (val_)) || (tokens[idx].value_length != (val_len_))) { \
730+ printf("Invalid token %d (%d, %d, %d, %d, %d)\n", idx, tokens[idx].type, tokens[idx].id, \
731+ tokens[idx].id_length, tokens[idx].value, tokens[idx].value_length); \
732+ return -1; \
733+ } else do {} while(0)
734+
735+ #define VERIFY_TOKEN_ID_VALUE (idx , type_ , idstr , valuestr ) \
736+ if ((tokens[idx].type != (type_)) || \
737+ !json_string_equal(input.str + tokens[idx].id, tokens[idx].id_length, JSON_STRING_CONST(idstr)) || \
738+ !json_string_equal(input.str + tokens[idx].value, tokens[idx].value_length, JSON_STRING_CONST(valuestr))) { \
739+ printf("Invalid token %d (%d, %d, %d, %d, %d)\n", idx, tokens[idx].type, tokens[idx].id, \
740+ tokens[idx].id_length, tokens[idx].value, tokens[idx].value_length); \
741+ return -1; \
742+ } else do {} while(0)
743+
744+ int
745+ main (int argc , char * * argv ) {
746+ struct json_token_t tokens [32 ];
747+ memset (tokens , 0 , sizeof (tokens ));
748+
749+ struct json_input {
750+ const char * str ;
751+ size_t len ;
752+ };
753+ {
754+ struct json_input input = {JSON_STRING_CONST ("\
755+ {\"foo\" :{\"subobj\": false ,\
756+ \"val\" :1.2345e45 \
757+ } ,\"arr\" :[ \
758+ \"string\",\
759+ -.34523e-78,[\
760+ true, \
761+ \"subarr [] {} =:\", { \"key\": []}, [] \
762+ ],[false],\
763+ { \t\
764+ \"final\" : null \
765+ }\
766+ ,{ } , \
767+ 1234.43E+123 \
768+ ]\
769+ }" )};
770+
771+ json_size_t num_tokens =
772+ json_parse (input .str , input .len , tokens , sizeof (tokens ) / sizeof (tokens [0 ]));
773+
774+ if (num_tokens != 19 ) {
775+ printf ("Invalid number of tokens: %d\n" , (int )num_tokens );
776+ return -1 ;
777+ }
778+
779+ VERIFY_TOKEN (0 , JSON_OBJECT , 0 , 0 , 1 , input .len - 1 );
780+ VERIFY_TOKEN_ID (1 , JSON_OBJECT , "foo" , 9 , 39 );
781+ VERIFY_TOKEN_ID_VALUE (2 , JSON_PRIMITIVE , "subobj" , "false" );
782+ VERIFY_TOKEN_ID_VALUE (3 , JSON_PRIMITIVE , "val" , "1.2345e45" );
783+ VERIFY_TOKEN_ID (4 , JSON_ARRAY , "arr" , 0 , 7 );
784+ VERIFY_TOKEN_ID_VALUE (5 , JSON_STRING , "" , "string" );
785+ VERIFY_TOKEN_ID_VALUE (6 , JSON_PRIMITIVE , "" , "-.34523e-78" );
786+ VERIFY_TOKEN (7 , JSON_ARRAY , 0 , 0 , 0 , 4 );
787+ VERIFY_TOKEN_ID_VALUE (8 , JSON_PRIMITIVE , "" , "true" );
788+ VERIFY_TOKEN_ID_VALUE (9 , JSON_STRING , "" , "subarr [] {} =:" );
789+ VERIFY_TOKEN (10 , JSON_OBJECT , 0 , 0 , 116 , 12 );
790+ VERIFY_TOKEN_ID_VALUE (11 , JSON_ARRAY , "key" , "" );
791+ VERIFY_TOKEN (12 , JSON_ARRAY , 0 , 0 , 0 , 0 );
792+ VERIFY_TOKEN (13 , JSON_ARRAY , 0 , 0 , 0 , 1 );
793+ VERIFY_TOKEN_ID_VALUE (14 , JSON_PRIMITIVE , "" , "false" );
794+ VERIFY_TOKEN (15 , JSON_OBJECT , 0 , 0 , 147 , 24 );
795+ VERIFY_TOKEN_ID_VALUE (16 , JSON_PRIMITIVE , "final" , "null" );
796+ VERIFY_TOKEN (17 , JSON_OBJECT , 0 , 0 , 174 , 3 );
797+ VERIFY_TOKEN_ID_VALUE (18 , JSON_PRIMITIVE , "" , "1234.43E+123" );
798+ }
799+ {
800+ struct json_input input = {JSON_STRING_CONST ("\
801+ foo ={subobj= false \
802+ val =1.2345e45 \
803+ } arr =[\
804+ string\
805+ -.34523e-78 [\
806+ true\
807+ \"subarr [] {} =:\" { key: []} []\
808+ ] [false] \
809+ { \t\
810+ final = null\
811+ }\
812+ { } \
813+ 1234.43E+123 \
814+ ]\
815+ " )};
816+
817+ json_size_t num_tokens =
818+ sjson_parse (input .str , input .len , tokens , sizeof (tokens ) / sizeof (tokens [0 ]));
819+
820+ if (num_tokens != 19 ) {
821+ printf ("Invalid number of tokens: %d\n" , (int )num_tokens );
822+ return -1 ;
823+ }
824+
825+ VERIFY_TOKEN (0 , JSON_OBJECT , 0 , 0 , 1 , input .len - 1 );
826+ VERIFY_TOKEN_ID (1 , JSON_OBJECT , "foo" , 6 , 34 );
827+ VERIFY_TOKEN_ID_VALUE (2 , JSON_PRIMITIVE , "subobj" , "false" );
828+ VERIFY_TOKEN_ID_VALUE (3 , JSON_PRIMITIVE , "val" , "1.2345e45" );
829+ VERIFY_TOKEN_ID (4 , JSON_ARRAY , "arr" , 0 , 7 );
830+ VERIFY_TOKEN_ID_VALUE (5 , JSON_STRING , "" , "string" );
831+ VERIFY_TOKEN_ID_VALUE (6 , JSON_PRIMITIVE , "" , "-.34523e-78" );
832+ VERIFY_TOKEN (7 , JSON_ARRAY , 0 , 0 , 0 , 4 );
833+ VERIFY_TOKEN_ID_VALUE (8 , JSON_PRIMITIVE , "" , "true" );
834+ VERIFY_TOKEN_ID_VALUE (9 , JSON_STRING , "" , "subarr [] {} =:" );
835+ VERIFY_TOKEN (10 , JSON_OBJECT , 0 , 0 , 98 , 10 );
836+ VERIFY_TOKEN_ID_VALUE (11 , JSON_ARRAY , "key" , "" );
837+ VERIFY_TOKEN (12 , JSON_ARRAY , 0 , 0 , 0 , 0 );
838+ VERIFY_TOKEN (13 , JSON_ARRAY , 0 , 0 , 0 , 1 );
839+ VERIFY_TOKEN_ID_VALUE (14 , JSON_PRIMITIVE , "" , "false" );
840+ VERIFY_TOKEN (15 , JSON_OBJECT , 0 , 0 , 125 , 21 );
841+ VERIFY_TOKEN_ID_VALUE (16 , JSON_PRIMITIVE , "final" , "null" );
842+ VERIFY_TOKEN (17 , JSON_OBJECT , 0 , 0 , 148 , 3 );
843+ VERIFY_TOKEN_ID_VALUE (18 , JSON_PRIMITIVE , "" , "1234.43E+123" );
844+ }
845+ printf ("Minimal tests passed\n" );
846+ return 0 ;
847+ }
848+
849+ #endif
850+
0 commit comments