1
- /* json.h - JSON/SJSON parser - Public Domain - 2016 Mattias Jansson / Rampant Pixels
1
+ /* json.h - JSON/SJSON parser - Public Domain
2
2
*
3
3
* This library provides a in-place JSON/SJSON parser in C99.
4
4
* The latest source code is always available at:
7
7
*
8
8
* This library is put in the public domain; you can redistribute
9
9
* 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
10
14
*/
11
15
12
- #pragma once
13
-
14
16
/*! \file json.h
15
17
\brief JSON/SJSON parser
16
18
@@ -32,10 +34,24 @@ the key to contain either whitespace or the equal sign =
32
34
You can think of this as an implicit set of curly quotes { ... } that surround
33
35
the contents of the file
34
36
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
+
35
43
Kudos to Niklas Gray for SJSON syntax,
36
44
http://bitsquid.blogspot.se/2009/10/simplified-json-notation.html
37
45
*/
38
46
47
+ #ifdef JSON_TEST
48
+ # include <stdbool.h>
49
+ # include <stdint.h>
50
+ # include <string.h>
51
+ #else
52
+ # pragma once
53
+ #endif
54
+
39
55
// Types
40
56
41
57
/*! 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
154
170
155
171
static void
156
172
json_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 ) {
158
174
struct json_token_t * token = json_get_token (tokens , capacity , current );
159
175
if (token ) {
160
176
token -> type = type ;
@@ -167,7 +183,7 @@ json_set_token_primitive(struct json_token_t* tokens, json_size_t capacity, json
167
183
168
184
static struct json_token_t *
169
185
json_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 ) {
171
187
struct json_token_t * token = json_get_token (tokens , capacity , current );
172
188
if (token ) {
173
189
token -> type = type ;
@@ -455,9 +471,10 @@ json_parse_value(const char* buffer, json_size_t length, json_size_t pos,
455
471
456
472
case 't' :
457
473
case 'f' :
474
+ case 'n' :
458
475
if ((c == 't' ) && (length - pos >= 4 ) &&
459
476
(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 ])) {
461
478
json_set_token_primitive (tokens , capacity , * current , JSON_PRIMITIVE , pos - 1 , 4 );
462
479
++ (* current );
463
480
return pos + 3 ;
@@ -469,11 +486,17 @@ json_parse_value(const char* buffer, json_size_t length, json_size_t pos,
469
486
++ (* current );
470
487
return pos + 4 ;
471
488
}
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
+ }
472
496
if (!simple )
473
497
return JSON_INVALID_POS ;
474
498
//Fall through to string handling
475
499
476
- case 'n' :
477
500
case '"' :
478
501
default :
479
502
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,
525
548
json_size_t pos = json_skip_whitespace (buffer , size , 0 );
526
549
if ((pos < size ) && (buffer [pos ] != '{' )) {
527
550
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 );
529
552
++ current ;
530
553
if (json_parse_object (buffer , size , pos , tokens , capacity , & current , true) == JSON_INVALID_POS )
531
554
return 0 ;
555
+ if (capacity )
556
+ tokens [0 ].value_length = size - tokens [0 ].value ;
532
557
return current ;
533
558
}
534
559
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_
677
702
return outlength ;
678
703
}
679
704
680
- #include <string.h>
681
-
682
705
static bool
683
706
json_string_equal (const char * rhs , size_t rhs_length , const char * lhs , size_t lhs_length ) {
684
707
if (rhs_length && (lhs_length == rhs_length )) {
685
708
return (memcmp (rhs , lhs , rhs_length ) == 0 );
686
709
}
687
710
return (!rhs_length && !lhs_length );
688
711
}
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