sJson (also known as Secure JSON) is a thread-safe, dynamic-allocation-free, cross-compilable JSON parser based on a PAL (Platform Abstraction Layer) architecture writed in c99
| 1 | #define JSON_IMPLEMENTATION |
| 2 | #include "json_pal.h" |
| 3 | #include <stdio.h> |
| 4 | #include <string.h> |
| 5 | |
| 6 | #define T(name) printf("[TEST] %-52s", name) |
| 7 | #define PASS() printf("PASS\n") |
| 8 | #define FAIL(m) do { printf("FAIL: %s\n", m); return 1; } while(0) |
| 9 | |
| 10 | static int test_primitives(void) { |
| 11 | JsonArena* a = json_arena_create(NULL, 4096); |
| 12 | JsonError err; JsonValue* v; |
| 13 | bool b; int64_t i; double f; const char* s; uint32_t sl; |
| 14 | |
| 15 | T("null"); v=json_parse_cstr(a,"null",&err); if(!v||v->type!=JSON_NULL) FAIL("null"); PASS(); |
| 16 | T("true/false"); v=json_parse_cstr(a,"true",&err); json_get_bool(v,&b); if(!b) FAIL("true"); |
| 17 | v=json_parse_cstr(a,"false",&err); json_get_bool(v,&b); if(b) FAIL("false"); PASS(); |
| 18 | T("integer"); v=json_parse_cstr(a,"-9223372036854775807",&err); |
| 19 | json_get_int(v,&i); if(i!=-9223372036854775807LL) FAIL("int"); PASS(); |
| 20 | T("float"); v=json_parse_cstr(a,"3.14159265358979",&err); |
| 21 | json_get_float(v,&f); if(f<3.14||f>3.15) FAIL("float"); PASS(); |
| 22 | T("string escapes");v=json_parse_cstr(a,"\"hello\\nworld\\t!\"",&err); |
| 23 | json_get_string(v,&s,&sl); |
| 24 | if(sl!=13||memcmp(s,"hello\nworld\t!",13)) FAIL("escape content"); PASS(); |
| 25 | |
| 26 | /* Bug #2 regression: \u00e9 must be 2 bytes (C3 A9), not 3 */ |
| 27 | T("\\u00e9 exact 2 bytes"); |
| 28 | v=json_parse_cstr(a,"\"\\u00e9\"",&err); |
| 29 | json_get_string(v,&s,&sl); |
| 30 | if(sl!=2) FAIL("u00e9 len wrong"); PASS(); |
| 31 | |
| 32 | /* \u00e9 (2 bytes) + \u4e2d (3 bytes) = 5 bytes total */ |
| 33 | T("\\u00e9\\u4e2d exact 5 bytes"); |
| 34 | v=json_parse_cstr(a,"\"\\u00e9\\u4e2d\"",&err); |
| 35 | json_get_string(v,&s,&sl); |
| 36 | if(sl!=5) FAIL("combined uXXXX len wrong"); PASS(); |
| 37 | |
| 38 | json_arena_destroy(a); |
| 39 | return 0; |
| 40 | } |
| 41 | |
| 42 | static int test_objects(void) { |
| 43 | JsonArena* a = json_arena_create(NULL, 8192); |
| 44 | JsonError err; JsonValue *v, *item; int64_t iv; uint32_t len; |
| 45 | |
| 46 | /* Bug #1 regression */ |
| 47 | T("simple object {\"a\":1,\"b\":2}"); |
| 48 | v=json_parse_cstr(a,"{\"a\":1,\"b\":2}",&err); |
| 49 | if(!v||err!=JSON_OK) FAIL("parse failed"); |
| 50 | json_obj_finalize(a,v); |
| 51 | json_obj_get(v,"a",&item); json_get_int(item,&iv); if(iv!=1) FAIL("a!=1"); |
| 52 | json_obj_get(v,"b",&item); json_get_int(item,&iv); if(iv!=2) FAIL("b!=2"); |
| 53 | PASS(); |
| 54 | |
| 55 | T("empty object {}"); |
| 56 | v=json_parse_cstr(a,"{}",&err); |
| 57 | if(!v||v->type!=JSON_OBJECT) FAIL("{}"); PASS(); |
| 58 | |
| 59 | T("nested {\"users\":[{\"name\":\"Alice\"}]}"); |
| 60 | v=json_parse_cstr(a,"{\"users\":[{\"name\":\"Alice\",\"age\":30}]}",&err); |
| 61 | if(!v||err!=JSON_OK) FAIL("nested parse"); |
| 62 | json_obj_finalize(a,v); |
| 63 | JsonValue *users, *user0, *name_v; |
| 64 | json_obj_get(v,"users",&users); |
| 65 | json_arr_get(users,0,&user0); |
| 66 | json_obj_finalize(a,user0); |
| 67 | json_obj_get(user0,"name",&name_v); |
| 68 | const char* nm; uint32_t nl; |
| 69 | json_get_string(name_v,&nm,&nl); |
| 70 | if(nl!=5||memcmp(nm,"Alice",5)) FAIL("nested name"); PASS(); |
| 71 | |
| 72 | T("array [1,2,3]"); v=json_parse_cstr(a,"[1,2,3]",&err); |
| 73 | json_get_arr_len(v,&len); if(len!=3) FAIL("arr len"); |
| 74 | json_arr_get(v,2,&item); json_get_int(item,&iv); if(iv!=3) FAIL("arr[2]"); PASS(); |
| 75 | |
| 76 | json_arena_destroy(a); |
| 77 | return 0; |
| 78 | } |
| 79 | |
| 80 | static int test_path(void) { |
| 81 | JsonArena* a = json_arena_create(NULL, 8192); |
| 82 | JsonError err; JsonValue *root, *v; int64_t iv; |
| 83 | |
| 84 | root=json_parse_cstr(a,"{\"config\":{\"host\":\"localhost\",\"port\":8080},\"data\":[10,20,30]}",&err); |
| 85 | if(!root){printf("[TEST] path query setup FAIL: parse\n"); return 1;} |
| 86 | json_obj_finalize(a,root); |
| 87 | JsonValue* cfg; json_obj_get(root,"config",&cfg); json_obj_finalize(a,cfg); |
| 88 | |
| 89 | T("path data[2]"); v=json_path(root,"data[2]",&err); |
| 90 | json_get_int(v,&iv); if(iv!=30) FAIL("data[2]"); PASS(); |
| 91 | T("path config.port");v=json_path(root,"config.port",&err); |
| 92 | json_get_int(v,&iv); if(iv!=8080) FAIL("port"); PASS(); |
| 93 | T("path config[\"host\"]"); |
| 94 | v=json_path(root,"config[\"host\"]",&err); |
| 95 | const char* s; uint32_t sl; |
| 96 | json_get_string(v,&s,&sl); |
| 97 | if(sl!=9||memcmp(s,"localhost",9)) FAIL("host"); PASS(); |
| 98 | |
| 99 | json_arena_destroy(a); |
| 100 | return 0; |
| 101 | } |
| 102 | |
| 103 | static int test_serialize(void) { |
| 104 | JsonArena* a = json_arena_create(NULL, 8192); |
| 105 | JsonError err; JsonValue* root; char buf[1024]; |
| 106 | |
| 107 | T("round-trip {x,y,z}"); |
| 108 | root=json_parse_cstr(a,"{\"x\":42,\"y\":[1,2,3],\"z\":true}",&err); |
| 109 | if(!root||err!=JSON_OK) FAIL("rt parse"); |
| 110 | json_obj_finalize(a,root); |
| 111 | size_t written; |
| 112 | err=json_write(root,buf,sizeof(buf),&written,NULL); |
| 113 | if(err!=JSON_OK) FAIL("serialize"); |
| 114 | JsonArena* a2=json_arena_create(NULL,4096); |
| 115 | JsonValue* r2=json_parse_cstr(a2,buf,&err); |
| 116 | if(!r2||err!=JSON_OK) FAIL("re-parse"); |
| 117 | json_arena_destroy(a2); PASS(); |
| 118 | |
| 119 | T("buffer too small returns correct error"); |
| 120 | root=json_parse_cstr(a,"{\"key\":\"value\"}",&err); |
| 121 | if(!root) FAIL("parse for buf-small test"); |
| 122 | char tiny[5]; |
| 123 | JsonError e2=json_write(root,tiny,sizeof(tiny),NULL,NULL); |
| 124 | if(e2!=JSON_ERR_BUFFER_TOO_SMALL) FAIL("wrong error code"); PASS(); |
| 125 | |
| 126 | T("pretty print"); { |
| 127 | JsonWriteOpts opts={0}; opts.pretty=true; opts.indent=2; |
| 128 | err=json_write(root,buf,sizeof(buf),&written,&opts); |
| 129 | if(err!=JSON_OK||written<5) FAIL("pretty"); } PASS(); |
| 130 | |
| 131 | T("measure == write len"); |
| 132 | { size_t sz=0; json_measure(root,&sz,NULL); |
| 133 | err=json_write(root,buf,sz+2,&written,NULL); |
| 134 | if(err!=JSON_OK||written!=sz) FAIL("measure"); } PASS(); |
| 135 | |
| 136 | json_arena_destroy(a); |
| 137 | return 0; |
| 138 | } |
| 139 | |
| 140 | static int test_construction(void) { |
| 141 | JsonArena* a = json_arena_create(NULL, 16384); |
| 142 | JsonValue* arr=json_make_array(a); |
| 143 | JsonValue* obj=json_make_object(a); |
| 144 | int i; uint32_t len; |
| 145 | |
| 146 | T("build 100-element array"); |
| 147 | for(i=0;i<100;i++) json_arr_push(arr,a,json_make_int(a,i)); |
| 148 | json_get_arr_len(arr,&len); if(len!=100) FAIL("arr len"); PASS(); |
| 149 | |
| 150 | T("build object + finalize"); |
| 151 | json_obj_setz(obj,a,"pi",json_make_float(a,3.14159)); |
| 152 | json_obj_setz(obj,a,"name",json_make_stringz(a,"test")); |
| 153 | json_obj_setz(obj,a,"ok",json_make_bool(a,true)); |
| 154 | json_obj_finalize(a,obj); |
| 155 | JsonValue* v; if(json_obj_get(obj,"pi",&v)!=JSON_OK) FAIL("pi"); PASS(); |
| 156 | |
| 157 | T("deep clone == original"); |
| 158 | JsonArena* a2=json_arena_create(NULL,8192); |
| 159 | JsonValue* cl=json_clone(a2,obj); |
| 160 | if(!cl||!json_equal(obj,cl)) FAIL("clone"); |
| 161 | json_arena_destroy(a2); PASS(); |
| 162 | |
| 163 | json_arena_destroy(a); |
| 164 | return 0; |
| 165 | } |
| 166 | |
| 167 | static int test_errors(void) { |
| 168 | JsonArena* a=json_arena_create(NULL,4096); |
| 169 | JsonError err; JsonValue* v; |
| 170 | |
| 171 | T("reject {bad}"); v=json_parse_cstr(a,"{bad}",&err); if(v) FAIL("accepted bad"); PASS(); |
| 172 | T("reject [01]"); v=json_parse_cstr(a,"[01]",&err); if(v) FAIL("leading zero"); PASS(); |
| 173 | T("reject \\q"); v=json_parse_cstr(a,"\"\\q\"",&err); if(v) FAIL("bad escape"); PASS(); |
| 174 | T("reject trailing");v=json_parse_cstr(a,"42 garbage",&err); if(v) FAIL("trailing"); PASS(); |
| 175 | T("reject \\uD800");v=json_parse_cstr(a,"\"\\uD800\"",&err); if(v) FAIL("lone surr"); PASS(); |
| 176 | T("type mismatch"); v=json_parse_cstr(a,"42",&err); |
| 177 | bool b; if(json_get_bool(v,&b)!=JSON_ERR_TYPE_MISMATCH) FAIL("mismatch"); PASS(); |
| 178 | |
| 179 | json_arena_destroy(a); |
| 180 | return 0; |
| 181 | } |
| 182 | |
| 183 | static int test_introsort(void) { |
| 184 | JsonArena* a=json_arena_create(NULL,512*1024); |
| 185 | JsonValue* obj=json_make_object(a); |
| 186 | char kb[32]; int n=200, i; |
| 187 | |
| 188 | T("introsort 200 keys + binary search"); |
| 189 | for(i=0;i<n;i++){ |
| 190 | snprintf(kb,sizeof(kb),"key_%04d",i); |
| 191 | json_obj_setz(obj,a,kb,json_make_int(a,(int64_t)i)); |
| 192 | } |
| 193 | json_obj_finalize(a,obj); |
| 194 | JsonValue* v; int64_t iv; |
| 195 | snprintf(kb,sizeof(kb),"key_%04d",99); |
| 196 | if(json_obj_get(obj,kb,&v)!=JSON_OK) FAIL("lookup 99"); |
| 197 | json_get_int(v,&iv); if(iv!=99) FAIL("value 99"); |
| 198 | snprintf(kb,sizeof(kb),"key_%04d",0); |
| 199 | if(json_obj_get(obj,kb,&v)!=JSON_OK) FAIL("lookup 0"); |
| 200 | snprintf(kb,sizeof(kb),"key_%04d",n-1); |
| 201 | if(json_obj_get(obj,kb,&v)!=JSON_OK) FAIL("lookup last"); PASS(); |
| 202 | |
| 203 | json_arena_destroy(a); |
| 204 | return 0; |
| 205 | } |
| 206 | |
| 207 | static int test_arena_reset(void) { |
| 208 | JsonArena* a=json_arena_create(NULL,4096); |
| 209 | JsonError err; int i; |
| 210 | |
| 211 | T("arena reset x5 reuse"); |
| 212 | for(i=0;i<5;i++){ |
| 213 | json_arena_reset(a); |
| 214 | JsonValue* v=json_parse_cstr(a,"[1,2,3,{\"ok\":true}]",&err); |
| 215 | if(!v||err!=JSON_OK) FAIL("reset round"); |
| 216 | } PASS(); |
| 217 | |
| 218 | json_arena_destroy(a); |
| 219 | return 0; |
| 220 | } |
| 221 | |
| 222 | int main(void) { |
| 223 | int f=0; |
| 224 | printf("=== json_pal.h v1.1 — regression suite ===\n\n"); |
| 225 | f+=test_primitives(); |
| 226 | f+=test_objects(); |
| 227 | f+=test_path(); |
| 228 | f+=test_serialize(); |
| 229 | f+=test_construction(); |
| 230 | f+=test_errors(); |
| 231 | f+=test_introsort(); |
| 232 | f+=test_arena_reset(); |
| 233 | printf("\n%s — %d failure(s)\n", f?"TESTS FAILED":"ALL TESTS PASSED", f); |
| 234 | return f?1:0; |
| 235 | } |
| 236 |