diff --git a/Makefile b/Makefile index 81d26a8..6bec1db 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ all: test example test.c: test/test.c src/js0n.c - gcc -Wall -Wextra -Wno-unused-parameter -o test/test test/test.c src/js0n.c + gcc -std=c99 -Wall -Wextra -Werror -o test/test test/test.c src/js0n.c test: test.c @if ./test/test ; then \ diff --git a/src/js0n.c b/src/js0n.c index 062859b..66d4937 100644 --- a/src/js0n.c +++ b/src/js0n.c @@ -2,15 +2,10 @@ // public domain or MIT license, contributions/improvements welcome via github at https://github.com/quartzjer/js0n #include // one strncmp() is used to do key comparison, and a strlen(key) if no len passed in - -// gcc started warning for the init syntax used here, is not helpful so don't generate the spam, supressing the warning is really inconsistently supported across versions -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#pragma GCC diagnostic push +#include +#ifdef DEBUG +#include #endif -#pragma GCC diagnostic ignored "-Wunknown-pragmas" -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Winitializer-overrides" -#pragma GCC diagnostic ignored "-Woverride-init" // only at depth 1, track start pointers to match key/value #define PUSH(i) if(depth == 1) { if(!index) { val = cur+i; }else{ if(klen && index == 1) start = cur+i; else index--; } } @@ -18,6 +13,17 @@ // determine if key matches or value is complete #define CAP(i) if(depth == 1) { if(val && !index) {*vlen = (size_t)((cur+i+1) - val); return val;}; if(klen && start) {index = (klen == (size_t)(cur-start) && strncmp(key,start,klen)==0) ? 0 : 2; start = 0;} } + +enum state { + S_STRUCT, + S_BARE, + S_STRING, + S_UTF8, + S_ESC, +}; + +#define range(x,s,e) ((x) >= (s) && (x) <= (e)) + // this makes a single pass across the json bytes, using each byte as an index into a jump table to build an index and transition state const char *js0n(const char *key, size_t klen, const char *json, size_t jlen, size_t *vlen) @@ -27,54 +33,11 @@ const char *js0n(const char *key, size_t klen, size_t index = 1; int depth = 0; int utf8_remain = 0; - static void *gostruct[] = - { - [0 ... 255] = &&l_bad, - ['\t'] = &&l_loop, [' '] = &&l_loop, ['\r'] = &&l_loop, ['\n'] = &&l_loop, - ['"'] = &&l_qup, - [':'] = &&l_loop,[','] = &&l_loop, - ['['] = &&l_up, [']'] = &&l_down, // tracking [] and {} individually would allow fuller validation but is really messy - ['{'] = &&l_up, ['}'] = &&l_down, - ['-'] = &&l_bare, [48 ... 57] = &&l_bare, // 0-9 - [65 ... 90] = &&l_bare, // A-Z - [97 ... 122] = &&l_bare // a-z - }; - static void *gobare[] = - { - [0 ... 31] = &&l_bad, - [32 ... 126] = &&l_loop, // could be more pedantic/validation-checking - ['\t'] = &&l_unbare, [' '] = &&l_unbare, ['\r'] = &&l_unbare, ['\n'] = &&l_unbare, - [','] = &&l_unbare, [']'] = &&l_unbare, ['}'] = &&l_unbare, [':'] = &&l_unbare, - [127 ... 255] = &&l_bad - }; - static void *gostring[] = - { - [0 ... 31] = &&l_bad, [127] = &&l_bad, - [32 ... 126] = &&l_loop, - ['\\'] = &&l_esc, ['"'] = &&l_qdown, - [128 ... 191] = &&l_bad, - [192 ... 223] = &&l_utf8_2, - [224 ... 239] = &&l_utf8_3, - [240 ... 247] = &&l_utf8_4, - [248 ... 255] = &&l_bad - }; - static void *goutf8_continue[] = - { - [0 ... 127] = &&l_bad, - [128 ... 191] = &&l_utf_continue, - [192 ... 255] = &&l_bad - }; - static void *goesc[] = - { - [0 ... 255] = &&l_bad, - ['"'] = &&l_unesc, ['\\'] = &&l_unesc, ['/'] = &&l_unesc, ['b'] = &&l_unesc, - ['f'] = &&l_unesc, ['n'] = &&l_unesc, ['r'] = &&l_unesc, ['t'] = &&l_unesc, ['u'] = &&l_unesc - }; - void **go = gostruct; - + enum state state = S_STRUCT; + if(!json || jlen <= 0 || !vlen) return 0; *vlen = 0; - + // no key is array mode, klen provides requested index if(!key) { @@ -86,77 +49,148 @@ const char *js0n(const char *key, size_t klen, for(start=cur=json,end=cur+jlen; cur%.5s< state %d depth %d\n", + cur, state, depth); +#endif return 0; - - l_up: + + l_up: PUSH(0); ++depth; - goto l_loop; + continue; - l_down: + l_down: --depth; CAP(0); - goto l_loop; + continue; - l_qup: + l_qup: PUSH(1); - go=gostring; - goto l_loop; + state = S_STRING; + continue; - l_qdown: + l_qdown: CAP(-1); - go=gostruct; - goto l_loop; - - l_esc: - go = goesc; - goto l_loop; - - l_unesc: - go = gostring; - goto l_loop; - - l_bare: + state = S_STRUCT; + continue; + + l_bare: PUSH(0); - go = gobare; - goto l_loop; + state = S_BARE; + continue; - l_unbare: + l_unbare: CAP(-1); - go = gostruct; - goto *go[(unsigned char)*cur]; - - l_utf8_2: - go = goutf8_continue; - utf8_remain = 1; - goto l_loop; - - l_utf8_3: - go = goutf8_continue; - utf8_remain = 2; - goto l_loop; + state = S_STRUCT; + goto again; - l_utf8_4: - go = goutf8_continue; - utf8_remain = 3; - goto l_loop; + } - l_utf_continue: - if (!--utf8_remain) - go=gostring; - goto l_loop; + if (depth) { + *vlen = jlen; // incomplete + } + return 0; } - -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#pragma GCC diagnostic pop -#endif diff --git a/test/test.c b/test/test.c index 29569c6..1330bbc 100644 --- a/test/test.c +++ b/test/test.c @@ -10,7 +10,10 @@ int main(int argc, char **argv) const char *ret; size_t len, len2, jlen=0; FILE *f; - + + (void) argc; + (void) argv; + fail_unless((f = fopen("./test/test.json","r")) != NULL); while((len = fread(buff,1,1024,f)) > 0) { @@ -19,13 +22,13 @@ int main(int argc, char **argv) jlen+=len; } fclose(f); - + fail_unless((ret = js0n("test",0,json,jlen,&len))); fail_unless(len); fail_unless(strncmp("value",ret,len) == 0); fail_unless((ret = js0n("foo",0,json,jlen,&len))); - fail_unless(len); + fail_unless(len == 13); fail_unless(strncmp("b\\\"a and \\\\ r",ret,len) == 0); fail_unless((ret = js0n("array",0,json,jlen,&len))); diff --git a/test/test.json b/test/test.json index ba8d9eb..d1e7b87 100644 --- a/test/test.json +++ b/test/test.json @@ -11,4 +11,4 @@ "obj":{"true":true}, "name":"value", "value":"real" -} \ No newline at end of file +}