Skip to content

Commit 54de51e

Browse files
committed
add minimal test case
1 parent e35b8e3 commit 54de51e

File tree

2 files changed

+178
-10
lines changed

2 files changed

+178
-10
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ the key to contain either whitespace or the equal sign =
2727
You can think of this as an implicit set of curly quotes { ... } that surround
2828
the contents of the file
2929

30+
Requires size_t, bool and memcmp to be declared prior to including this
31+
header. Headers are only included when compiled as the minimal test case.
32+
33+
To compile the minimal test case, use
34+
gcc -D JSON_TEST -x c --std=c99 json.h
35+
3036
Kudos to Niklas Gray for SJSON syntax,
3137
http://bitsquid.blogspot.se/2009/10/simplified-json-notation.html
3238

json.h

+172-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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:
@@ -7,10 +7,12 @@
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 =
3234
You can think of this as an implicit set of curly quotes { ... } that surround
3335
the 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+
3543
Kudos to Niklas Gray for SJSON syntax,
3644
http://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

155171
static void
156172
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) {
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

168184
static struct json_token_t*
169185
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) {
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-
682705
static bool
683706
json_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

Comments
 (0)