/* * 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 #include "jsmn_defines.h" #ifdef JSMN_SHORT_TOKENS typedef unsigned short jsmnint; #else typedef unsigned int jsmnint; #endif #define JSMN_NEG ((jsmnint)-1) /** * JSON type identifier. Basic types are: */ typedef enum jsmntype { JSMN_UNDEFINED = 0x0000, JSMN_OBJECT = 0x0001, /*!< Object */ JSMN_ARRAY = 0x0002, /*!< Array */ JSMN_STRING = 0x0004, /*!< String */ JSMN_PRIMITIVE = 0x0008, /*!< Other primitive: number, boolean (true/false) or null */ JSMN_KEY = 0x0010, /*!< is a key */ JSMN_VALUE = 0x0020, /*!< is a value */ /* Complex elements */ JSMN_CONTAINER = JSMN_OBJECT | JSMN_ARRAY, #ifndef JSMN_PERMISSIVE_KEY JSMN_KEY_TYPE = JSMN_STRING, #else JSMN_KEY_TYPE = JSMN_STRING | JSMN_PRIMITIVE, #endif JSMN_ANY_TYPE = JSMN_OBJECT | JSMN_ARRAY | JSMN_STRING | JSMN_PRIMITIVE, JSMN_OBJ_VAL = JSMN_OBJECT | JSMN_VALUE, JSMN_ARR_VAL = JSMN_ARRAY | JSMN_VALUE, JSMN_STR_KEY = JSMN_STRING | JSMN_KEY, JSMN_STR_VAL = JSMN_STRING | JSMN_VALUE, JSMN_PRI_VAL = JSMN_PRIMITIVE | JSMN_VALUE, #ifdef JSMN_PERMISSIVE_KEY JSMN_OBJ_KEY = JSMN_OBJECT | JSMN_KEY, JSMN_ARR_KEY = JSMN_ARRAY | JSMN_KEY, JSMN_PRI_KEY = JSMN_PRIMITIVE | JSMN_KEY, #endif /* Primitive extension */ JSMN_PRI_LITERAL = 0x0040, /*!< true, false, null */ JSMN_PRI_INTEGER = 0x0080, /*!< 0, 1 - 9 */ JSMN_PRI_SIGN = 0x0100, /*!< minus sign, '-' or plus sign, '+' */ JSMN_PRI_DECIMAL = 0x0200, /*!< deminal point '.' */ JSMN_PRI_EXPONENT = 0x0400, /*!< exponent, 'e' or 'E' */ JSMN_PRI_MINUS = JSMN_PRI_SIGN, /* Parsing validation, expectations, and state information */ JSMN_PRI_CONTINUE = 0x0800, /*!< Allow a continuation of a PRIMITIVE */ JSMN_CLOSE = 0x1000, /*!< Close OBJECT '}' or ARRAY ']' */ JSMN_COLON = 0x2000, /*!< Colon ':' expected after KEY */ JSMN_COMMA = 0x4000, /*!< Comma ',' expected after VALUE */ JSMN_INSD_OBJ = 0x8000, /*!< Inside an OBJECT */ /* Parsing rules */ JSMN_ROOT_INIT = JSMN_ANY_TYPE | JSMN_VALUE, #ifndef JSMN_PERMISSIVE #ifndef JSMN_MULTIPLE_JSON JSMN_ROOT = JSMN_UNDEFINED, #else JSMN_ROOT = JSMN_ANY_TYPE | JSMN_VALUE, #endif JSMN_OPEN_OBJECT = JSMN_KEY_TYPE | JSMN_KEY | JSMN_CLOSE | JSMN_INSD_OBJ, JSMN_AFTR_OBJ_KEY = JSMN_VALUE | JSMN_INSD_OBJ | JSMN_COLON, JSMN_AFTR_OBJ_VAL = JSMN_KEY | JSMN_CLOSE | JSMN_INSD_OBJ | JSMN_COMMA, JSMN_OPEN_ARRAY = JSMN_ANY_TYPE | JSMN_VALUE | JSMN_CLOSE, JSMN_AFTR_ARR_VAL = JSMN_VALUE | JSMN_CLOSE | JSMN_COMMA, JSMN_AFTR_CLOSE = JSMN_CLOSE | JSMN_COMMA, JSMN_AFTR_COLON = JSMN_ANY_TYPE | JSMN_VALUE | JSMN_INSD_OBJ, JSMN_AFTR_COMMA_O = JSMN_KEY_TYPE | JSMN_KEY | JSMN_INSD_OBJ, JSMN_AFTR_COMMA_A = JSMN_ANY_TYPE | JSMN_VALUE, #else JSMN_ROOT = JSMN_ANY_TYPE | JSMN_COLON | JSMN_COMMA, JSMN_ROOT_AFTR_O = JSMN_ANY_TYPE | JSMN_COMMA, JSMN_OPEN_OBJECT = JSMN_KEY_TYPE | JSMN_KEY | JSMN_CLOSE | JSMN_INSD_OBJ, JSMN_AFTR_OBJ_KEY = JSMN_VALUE | JSMN_INSD_OBJ | JSMN_COLON, JSMN_AFTR_OBJ_VAL = JSMN_ANY_TYPE | JSMN_CLOSE | JSMN_INSD_OBJ | JSMN_COMMA, JSMN_OPEN_ARRAY = JSMN_ANY_TYPE | JSMN_VALUE | JSMN_CLOSE, JSMN_AFTR_ARR_VAL = JSMN_ANY_TYPE | JSMN_CLOSE | JSMN_COMMA, JSMN_AFTR_CLOSE = JSMN_ANY_TYPE | JSMN_CLOSE | JSMN_COMMA, JSMN_AFTR_COLON = JSMN_ANY_TYPE | JSMN_VALUE | JSMN_INSD_OBJ, JSMN_AFTR_COLON_R = JSMN_ANY_TYPE | JSMN_VALUE, JSMN_AFTR_COMMA_O = JSMN_KEY_TYPE | JSMN_KEY | JSMN_INSD_OBJ, JSMN_AFTR_COMMA_A = JSMN_ANY_TYPE | JSMN_VALUE, JSMN_AFTR_COMMA_R = JSMN_ANY_TYPE, #endif } jsmntype; /*! * JSMN Error Codes */ typedef enum jsmnerr { JSMN_SUCCESS = 0, JSMN_ERROR_NOMEM = -1, /*!< Not enough tokens were provided */ JSMN_ERROR_LEN = -2, /*!< Input data too long */ JSMN_ERROR_INVAL = -3, /*!< Invalid character inside JSON string */ JSMN_ERROR_PART = -4, /*!< The string is not a full JSON packet, more bytes expected */ JSMN_ERROR_UNMATCHED_BRACKETS = -5, /*!< The JSON string has unmatched brackets */ } jsmnerr; /*! * JSMN Boolean */ typedef enum jsmnbool { JSMN_FALSE = 0, JSMN_TRUE = 1, } jsmnbool; /** * JSON token description. */ typedef struct jsmntok { jsmntype type; /*!< type (object, array, string etc.) */ jsmnint start; /*!< start position in JSON data string */ jsmnint end; /*!< end position in JSON data string */ jsmnint size; /*!< number of children */ #ifdef JSMN_PARENT_LINKS jsmnint parent; /*!< parent id */ #endif #ifdef JSMN_NEXT_SIBLING jsmnint next_sibling; /*!< next sibling id */ #endif } jsmntok; /** * JSON parser * * Contains an array of token blocks available. Also stores * the string being parsed now and current position in that string. */ typedef struct jsmnparser { jsmnint pos; /*!< offset in the JSON string */ jsmnint toknext; /*!< next token to allocate */ /*!< when tokens == NULL, keeps track of container types to a depth of * (sizeof(jsmnint) * 8) */ jsmnint toksuper; /*!< superior token node, e.g. parent object or array */ /*!< when tokens == NULL, toksuper represents container depth */ jsmntype expected; /*!< Expected jsmn type(s) */ } jsmnparser; /** * @brief Create JSON parser over an array of tokens * * @param[out] parser jsmn parser */ JSMN_API void jsmn_init(jsmnparser *parser); /** * @brief Run JSON parser * * It parses a JSON data string into and array of tokens, each * describing a single JSON object. * * @param[in,out] parser jsmn parser * @param[in] js JSON data string * @param[in] len JSON data string length * @param[in,out] tokens pointer to memory allocated for tokens or NULL * @param[in] num_tokens number of tokens allocated * @return jsmnint number of tokens found or ERRNO */ JSMN_API jsmnint jsmn_parse(jsmnparser *parser, const char *js, const size_t len, jsmntok *tokens, const size_t num_tokens); #ifndef JSMN_HEADER #ifdef __cplusplus extern "C" { #endif /** * Allocates a fresh unused token from the token pool. */ static jsmntok *jsmn_alloc_token(jsmnparser *parser, jsmntok *tokens, const size_t num_tokens) { if (parser->toknext >= num_tokens) { return NULL; } jsmntok *tok; tok = &tokens[parser->toknext++]; tok->start = tok->end = JSMN_NEG; tok->size = 0; #ifdef JSMN_PARENT_LINKS tok->parent = JSMN_NEG; #endif #ifdef JSMN_NEXT_SIBLING tok->next_sibling = JSMN_NEG; #endif return tok; } /** * Fills token type and boundaries. */ static void jsmn_fill_token(jsmntok *token, const jsmntype type, const jsmnint start, const jsmnint end) { token->type = type; token->start = start; token->end = end; token->size = 0; } #ifdef JSMN_NEXT_SIBLING /** * Set previous child's next_sibling to current token */ static void jsmn_next_sibling(jsmnparser *parser, jsmntok *tokens) { jsmnint sibling; /* Start with parent's first child */ if (parser->toksuper != JSMN_NEG) { sibling = parser->toksuper + 1; } else { sibling = 0; } /* If the first child is the current token */ if (sibling == parser->toknext - 1) { return; } /* Loop until we find previous sibling */ while (tokens[sibling].next_sibling != JSMN_NEG) { sibling = tokens[sibling].next_sibling; } /* Set previous sibling's next_sibling to current token */ tokens[sibling].next_sibling = parser->toknext - 1; } #endif static jsmnbool is_whitespace(const char c) { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { return JSMN_TRUE; } return JSMN_FALSE; } static jsmnbool is_hexadecimal(const char c) { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { return JSMN_TRUE; } return JSMN_FALSE; } /* TODO: Confusing function name */ static jsmnbool is_character(const char c) { if ((c >= 0x20 && c <= 0x21) || (c >= 0x23 && c <= 0x5B) || (c >= 0x5D)) { return JSMN_TRUE; } return JSMN_FALSE; } static jsmnbool is_special(const char c) { if (c == '{' || c == '}' || c == '[' || c == ']' || c == '"' || c == ':' || c == ',') { return JSMN_TRUE; } return JSMN_FALSE; } /** * Fills next available token with JSON primitive. */ static jsmnint jsmn_parse_primitive(jsmnparser *parser, const char *js, const size_t len, jsmntok *tokens, const size_t num_tokens) { /* If a PRIMITIVE wasn't expected */ if (!(parser->expected & (JSMN_PRIMITIVE | JSMN_PRI_CONTINUE))) { return JSMN_ERROR_INVAL; } jsmnint pos; jsmntype type; jsmntype expected = JSMN_CLOSE; /** * Find beginning of the primitive * TODO: See if it is really necessary. * Shouldn't parser stay at the last valid state in case of an error? * In this case it should be right before primitive is parsed. */ if (!(parser->expected & JSMN_PRI_CONTINUE)) { pos = parser->pos; } else { if (tokens != NULL) { pos = tokens[parser->toknext - 1].start; } else { pos = parser->pos; while (pos != JSMN_NEG && !is_whitespace(js[pos]) && !is_special(js[pos]) && is_character(js[pos])) { pos--; } pos++; } } type = JSMN_PRIMITIVE; #ifndef JSMN_PERMISSIVE_PRIMITIVE if (js[pos] == 't' || js[pos] == 'f' || js[pos] == 'n') { char *literal = NULL; jsmnint size = 0; if (js[pos] == 't') { literal = "true"; size = 4; } else if (js[pos] == 'f') { literal = "false"; size = 5; } else if (js[pos] == 'n') { literal = "null"; size = 4; } jsmnint i; for (i = 1, pos++; i < size; i++, pos++) { if (pos == len || js[pos] == '\0') { return JSMN_ERROR_PART; } else if (js[pos] != literal[i]) { return JSMN_ERROR_INVAL; } } type |= JSMN_PRI_LITERAL; if (pos == len || js[pos] == '\0') { goto found; } } else { expected = JSMN_PRI_MINUS | JSMN_PRI_INTEGER; for (; pos < len && js[pos] != '\0'; pos++) { switch (js[pos]) { case '0': if (!(expected & JSMN_PRI_INTEGER)) { return JSMN_ERROR_INVAL; } if (type & JSMN_PRI_EXPONENT) { expected = JSMN_PRI_INTEGER | JSMN_CLOSE; } else if (type & JSMN_PRI_DECIMAL) { expected = JSMN_PRI_INTEGER | JSMN_PRI_EXPONENT | JSMN_CLOSE; } else if (parser->pos == pos || (parser->pos + 1 == pos && (type & JSMN_PRI_MINUS))) { expected = JSMN_PRI_DECIMAL | JSMN_PRI_EXPONENT | JSMN_CLOSE; } else { expected = JSMN_PRI_INTEGER | JSMN_PRI_DECIMAL | JSMN_PRI_EXPONENT | JSMN_CLOSE; } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (!(expected & JSMN_PRI_INTEGER)) { return JSMN_ERROR_INVAL; } if (type & JSMN_PRI_EXPONENT) { expected = JSMN_PRI_INTEGER | JSMN_CLOSE; } else if (type & JSMN_PRI_DECIMAL) { expected = JSMN_PRI_INTEGER | JSMN_PRI_EXPONENT | JSMN_CLOSE; } else { expected = JSMN_PRI_INTEGER | JSMN_PRI_DECIMAL | JSMN_PRI_EXPONENT | JSMN_CLOSE; } break; case '-': if (!(expected & JSMN_PRI_MINUS)) { return JSMN_ERROR_INVAL; } expected = JSMN_PRI_INTEGER; if (parser->pos == pos) { type |= JSMN_PRI_MINUS; } break; case '+': if (!(expected & JSMN_PRI_SIGN)) { return JSMN_ERROR_INVAL; } expected = JSMN_PRI_INTEGER; break; case '.': if (!(expected & JSMN_PRI_DECIMAL)) { return JSMN_ERROR_INVAL; } type |= JSMN_PRI_DECIMAL; expected = JSMN_PRI_INTEGER; break; case 'e': case 'E': if (!(expected & JSMN_PRI_EXPONENT)) { return JSMN_ERROR_INVAL; } type |= JSMN_PRI_EXPONENT; expected = JSMN_PRI_SIGN | JSMN_PRI_INTEGER; break; default: if (!(expected & JSMN_CLOSE)) { return JSMN_ERROR_INVAL; } goto check_primitive_border; } } if (!(expected & JSMN_CLOSE)) { return JSMN_ERROR_INVAL; } else { goto found; } } check_primitive_border: switch (js[pos]) { case ' ': case '\t': case '\n': case '\r': case ',': case '}': case ']': goto found; case '"': case ':': case '{': case '[': return JSMN_ERROR_INVAL; case '\0': goto found; default: return JSMN_ERROR_INVAL; } #else for (; pos < len && js[pos] != '\0'; pos++) { switch (js[pos]) { case ' ': case '\t': case '\n': case '\r': case ',': case '}': case ']': case ':': case '{': case '[': case '"': goto found; default: /* to quiet a warning from gcc */ break; } if (!is_character(js[pos])) { return JSMN_ERROR_INVAL; } } #endif found: expected = parser->expected; if (parser->toksuper != JSMN_NEG) { /* OBJECT KEY, strict query */ if ((parser->expected & (JSMN_KEY | JSMN_INSD_OBJ)) == (JSMN_KEY | JSMN_INSD_OBJ)) { parser->expected = JSMN_AFTR_OBJ_KEY; type |= JSMN_KEY | JSMN_INSD_OBJ; /* OBJECT VALUE, VALUE is implicit */ } else if (parser->expected & JSMN_INSD_OBJ) { parser->expected = JSMN_AFTR_OBJ_VAL; type |= JSMN_VALUE | JSMN_INSD_OBJ; #ifdef JSMN_PERMISSIVE /* OBJECT VALUE at the ROOT level */ } else if (parser->toksuper == JSMN_NEG) { parser->expected = JSMN_ROOT_AFTR_O; type |= JSMN_VALUE; #endif /* ARRAY VALUE, VALUE is implicit */ } else { parser->expected = JSMN_AFTR_ARR_VAL; type |= JSMN_VALUE; } } else { parser->expected = JSMN_ROOT; type |= JSMN_VALUE; } if (pos == len || js[pos] == '\0') { parser->expected |= JSMN_PRI_CONTINUE; } if (tokens == NULL) { parser->pos = pos - 1; return JSMN_SUCCESS; } jsmntok *token; if (!(expected & JSMN_PRI_CONTINUE)) { token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->expected = expected; return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, type, parser->pos, pos); } else { token = &tokens[parser->toknext - 1]; jsmn_fill_token(token, type, token->start, pos); } parser->pos = pos; #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif #ifdef JSMN_NEXT_SIBLING jsmn_next_sibling(parser, tokens); #endif if (parser->toksuper != JSMN_NEG) { if (!(expected & JSMN_PRI_CONTINUE)) { tokens[parser->toksuper].size++; } if (!(tokens[parser->toksuper].type & JSMN_CONTAINER)) { #ifdef JSMN_PARENT_LINKS parser->toksuper = tokens[parser->toksuper].parent; #else jsmnint i; for (i = parser->toksuper; i != JSMN_NEG; i--) { if (tokens[i].type & JSMN_CONTAINER && tokens[i].end == JSMN_NEG) { parser->toksuper = i; break; } } #ifdef JSMN_PERMISSIVE if (i == JSMN_NEG) { parser->toksuper = i; } #endif #endif } } parser->pos--; return JSMN_SUCCESS; } /** * Fills next token with JSON string. */ static jsmnint jsmn_parse_string(jsmnparser *parser, const char *js, const size_t len, jsmntok *tokens, const size_t num_tokens) { /* If a STRING wasn't expected */ if (!(parser->expected & JSMN_STRING)) { return JSMN_ERROR_INVAL; } if (len >= JSMN_NEG) { return JSMN_ERROR_LEN; } jsmnint pos; pos = parser->pos; /* Skip starting quote */ pos++; char c; for (; pos < len && js[pos] != '\0'; pos++) { c = js[pos]; /* Quote: end of string */ if (c == '\"') { jsmntype expected = parser->expected; jsmntype type; if (parser->toksuper != JSMN_NEG) { /* OBJECT KEY, strict query */ if ((parser->expected & (JSMN_INSD_OBJ | JSMN_KEY)) == (JSMN_INSD_OBJ | JSMN_KEY)) { parser->expected = JSMN_AFTR_OBJ_KEY; type = JSMN_STRING | JSMN_KEY | JSMN_INSD_OBJ; /* OBJECT VALUE, VALUE is implicit */ } else if (parser->expected & JSMN_INSD_OBJ) { parser->expected = JSMN_AFTR_OBJ_VAL; type = JSMN_STRING | JSMN_VALUE | JSMN_INSD_OBJ; #ifdef JSMN_PERMISSIVE /* OBJECT VALUE at the ROOT level */ } else if (parser->toksuper == JSMN_NEG) { parser->expected = JSMN_ROOT_AFTR_O; type = JSMN_STRING | JSMN_VALUE; #endif /* ARRAY VALUE, VALUE is implicit */ } else { parser->expected = JSMN_AFTR_ARR_VAL; type = JSMN_STRING | JSMN_VALUE; } } else { parser->expected = JSMN_ROOT; type = JSMN_STRING | JSMN_VALUE; } if (tokens == NULL) { parser->pos = pos; return JSMN_SUCCESS; } jsmntok *token; token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->expected = expected; return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, type, parser->pos + 1, pos); parser->pos = pos; #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif #ifdef JSMN_NEXT_SIBLING jsmn_next_sibling(parser, tokens); #endif if (parser->toksuper != JSMN_NEG) { tokens[parser->toksuper].size++; if (!(tokens[parser->toksuper].type & JSMN_CONTAINER)) { #ifdef JSMN_PARENT_LINKS parser->toksuper = tokens[parser->toksuper].parent; #else jsmnint i; for (i = parser->toksuper; i != JSMN_NEG; i--) { if (tokens[i].type & JSMN_CONTAINER && tokens[i].end == JSMN_NEG) { parser->toksuper = i; break; } } #ifdef JSMN_PERMISSIVE if (i == JSMN_NEG) { parser->toksuper = i; } #endif #endif } } return JSMN_SUCCESS; } /* Backslash: Quoted symbol expected */ if (c == '\\' && pos + 1 < len) { pos++; switch (js[pos]) { /* Allowed escaped symbols */ case '\"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': break; /* Allows escaped symbol \uXXXX */ case 'u': pos++; jsmnint i; for (i = pos + 4; pos < i; pos++) { if (pos == len || js[pos] == '\0') { return JSMN_ERROR_PART; } /* If it isn't a hex character we have an error */ if (!is_hexadecimal(js[pos])) { return JSMN_ERROR_INVAL; } } pos--; break; /* Unexpected symbol */ default: return JSMN_ERROR_INVAL; } } /* form feed, new line, carraige return, tab, and vertical tab not allowed */ else if (c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') { return JSMN_ERROR_INVAL; } } return JSMN_ERROR_PART; } static jsmnint jsmn_parse_container_open(jsmnparser *parser, const char c, jsmntok *tokens, const size_t num_tokens) { /* If an OBJECT or ARRAY wasn't expected */ if (!(parser->expected & JSMN_CONTAINER)) { return JSMN_ERROR_INVAL; } jsmntype type; if (c == '{') { parser->expected = JSMN_OPEN_OBJECT; type = JSMN_OBJECT | JSMN_VALUE; } else { parser->expected = JSMN_OPEN_ARRAY; type = JSMN_ARRAY | JSMN_VALUE; } if (tokens == NULL) { parser->toksuper++; if (parser->toksuper < (sizeof(jsmnint) * 8) && parser->expected & JSMN_INSD_OBJ) { parser->toknext |= (1 << parser->toksuper); } return JSMN_SUCCESS; } if (parser->toksuper != JSMN_NEG && tokens[parser->toksuper].type & JSMN_INSD_OBJ) { type |= JSMN_INSD_OBJ; } jsmntok *token; token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, type, parser->pos, JSMN_NEG); #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif #ifdef JSMN_NEXT_SIBLING jsmn_next_sibling(parser, tokens); #endif if (parser->toksuper != JSMN_NEG) { tokens[parser->toksuper].size++; } parser->toksuper = parser->toknext - 1; return JSMN_SUCCESS; } static jsmnint jsmn_parse_container_close(jsmnparser *parser, const char c, jsmntok *tokens) { /* If an OBJECT or ARRAY CLOSE wasn't expected */ if (!(parser->expected & JSMN_CLOSE)) { return JSMN_ERROR_INVAL; } if (tokens == NULL) { if (parser->toksuper < (sizeof(jsmnint) * 8)) { jsmntype type; type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); if ((((parser->toknext & (1 << parser->toksuper)) == 1) && !(type & JSMN_OBJECT)) || (((parser->toknext & (1 << parser->toksuper)) == 0) && !(type & JSMN_ARRAY))) { return JSMN_ERROR_UNMATCHED_BRACKETS; } parser->toknext &= ~(1 << parser->toksuper); } parser->toksuper--; } else { jsmntype type; jsmntok *token; type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); #ifdef JSMN_PERMISSIVE if (parser->toksuper == JSMN_NEG) { return JSMN_ERROR_UNMATCHED_BRACKETS; } #endif token = &tokens[parser->toksuper]; if (!(token->type & type) || token->end != JSMN_NEG) { return JSMN_ERROR_UNMATCHED_BRACKETS; } token->end = parser->pos + 1; #ifdef JSMN_PARENT_LINKS if (token->type & JSMN_INSD_OBJ) { if (tokens[token->parent].type & JSMN_CONTAINER) { parser->toksuper = token->parent; } else { parser->toksuper = tokens[token->parent].parent; } } else { parser->toksuper = token->parent; } #else jsmnint i; for (i = parser->toksuper - 1; i != JSMN_NEG; i--) { if (tokens[i].type & JSMN_CONTAINER && tokens[i].end == JSMN_NEG) { parser->toksuper = i; break; } } if (i == JSMN_NEG) { parser->toksuper = i; } #endif } if (parser->toksuper != JSMN_NEG) { parser->expected = JSMN_AFTR_CLOSE; } else { parser->expected = JSMN_ROOT; } return JSMN_SUCCESS; } static jsmnint jsmn_parse_colon(jsmnparser *parser, jsmntok *tokens) { /* If a COLON wasn't expected; strict check because it is a complex enum */ if (!((parser->expected & JSMN_COLON) == JSMN_COLON)) { return JSMN_ERROR_INVAL; } if (parser->toksuper != JSMN_NEG) { parser->expected = JSMN_AFTR_COLON; #ifdef JSMN_PERMISSIVE } else { parser->expected = JSMN_AFTR_COLON_R; #endif } if (tokens == NULL) { return JSMN_SUCCESS; } #ifdef JSMN_PERMISSIVE tokens[parser->toknext - 1].type &= ~JSMN_VALUE; tokens[parser->toknext - 1].type |= JSMN_KEY; #endif parser->toksuper = parser->toknext - 1; return JSMN_SUCCESS; } static jsmnint jsmn_parse_comma(jsmnparser *parser, jsmntok *tokens) { /* If a COMMA wasn't expected; strict check because it is a complex enum */ if (!((parser->expected & JSMN_COMMA) == JSMN_COMMA)) { return JSMN_ERROR_INVAL; } jsmntype type = JSMN_UNDEFINED; if (tokens == NULL) { if (parser->toksuper < (sizeof(jsmnint) * 8) && parser->toknext & (1 << parser->toksuper)) { type = JSMN_INSD_OBJ; } } else { if (parser->toksuper != JSMN_NEG) { type = tokens[parser->toksuper].type; } } if (parser->toksuper != JSMN_NEG) { if (type & (JSMN_OBJECT | JSMN_INSD_OBJ)) { parser->expected = JSMN_AFTR_COMMA_O; } else { parser->expected = JSMN_AFTR_COMMA_A; } #ifdef JSMN_PERMISSIVE } else { parser->expected = JSMN_AFTR_COMMA_R; #endif } if (tokens == NULL) { return JSMN_SUCCESS; } #ifdef JSMN_PERMISSIVE tokens[parser->toknext - 1].type |= JSMN_VALUE; #endif return JSMN_SUCCESS; } /** * Parse JSON string and fill tokens. */ JSMN_API jsmnint jsmn_parse(jsmnparser *parser, const char *js, const size_t len, jsmntok *tokens, const size_t num_tokens) { jsmnint r; jsmnint count = parser->toknext; char c; for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { #ifndef JSMN_MULTIPLE_JSON_FAIL if (parser->expected == JSMN_UNDEFINED) { break; } #endif c = js[parser->pos]; switch (c) { case '{': case '[': r = jsmn_parse_container_open(parser, c, tokens, num_tokens); if (r != JSMN_SUCCESS) { return r; } count++; break; case '}': case ']': r = jsmn_parse_container_close(parser, c, tokens); if (r != JSMN_SUCCESS) { return r; } break; case '\"': r = jsmn_parse_string(parser, js, len, tokens, num_tokens); if (r != JSMN_SUCCESS) { return r; } count++; break; case ':': r = jsmn_parse_colon(parser, tokens); if (r != JSMN_SUCCESS) { return r; } break; case ',': r = jsmn_parse_comma(parser, tokens); if (r != JSMN_SUCCESS) { return r; } break; /* Valid whitespace */ case ' ': case '\t': case '\n': case '\r': break; #ifndef JSMN_PERMISSIVE_PRIMITIVE /* rfc8259: 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': #else /* In permissive mode every unquoted value is a PRIMITIVE */ default: #endif r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); if (r != JSMN_SUCCESS) { return r; } count++; break; #ifndef JSMN_PERMISSIVE /* Unexpected char */ default: return JSMN_ERROR_INVAL; #endif } } if (parser->toksuper != JSMN_NEG) { return JSMN_ERROR_PART; } if (count == 0) { return JSMN_ERROR_INVAL; } while (parser->pos < len && is_whitespace(js[parser->pos])) { parser->pos++; } return count; } /** * Creates a new parser based over a given buffer with an array of tokens * available. */ JSMN_API void jsmn_init(jsmnparser *parser) { parser->pos = 0; parser->toknext = 0; parser->toksuper = JSMN_NEG; parser->expected = JSMN_ROOT_INIT; } #ifdef __cplusplus } #endif #endif /* JSMN_HEADER */ #endif /* JSMN_H */