From c9dbd38255d0cc2c21d99285ddb1e4ebd46aa16d Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Mon, 8 Feb 2021 21:10:10 +0200 Subject: [PATCH] Include pending library. --- module/Library/Utils/JSON.cpp | 13 + module/Library/Utils/JSON.hpp | 14 + vendor/CMakeLists.txt | 1 + vendor/JSMN/CMakeLists.txt | 5 + vendor/JSMN/include/jsmn.h | 1037 ++++++++++++++++++++++++++++ vendor/JSMN/include/jsmn_defines.h | 137 ++++ vendor/JSMN/jsmn.c | 1 + 7 files changed, 1208 insertions(+) create mode 100644 module/Library/Utils/JSON.cpp create mode 100644 module/Library/Utils/JSON.hpp create mode 100644 vendor/JSMN/CMakeLists.txt create mode 100644 vendor/JSMN/include/jsmn.h create mode 100644 vendor/JSMN/include/jsmn_defines.h create mode 100644 vendor/JSMN/jsmn.c diff --git a/module/Library/Utils/JSON.cpp b/module/Library/Utils/JSON.cpp new file mode 100644 index 00000000..acfc6eda --- /dev/null +++ b/module/Library/Utils/JSON.cpp @@ -0,0 +1,13 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Utils/JSON.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ================================================================================================ +void Register_JSON(HSQUIRRELVM vm, Table & ns) +{ + +} + +} // Namespace:: SqMod diff --git a/module/Library/Utils/JSON.hpp b/module/Library/Utils/JSON.hpp new file mode 100644 index 00000000..b811576f --- /dev/null +++ b/module/Library/Utils/JSON.hpp @@ -0,0 +1,14 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#define JSMN_HEADER +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + + +} // Namespace:: SqMod diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 7e01666e..a43b7ded 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Fmt) add_subdirectory(Squirrel) add_subdirectory(SimpleIni) add_subdirectory(TinyDir) +add_subdirectory(JSMN) set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE) add_subdirectory(MaxmindDB) diff --git a/vendor/JSMN/CMakeLists.txt b/vendor/JSMN/CMakeLists.txt new file mode 100644 index 00000000..729d3ca6 --- /dev/null +++ b/vendor/JSMN/CMakeLists.txt @@ -0,0 +1,5 @@ +# Create the JSMN library +add_library(JSMN STATIC include/jsmn.h jsmn.c) +# Library includes +target_include_directories(JSMN PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_include_directories(JSMN PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/vendor/JSMN/include/jsmn.h b/vendor/JSMN/include/jsmn.h new file mode 100644 index 00000000..7ca78637 --- /dev/null +++ b/vendor/JSMN/include/jsmn.h @@ -0,0 +1,1037 @@ +/* + * 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 */ diff --git a/vendor/JSMN/include/jsmn_defines.h b/vendor/JSMN/include/jsmn_defines.h new file mode 100644 index 00000000..c0122090 --- /dev/null +++ b/vendor/JSMN/include/jsmn_defines.h @@ -0,0 +1,137 @@ +#ifndef JSMN_DEFINES +#define JSMN_DEFINES + +/*! + * If nothing is defined, the default definitions are JSMN_PARENT_LINKS and * + * JSMN_NEXT_SIBLING with a jsmntok field size of 4 bytes (unsigned int). * + * This will parse one json object in a buffer at a time and return after a * + * successful json object parse. To check if there is more data in the * + * buffer that hasn't been parsed, run jsmn_eof. !*/ + +/*! @def JSMN_PARENT_LINKS + * @brief Adds a parent field to the token + * + * This decreases the initial time required to parse a json buffer and + * simplifies the post-processing of token array by adding a link to the id of + * a token's parent. + * This is enabled by default and highly recommended. + */ + +/*! @def JSMN_NEXT_SIBLING + * @brief Adds a next_sibling field to the token + * + * This simplifies the post-processing of token array by adding a link to the id + * of a token's next sibling. + * This is enabled by default and highly recommended. + */ + +/*! @def JSMN_UTF8 + * @brief Add UTF-8 functionality + * + * This allows for stricter parsing of json strings and also allows for the + * conversion of escaped characters (\uXXXX) to UTF-8 and back. + */ + +/*! @def JSMN_LOW_MEMORY + * @brief Enables defintions that reduce jsmn's memory footprint for small + * devices and doesn't enable definitions that increase it's footprint. + * + * This enables definitions that reduce jsmn's memory footprint at the cost of + * CPU usage. This is useful for small devices that don't parse json objects + * often and have restrictive memory requirements. + */ + +/*! @def JSMN_SHORT_TOKENS + * @brief Changes the tokens field size from a uint32_t to a uint16_t + * + * This reduces the jsmntok size by half by changing jsmntok field sizes + * from an unsigned int to an unsigned short. NOTE: This reduces the maximum + * possible json string length from 4,294,967,295 to 65,535 minus the size of + * jsmnerr. + */ + +/*! @def JSMN_PERMISSIVE + * @brief Enables all PERMISSIVE definitions + * + * Enables JSMN_PERMISSIVE_KEY, JSMN_PERMISSIVE_PRIMITIVE, and + * JSMN_MULTIPLE_JSON + */ + +/*! @def JSMN_PERMISSIVE_KEY + * @brief Allows PRIMITIVEs to be OBJECT KEYs + */ + +/*! @def JSMN_PERMISSIVE_PRIMITIVE + * @brief Allows PRIMITIVEs to be any contiguous value + * + * This allows PRIMIVITEs to be any contiguous value that does not contain a + * character that has a special meaning to json (`{}[]",:`). NOTE: There is no + * validation of JSMN_PRI_MINUS, JSNM_PRI_DECIMAL, or JSMN_PRI_EXPONENT; + * everything is the base type JSMN_PRIMITIVE. + */ + +/*! @def JSMN_MULTIPLE_JSON + * @brief Allows multiple json objects in a complete buffer + * + * This allows jsmn to parse multiple json objects in a single buffer. + * NOTE: If a single json object is malformed jsmn_parse will return with + * an error. + */ + +/*! @def JSMN_MULTIPLE_JSON_FAIL + * @brief Fails if there is more than one json object in a buffer. + */ + +#ifndef JSMN_API +# ifdef JSMN_STATIC +# define JSMN_API static +# else +# define JSMN_API extern +# endif +#endif + +#ifndef JSMN_LOW_MEMORY + +# ifndef JSMN_PARENT_LINKS +# define JSMN_PARENT_LINKS +# endif +# ifndef JSMN_NEXT_SIBLING +# define JSMN_NEXT_SIBLING +# endif + +#else + +# ifndef JSMN_SHORT_TOKENS +# define JSMN_SHORT_TOKENS +# endif + +#endif + +#ifdef JSMN_PERMISSIVE +# ifndef JSMN_PERMISSIVE_KEY +# define JSMN_PERMISSIVE_KEY +# endif +# ifndef JSMN_PERMISSIVE_PRIMITIVE +# define JSMN_PERMISSIVE_PRIMITIVE +# endif +# ifndef JSMN_MULTIPLE_JSON +# define JSMN_MULTIPLE_JSON +# endif +#endif + +#ifdef JSMN_MULTIPLE_JSON_FAIL +# undef JSMN_MULTIPLE_JSON +#endif + +#if (defined(__linux__) || defined(__APPLE__) || defined(ARDUINO)) +# define JSMN_EXPORT __attribute__((visibility("default"))) +# define JSMN_LOCAL __attribute__((visibility("hidden"))) +#elif (defined(_WIN32)) +# define JSMN_EXPORT __declspec(dllexport) +# define JSMN_LOCAL +#else +# define JSMN_EXPORT +# define JSMN_LOCAL +#endif + +#endif /* JSMN_DEFINES */ diff --git a/vendor/JSMN/jsmn.c b/vendor/JSMN/jsmn.c new file mode 100644 index 00000000..ae9c75fe --- /dev/null +++ b/vendor/JSMN/jsmn.c @@ -0,0 +1 @@ +#include \ No newline at end of file