1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Replace JSMN with SAJSON.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Sandu Liviu Catalin 2021-07-16 20:42:34 +03:00
parent e685b3ffe0
commit d79f292729
11 changed files with 2702 additions and 1392 deletions

View File

@ -133,7 +133,7 @@ if(WIN32 OR MINGW)
target_link_libraries(SqModule wsock32 ws2_32 shlwapi) target_link_libraries(SqModule wsock32 ws2_32 shlwapi)
endif() endif()
# Link to base libraries # Link to base libraries
target_link_libraries(SqModule Squirrel fmt::fmt SimpleINI TinyDir ConcurrentQueue JSMN CPR PUGIXML maxminddb libzmq-static) target_link_libraries(SqModule Squirrel fmt::fmt SimpleINI TinyDir ConcurrentQueue SAJSON CPR PUGIXML maxminddb libzmq-static)
# Link to POCO libraries # Link to POCO libraries
target_link_libraries(SqModule Poco::Foundation Poco::Crypto Poco::Data Poco::Net Poco::JSON Poco::XML) target_link_libraries(SqModule Poco::Foundation Poco::Crypto Poco::Data Poco::Net Poco::JSON Poco::XML)
# Does POCO have SQLite support? # Does POCO have SQLite support?

View File

@ -17,90 +17,76 @@ static SQInteger SqToJSON(HSQUIRRELVM vm) noexcept
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const char * str, jsmntok * t, size_t count) noexcept static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const sajson::value & node) noexcept
{ {
// Are there any elements to process? // Operation result
if (count == 0) SQInteger r = SQ_OK;
// Identify element type
switch (node.get_type())
{ {
// We still need something on the stack case sajson::TYPE_INTEGER: {
sq_pushnull(vm); sq_pushinteger(vm, static_cast< SQInteger >(node.get_integer_value()));
// No token consumed } break;
return 0; case sajson::TYPE_DOUBLE: {
} sq_pushfloat(vm, static_cast< SQFloat >(node.get_double_value()));
// Is this a primitive type? } break;
else if (t->type & JSMN_PRIMITIVE) case sajson::TYPE_NULL: {
{
// Primitive length (in characters)
const jsmnint l = (t->end - t->start);
// Primitive start (character offset)
const char * v = (str + t->start);
// Is this a floating point?
if (memchr(v, '.', l) || memchr(v, 'e', l) || memchr(v, 'E', l))
{
sq_pushfloat(vm, ConvNum< SQFloat >::FromStr(v));
}
// Is this an integer?
else if (((v[0] >= '0') && (v[0] <= '9')) || (v[0] == '-') || (v[0] == '+'))
{
sq_pushinteger(vm, ConvNum< SQInteger >::FromStr(v));
}
// Is this a boolean true?
else if (v[0] == 't')
{
sq_pushbool(vm, SQTrue);
}
// Is this a boolean false?
else if (v[0] == 'f')
{
sq_pushbool(vm, SQFalse);
}
// Is this null?
else if (v[0] == 'n')
{
sq_pushnull(vm); sq_pushnull(vm);
} } break;
// Should never really get here because it should be sanitized by the JSON parser case sajson::TYPE_FALSE: {
// But doesn't hurt to have it here in case something out of our scope goes wrong sq_pushbool(vm, SQFalse);
else } break;
{ case sajson::TYPE_TRUE: {
return sq_throwerrorf(vm, _SC("Unrecognized JSON primitive: '%.*s'"), l, v); sq_pushbool(vm, SQTrue);
} } break;
// One token was consumed case sajson::TYPE_STRING: {
return 1; sq_pushstring(vm, node.as_cstring(), static_cast< SQInteger >(node.get_string_length()));
} } break;
// Is this a string? case sajson::TYPE_ARRAY: {
else if (t->type & JSMN_STRING) // Array length
{ const size_t n = node.get_length();
sq_pushstring(vm, (str + t->start), static_cast< SQInteger >(t->end - t->start)); // Create a new array on the stack
// One token was consumed sq_newarrayex(vm, static_cast< SQInteger >(n));
return 1; // Process array elements
} for (size_t i = 0; i < n; ++i)
// Is this an object?
else if (t->type & JSMN_OBJECT)
{
// Number of tokens consumed by this object
SQInteger c = 0, r = SQ_OK;
// Create a new table on the stack
sq_newtableex(vm, static_cast< SQInteger >(t->size));
// Process object elements
for (jsmnint i = 0; i < t->size; i++)
{
// Locate key token relative to the current token
jsmntok * k = (t + 1 + c);
// Transform the key into a script object on the stack
r = SqFromJson_Push(vm, str, k, count - c);
// Did we fail?
if (SQ_FAILED(r))
{
break; // Abort
}
// Update consumed tokens
c += r;
// Does the key have an associated value?
if (k->size > 0)
{ {
// Transform the value into a script object on the stack // Transform the value into a script object on the stack
r = SqFromJson_Push(vm, str, (t + 1 + c), count - c); r = SqFromJson_Push(vm, node.get_array_element(i));
// Did we fail?
if (SQ_FAILED(r))
{
break; // Abort
}
// At this point we have a value on the stack
r = sq_arrayappend(vm, -2);
// Did we fail?
if (SQ_FAILED(r))
{
// Discard the value
sq_poptop(vm);
// Abort
break;
}
}
// Anything bad happened?
if (SQ_FAILED(r))
{
sq_poptop(vm); // Discard the array
}
} break;
case sajson::TYPE_OBJECT: {
// Object length
const size_t n = node.get_length();
// Create a new table on the stack
sq_newtableex(vm, static_cast< SQInteger >(n));
//
for (size_t i = 0; i < n; ++i)
{
const auto k = node.get_object_key(i);
// Transform the key into a script object on the stack
sq_pushstring(vm, k.data(), static_cast< SQInteger >(k.length()));
// Transform the value into a script object on the stack
r = SqFromJson_Push(vm, node.get_object_value(i));
// Did we fail? // Did we fail?
if (SQ_FAILED(r)) if (SQ_FAILED(r))
{ {
@ -109,79 +95,30 @@ static SQInteger SqFromJson_Push(HSQUIRRELVM vm, const char * str, jsmntok * t,
// Abort // Abort
break; break;
} }
// Update consumed tokens // At this point we have a key and a value on the stack
c += r; r = sq_newslot(vm, -3, SQFalse);
// Did we fail?
if (SQ_FAILED(r))
{
// Discard the key/value pair
sq_pop(vm, 2);
// Abort
break;
}
} }
else // Anything bad happened?
{
sq_pushnull(vm); // Default to null because a value must exist
}
// At this point we have a key and a value on the stack
r = sq_newslot(vm, -3, SQFalse);
// Did we fail?
if (SQ_FAILED(r)) if (SQ_FAILED(r))
{ {
// Discard the key/value pair sq_poptop(vm); // Discard the table
sq_pop(vm, 2);
// Abort
break;
} }
} } break;
// Anything bad happened? default:
if (SQ_FAILED(r)) // Should never really get here because it should be sanitized by the JSON parser
{ // But doesn't hurt to have it here in case something out of our scope goes wrong
// Discard the table r = sq_throwerror(vm, _SC("Unrecognized JSON type"));
sq_poptop(vm);
// Propagate the error
return r;
}
// Return consumed tokens
return c + 1;
} }
// Is this an array? // Return the result
else if (t->type & JSMN_ARRAY) return r;
{
// Number of tokens consumed by this array
SQInteger c = 0, r = SQ_OK;
// Create a new array on the stack
sq_newarrayex(vm, static_cast< SQInteger >(t->size));
// Process array elements
for (jsmnint i = 0; i < t->size; i++)
{
// Transform the value into a script object on the stack
r = SqFromJson_Push(vm, str, (t + 1 + c), count - c);
// Did we fail?
if (SQ_FAILED(r))
{
break; // Abort
}
// Update consumed tokens
c += r;
// At this point we have a value on the stack
r = sq_arrayappend(vm, -2);
// Did we fail?
if (SQ_FAILED(r))
{
// Discard the value
sq_poptop(vm);
// Abort
break;
}
}
// Anything bad happened?
if (SQ_FAILED(r))
{
// Discard the array
sq_poptop(vm);
// Propagate the error
return r;
}
// Return consumed tokens
return c + 1;
}
// Should never really get here because it should be sanitized by the JSON parser
// But doesn't hurt to have it here in case something out of our scope goes wrong
return sq_throwerror(vm, _SC("Unrecognized JSON type"));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -201,45 +138,17 @@ static SQInteger SqFromJSON(HSQUIRRELVM vm) noexcept
{ {
return s.mRes; // Propagate the error return s.mRes; // Propagate the error
} }
// Parser context
jsmnparser p;
// Initialize parser
jsmn_init(&p);
// Estimate the number of tokens necessary to parse the specified JSON string
jsmnint r = jsmn_parse(&p, s.mPtr, static_cast< size_t >(s.mLen), nullptr, 0);
// Is there anything to parse?
if (r == 0)
{
// Default to null
sq_pushnull(vm);
// A value was returned
return 1;
}
// See if there was an error
switch (r)
{
case jsmnint(JSMN_ERROR_NOMEM):
return sq_throwerror(vm, _SC("Not enough token memory was provided"));
case jsmnint(JSMN_ERROR_LEN):
return sq_throwerror(vm, _SC("Input data too long"));
case jsmnint(JSMN_ERROR_INVAL):
return sq_throwerror(vm, _SC("Invalid character inside JSON string"));
case jsmnint(JSMN_ERROR_PART):
return sq_throwerror(vm, _SC("The string is not a full JSON packet, more bytes expected"));
case jsmnint(JSMN_ERROR_UNMATCHED_BRACKETS):
return sq_throwerror(vm, _SC("The JSON string has unmatched brackets"));
default: break; // Nothing bad happened
}
// Initialize the token array
std::vector< jsmntok > tks(static_cast< size_t >(r) + 16);
// Initialize parser
jsmn_init(&p);
// Attempt to parse the specified JSON string // Attempt to parse the specified JSON string
r = jsmn_parse(&p, s.mPtr, static_cast< size_t >(s.mLen), tks.data(), tks.size()); const sajson::document & document = sajson::parse(sajson::dynamic_allocation(), sajson::string(s.mPtr, static_cast<size_t>(s.mLen)));
// Process the tokens that were parsed from the string // See if there was an error
SQInteger res = SqFromJson_Push(vm, s.mPtr, tks.data(), p.toknext); if (!document.is_valid())
{
return sq_throwerror(vm, document.get_error_message_as_cstring());
}
// Process the nodes that were parsed from the string
SQInteger r = SqFromJson_Push(vm, document.get_root());
// We either have a value to return or we propagate some error // We either have a value to return or we propagate some error
return SQ_SUCCEEDED(res) ? 1 : res; return SQ_SUCCEEDED(r) ? 1 : r;
} }
// ================================================================================================ // ================================================================================================

