Created
October 15, 2014 12:17
-
-
Save Arseny-N/d233f401b0f6c6f98ccd to your computer and use it in GitHub Desktop.
Callback based (zero-copy) JSON C parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdarg.h> | |
#include "generic-json-zero-copy.h" | |
void print_xjson(char *prefix, char *postfix, xjson *b, offset off, int len) | |
{ | |
int i; | |
printf("len=%d,off=%d ",len, off); | |
printf("%s", prefix); | |
for(i=off; i < off+len && ok(b, i); ++i ) { | |
char c = at(b, i); | |
switch(c) { | |
case '\r': printf("\\r"); break; | |
case '\n': printf("\\n\n"); break; | |
case '\0': printf("\\0"); break; | |
default: printf("%c", c); break; | |
} | |
} | |
printf("%s", postfix); | |
} | |
static offset __get_json_string_end(xjson *b, offset off) | |
{ | |
offset i; | |
char depth = 0; | |
for(i=off; ok(b, i); ++i) { | |
char c = at(b, i); | |
if( c == '"' ) { | |
depth = (depth == 0); | |
if(depth == 0) { | |
if( i > 0 && at(b, i-1) == '\\' ) { | |
depth = (depth == 0); | |
continue; | |
} | |
JSON_assert("offset == ERR, bad ERR value!", i == ERR, return ERR;); | |
return i; | |
} | |
continue; | |
} | |
} | |
JSON_error("__get_json_string_end() failed to find `\"'"); | |
return ERR; | |
} | |
static offset __get_json_object_end(xjson *b, offset off) | |
{ | |
offset i; | |
char depth = 0; | |
for(i=off; ok(b, i) ; ++i) { | |
char c = at(b, i); | |
if( c == '"' ) { | |
i = __get_json_string_end(b, i); | |
if(i == ERR) { | |
JSON_error("__get_json_sting_end() failed"); | |
return ERR; | |
} | |
continue; | |
} | |
if( c == '{' ) { depth ++; continue; } | |
if( c == '}' ) { | |
depth --; | |
if(depth == 0) { | |
JSON_assert("offset == ERR, bad ERR value!", i == ERR, return ERR;); | |
return i; | |
} | |
continue; | |
} | |
} | |
JSON_error("__get_json_object_end() failed to find `}'"); | |
return ERR; | |
} | |
static offset __get_json_array_end(xjson *b, offset off) | |
{ | |
offset i; | |
char depth = 0; | |
for(i=off; ok(b, i) ; ++i) { | |
char c = at(b, i); | |
if( c == '"' ) { | |
i = __get_json_string_end(b, i); | |
if(i == ERR) { | |
JSON_assert("offset == ERR, bad ERR value!", i == ERR, return ERR;); | |
return ERR; | |
} | |
continue; | |
} | |
if( c == '[' ) { depth ++; continue; } | |
if( c == ']' ) { | |
depth --; | |
if(depth == 0) | |
return i; | |
continue; | |
} | |
} | |
JSON_error("__get_json_array_end() failed to find `]'"); | |
return ERR; | |
} | |
static offset __get_json_number_end(xjson *b, offset off) | |
{ | |
int i; | |
for(i=off; ok(b, i); ++i) { | |
char c = at(b, i); | |
if( !isdigit(c) && c != 'e' && c != 'E' | |
&& c != '-' && c != '+' && c != '.' ) { | |
JSON_assert("offset == ERR, bad ERR value!", i-1 == ERR, return ERR;); | |
return i-1; | |
} | |
} | |
JSON_error("__get_json_number_end() failed to find number end"); | |
return ERR; | |
} | |
static int __compare(xjson *b, offset off, char *str, int len) | |
{ | |
offset i; | |
for(i=off; ok(b, i) && i-off < len && str[i-off]; ++i) { | |
char c = at(b, i); | |
if(str[i-off] != c) | |
return str[i-off] > c ? 1 : -1; | |
} | |
return 0; | |
} | |
static offset __get_json_bool_or_null_end(xjson *b, offset off) | |
{ | |
if( __compare(b, off, "true", 4) == 0 || __compare(b, off, "null", 4) == 0) { | |
off += 3; | |
} else if( __compare(b, off, "false", 5) == 0) { | |
off += 4; | |
} else { | |
JSON_error("__get_json_bool_or_null_end() bad json value"); | |
return ERR; | |
} | |
JSON_assert("offset == ERR, bad ERR value!", off == ERR, return ERR;); | |
return off; | |
} | |
static offset _get_json_element_end(xjson *b, offset off) | |
{ | |
char c = at(b, off); | |
return c == '"' ? __get_json_string_end(b, off) : | |
c == '{' ? __get_json_object_end(b, off) : | |
c == '[' ? __get_json_array_end(b, off) : | |
(isdigit(c) || c == '-' || c == '+') ? __get_json_number_end(b, off) : __get_json_bool_or_null_end(b, off); | |
} | |
/*------------------------------------------------*/ | |
static offset __first_non_space(xjson *b, offset off) | |
{ | |
offset i; | |
for(i = off; ok(b, i) && isspace(at(b, i)) ; ++i); | |
JSON_assert("offset == ERR, bad ERR value!", i == ERR, return ERR;); | |
return ok(b, i) ? i : ERR; | |
} | |
static offset __get_next_elem(xjson *b, offset off, char skip_once) | |
{ | |
off = __first_non_space(b, off); | |
if(off == ERR) | |
return off; | |
if( at(b, off) != skip_once ) | |
return off; | |
JSON_assert("offset == ERR, bad ERR value!", off + 1 == ERR, return ERR;); | |
off = __first_non_space(b, off+1); | |
if(off == ERR) | |
return off; | |
return off; | |
} | |
/*------------------------------------------------*/ | |
int JSON_decode_object(xjson *b, offset off, JSON_object_cb callback, void *arg) | |
{ | |
offset oname, oname_e; | |
offset ovalue, ovalue_e; | |
int rv = 0; | |
for(; ok(b, off) && ok(b, off + 1) ;) { | |
oname = __get_next_elem(b, off+1, ','); | |
if(oname == ERR) { | |
JSON_error("__get_next_elem(1)"); | |
return -1; | |
} | |
if(at(b, oname) == '}' ) { | |
return 0; | |
} | |
oname_e = __get_json_string_end(b, oname); | |
if(oname_e == ERR) { | |
JSON_error("__get_json_string_end()"); | |
return -1; | |
} | |
ovalue = __get_next_elem(b, oname_e+1, ':'); | |
if(ovalue == ERR) { | |
JSON_error("__get_next_elem(2)"); | |
return -1; | |
} | |
ovalue_e = _get_json_element_end(b, ovalue); | |
if(ovalue_e == ERR) { | |
JSON_error("_get_json_element_end()"); | |
return -1; | |
} | |
rv = callback(b, oname, oname_e - oname + 1 , ovalue, ovalue_e - ovalue + 1, arg); | |
if(rv) | |
return rv; | |
off = ovalue_e; | |
} | |
return 0; | |
} | |
int JSON_decode_array(xjson *b, offset off, JSON_array_cb callback, void *arg) | |
{ | |
offset ovalue, ovalue_e; | |
int rv = 0, index = 0; | |
for(; ok(b, off) && ok(b, off + 1) ;) { | |
ovalue = __get_next_elem(b, off+1, ','); | |
if(ovalue == ERR) { | |
JSON_error("__get_next_elem()"); | |
return -1; | |
} | |
if(at(b, ovalue) == ']' ) { | |
return 0; | |
} | |
ovalue_e = _get_json_element_end(b, ovalue); | |
if(ovalue_e == ERR) { | |
JSON_error("_get_json_element_end() ovalue=%lu, ovalue_e=%lx", (long)ovalue, (long)ovalue_e); | |
return -1; | |
} | |
rv = callback(b, index++, ovalue, ovalue_e - ovalue + 1 ,arg); | |
if(rv) | |
return rv; | |
off = ovalue_e; | |
} | |
return 0; | |
} | |
int JSON_decode(xjson *b, offset off, JSON_array_cb a_cb, JSON_object_cb o_cb, void *arg) | |
{ | |
char c = at(b, off); | |
if(c == '{' && o_cb) | |
return JSON_decode_object(b, off, o_cb, arg); | |
if(c == '[' && a_cb ) | |
return JSON_decode_array(b, off, a_cb, arg); | |
return -1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __JSON_ZERO_H_ | |
#define __JSON_ZERO_H_ | |
#include <ctype.h> /* for isdigit() and isspace() */ | |
#include <stdlib.h> /* for NULL */ | |
#ifdef JSON_ERRORS | |
#include <stdio.h> | |
#include <errno.h> | |
#ifndef error | |
#define error(fmt, args...) fprintf(stderr, __FILE__ ":%s:%d ERROR(%d): " fmt "\n", __func__, __LINE__,errno, ##args) | |
#endif | |
#define JSON_error(fmt, args...) error("json-error:" fmt, ##args) | |
#define JSON_assert(msg, assert, handler) do{ if(assert){ JSON_error(msg); handler; } } while(0) | |
#else | |
#define JSON_error(fmt, args...) | |
#define JSON_assert(msg, assert, handler) (assert); | |
#endif | |
#define ERR 0x0 | |
typedef unsigned int offset; | |
#ifdef JSONX_LWIP | |
#include "network-stack.h" | |
#define at(buf, ind) pbuf_get_at(buf->p, (ind)) | |
#define ok(buf, ind) ( (ind) < (buf->p->tot_len) ) | |
typedef struct netbuf xjson; | |
#else | |
#define at(buf, ind) buf[ind] | |
#define ok(buf, ind) (buf[ind] != 0) | |
typedef char xjson; | |
#endif | |
/** | |
* index: the index of the array element | |
* *val : a value corresponding to the name, could be any JSON type including arrays and objects. | |
* vlen: length of the *val | |
* *arg : the pinter passed to the JSON_decode_object() function. | |
* | |
* If the *val is an object or array, it could be parsed by the JSON_decode_* functions. | |
* | |
**/ | |
typedef int (*JSON_array_cb) (xjson *b, int index, offset oval, int vlen, void *arg); | |
/** | |
* *name: null terminated name | |
* nlen: length of the *name | |
* *val : a value corresponding to the name, could be any JSON type including arrays and objects. | |
* vlen: length of the *val | |
* *arg : the pinter passed to the JSON_decode_object() function. | |
* | |
* If the *val is an object or array, it could be parsed by the JSON_decode_* functions. | |
* | |
**/ | |
typedef int (*JSON_object_cb) (xjson *b, offset oname, int nlen, offset oval, int vlen, void *arg); | |
int JSON_decode_array(xjson *b, offset off, JSON_array_cb callback, void *arg); | |
int JSON_decode_object(xjson *b, offset off, JSON_object_cb callback, void *arg); | |
int JSON_decode(xjson *b, offset off, JSON_array_cb a_cb, JSON_object_cb o_cb, void *arg); | |
void print_xjson(char *prefix, char *postfix, xjson *b, offset off, int len); | |
/** | |
* JSON_decode(char *s, JSON_array_cb a_cb, JSON_object_cb o_cb, void *arg) | |
* JSON_decode_array(char *s, JSON_array_cb callback, void *arg) | |
* JSON_decode_object(char *s, JSON_object_cb callback, void *arg) | |
* | |
* *s: | |
* the first character should be a valid JSON one, the string should be null terminated. | |
* callback: | |
* the callback function could call the JSON_decode_* funcs to decode an element. | |
*/ | |
#define JSON_string_unbracet(start, len) do{ start += 1; len -= 1; } while(0) | |
#define JSON_string_bracet_restore(start, len) do{ start -= 1; len += 1; } while(0) | |
#define string_static(string) string, ((size_t)sizeof(string) -1 ) | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment