diff options
Diffstat (limited to 'json')
-rw-r--r-- | json/jsmn.h | 471 | ||||
-rw-r--r-- | json/jsonparse.c | 167 | ||||
-rw-r--r-- | json/jsonparse.h | 35 |
3 files changed, 673 insertions, 0 deletions
diff --git a/json/jsmn.h b/json/jsmn.h new file mode 100644 index 0000000..3178dcc --- /dev/null +++ b/json/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/json/jsonparse.c b/json/jsonparse.c new file mode 100644 index 0000000..6cfae2e --- /dev/null +++ b/json/jsonparse.c @@ -0,0 +1,167 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include <jsonparse.h> + +void substr_token (char *json, char *temp, jsmntok_t *t) { + memcpy(temp, json+t->start, t->end - t->start); + temp[t->end-t->start] = '\0'; +} + +void json_parse_array(char *json, void *temp, jsmntok_t *t, int r, char type) { + for (int i=0; i<r; i++) { + if (t[i].type == JSMN_PRIMITIVE) { + char val[32]; substr_token(json, val, t+i); + if (type == 'i') { + int *p = (int*)(intptr_t)temp+sizeof(int)*i; + *p = atoi(val); + } + if (type == 'c') { + int8_t *p = (int8_t*)(intptr_t)temp+sizeof(int8_t)*i; + *p = atoi(val); + } + } + } +} + +void json_dump_array (char *str, void *arr, int len, char type) { + int cur = 0; + sprintf(str+cur, "[ "); cur = strlen(str); + for (int i=0; i<len; i++) { + if (type == 'i') { + sprintf(str+cur, "%d", (int)(intptr_t)(arr+i*sizeof(int))); + cur = strlen(str); + } + if (type == 'c') { + printf("%d\n", ((int8_t*)(intptr_t)(arr))[i]); + sprintf(str+cur, "%d", ((int8_t*)(intptr_t)(arr))[i]); + cur = strlen(str); + } + if (i < len-1) { + sprintf(str+cur, ", "); cur = strlen(str); + } + } + sprintf(str+cur, " ]"); +} + +int json_parse_subtokens (char *json, jsmntok_t *t, int r, int i) { + int rt = 0; + for (int j=i; j<r; j++) { + if (t[j].start < t[i].end) { rt ++; } + else { break; } + } + return rt; +} + +/* DEPRECATED TRIALS, delete after backup +// parses a <str, int> pair set with a set of already parsed tokens +int json_parse_dict_ci_init (char *json, + pair_ci *dict, jsmntok_t *t, int r) { + int obj_i = 0, dict_i = -1; + for (int i=0; i<r; i++) { + if (t[i].type == JSMN_OBJECT) { + dict_i ++; + obj_i = 0; + } + if (t[i].type == JSMN_STRING) { + if (obj_i == 0) { + substr_token(json, dict[dict_i].key, t+i); + } else if (obj_i == 1) { + char ts[13]; substr_token(json, ts, t+i); + dict[dict_i].i = atoi(ts); + } + obj_i++; + } + } + return dict_i+1; +} + +// parses a <str, int> pair set with a set of already parsed tokens +int json_parse_dict_cf_init (char *json, + pair_cf *dict, jsmntok_t *t, int r) { + int obj_i = 0, dict_i = -1; + for (int i=0; i<r; i++) { + if (t[i].type == JSMN_OBJECT) { + dict_i ++; + obj_i = 0; + } + if (t[i].type == JSMN_STRING) { + if (obj_i == 0) { + substr_token(json, dict[dict_i].key, t+i); + } else if (obj_i == 1) { + char ts[13]; substr_token(json, ts, t+i); + dict[dict_i].i = atof(ts); + } + obj_i++; + } + } + return dict_i+1; +} + + +// parses a <str, int> pair set +int json_parse_dict_ci (char *json, pair_ci *dict) { + jsmn_parser p; jsmn_init(&p); + jsmntok_t t[MAXTOKENS]; + int r = jsmn_parse(&p, json, strlen(json), t, MAXTOKENS); + json_parse_dict_ci_init(json, dict, t, r); +} + +// parses a <str, float> pair set +int json_parse_dict_cf (char *json, pair_cf *dict) { + jsmn_parser p; jsmn_init(&p); + jsmntok_t t[MAXTOKENS]; + int r = jsmn_parse(&p, json, strlen(json), t, MAXTOKENS); + json_parse_dict_cf_init(json, dict, t, r); +} + + + +// parses a list of objects which are lists of pairs <str, int> +int json_parse_list_ci (char *json, list_ci *list) { + jsmn_parser p; jsmn_init(&p); + jsmntok_t t[MAXTOKENS]; + int r = jsmn_parse(&p, json, strlen(json), t, MAXTOKENS); + int list_i = 0; + for (int i=1; i<r; i++) { // i=1: ignore outer [] + if (t[i].type == JSMN_ARRAY) { + int rt = 0; + for (int j=i; j<r; j++) { + if (t[j].start < t[i].end) { rt ++; } + else { break; } + } + int len = json_parse_dict_ci_init( + json, list[list_i].pairs, t+i, rt); + list[list_i].len = len; + list_i ++; + i += rt-1; + } + } + return list_i; +} + +// parses a list of objects which are lists of pairs <str, float> +int json_parse_list_cf (char *json, list_cf *list) { + jsmn_parser p; jsmn_init(&p); + jsmntok_t t[MAXTOKENS]; + int r = jsmn_parse(&p, json, strlen(json), t, MAXTOKENS); + int list_i = 0; + for (int i=1; i<r; i++) { // i=1: ignore outer [] + if (t[i].type == JSMN_ARRAY) { + int rt = 0; + for (int j=i; j<r; j++) { + if (t[j].start < t[i].end) { rt ++; } + else { break; } + } + int len = json_parse_dict_cf_init( + json, list[list_i].pairs, t+i, rt); + list[list_i].len = len; + list_i ++; + i += rt-1; + } + } + return list_i; +} +*/
\ No newline at end of file diff --git a/json/jsonparse.h b/json/jsonparse.h new file mode 100644 index 0000000..2186154 --- /dev/null +++ b/json/jsonparse.h @@ -0,0 +1,35 @@ +#ifndef JSONPARSE_H +#define JSONPARSE_H + +#define JSMN_STATIC +#include <jsmn.h> + +#define MAXTOKENS 2048 + +void substr_token(char *json, char *temp, jsmntok_t *t); + +void json_parse_array(char *json, void *temp, jsmntok_t *t, int r, char type); +void json_dump_array (char *str, void *arr, int len, char type); + +int json_parse_subtokens (char *json, jsmntok_t *t, int r, int i); +/* + +typedef struct { char key[32]; int i; } pair_ci; +typedef struct { char key[32]; float i; } pair_cf; + + +int json_parse_dict_ci_init (char *json, pair_ci *dict, jsmntok_t *t, int r); +int json_parse_dict_cf_init (char *json, pair_cf *dict, jsmntok_t *t, int r); +int json_parse_dict_ci (char *json, pair_ci *dict); +int json_parse_dict_cf (char *json, pair_cf *dict); + +#define MAXLISTLEN 64 +#define MAXOBJLEN 32 + +typedef struct { pair_ci pairs[MAXOBJLEN]; int len; } list_ci; +typedef struct { pair_cf pairs[MAXOBJLEN]; int len; } list_cf; + +int json_parse_list_ci (char *json, list_ci *list); +int json_parse_list_cf (char *json, list_cf *list); +*/ +#endif
\ No newline at end of file |