View File

@ -5,9 +5,7 @@
#include "Library/IO/Buffer.hpp" #include "Library/IO/Buffer.hpp"
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
extern "C" { #include <sajson.h>
#include <jsmn.h>
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
namespace SqMod { namespace SqMod {

View File

@ -3,7 +3,7 @@ add_subdirectory(Fmt)
add_subdirectory(Squirrel) add_subdirectory(Squirrel)
add_subdirectory(SimpleIni) add_subdirectory(SimpleIni)
add_subdirectory(TinyDir) add_subdirectory(TinyDir)
add_subdirectory(JSMN) add_subdirectory(SAJSON)
add_subdirectory(CPR) add_subdirectory(CPR)
add_subdirectory(PUGIXML) add_subdirectory(PUGIXML)
set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE) set(BUILD_TESTING OFF CACHE INTERNAL "" FORCE)

View File

@ -1,7 +0,0 @@
# 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)
# Compile time options
target_compile_definitions(JSMN PUBLIC JSMN_UTF8=1)

View File

@ -1,202 +0,0 @@
/*
* 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>
#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);
#endif /* JSMN_H */

View File

@ -1,137 +0,0 @@
#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 */

857
vendor/JSMN/jsmn.c vendored
View File

@ -1,857 +0,0 @@
/*
* 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.
*/
#include "jsmn.h"
#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

5
vendor/SAJSON/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,5 @@
# Create the SAJSON library
add_library(SAJSON STATIC include/sajson.h sajson.cpp)
# Library includes
target_include_directories(SAJSON PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(SAJSON PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)

2600
vendor/SAJSON/include/sajson.h vendored Normal file

File diff suppressed because it is too large Load Diff

1
vendor/SAJSON/sajson.cpp vendored Normal file
View File

@ -0,0 +1 @@
#include <sajson.h>