/* Lua CJSON - JSON support for Lua
*
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* 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.
*/
/* Caveats:
* - JSON "null" values are represented as lightuserdata since Lua
* tables cannot contain "nil". Compare with cjson.null.
* - Invalid UTF-8 characters are not detected and will be passed
* untouched. If required, UTF-8 error checking should be done
* outside this library.
* - Javascript comments are not part of the JSON spec, and are not
* currently supported.
*
* Note: Decoding is slower than encoding. Lua spends significant
* time (30%) managing tables when parsing JSON since it is
* difficult to know object/array sizes ahead of time.
*/
#include <assert.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <lua.h>
#include <lauxlib.h>
#include "strbuf.h"
#include "fpconv.h"
#ifndef CJSON_MODNAME
#define CJSON_MODNAME "cjson"
#endif
#ifndef CJSON_VERSION
#define CJSON_VERSION "2.1.0"
#endif
/* Workaround for Solaris platforms missing isinf() */
#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF))
#define isinf(x) (!isnan(x) && isnan((x) - (x)))
#endif
#define DEFAULT_SPARSE_CONVERT 0
#define DEFAULT_SPARSE_RATIO 2
#define DEFAULT_SPARSE_SAFE 10
#define DEFAULT_ENCODE_MAX_DEPTH 1000
#define DEFAULT_DECODE_MAX_DEPTH 1000
#define DEFAULT_ENCODE_INVALID_NUMBERS 0
#define DEFAULT_DECODE_INVALID_NUMBERS 1
#define DEFAULT_ENCODE_KEEP_BUFFER 1
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
#ifdef DISABLE_INVALID_NUMBERS
#undef DEFAULT_DECODE_INVALID_NUMBERS
#define DEFAULT_DECODE_INVALID_NUMBERS 0
#endif
typedef enum {
T_OBJ_BEGIN,
T_OBJ_END,
T_ARR_BEGIN,
T_ARR_END,
T_STRING,
T_NUMBER,
T_BOOLEAN,
T_NULL,
T_COLON,
T_COMMA,
T_END,
T_WHITESPACE,
T_ERROR,
T_UNKNOWN
} json_token_type_t;
static const char *json_token_type_name[] = {
"T_OBJ_BEGIN",
"T_OBJ_END",
"T_ARR_BEGIN",
"T_ARR_END",
"T_STRING",
"T_NUMBER",
"T_BOOLEAN",
"T_NULL",
"T_COLON",
"T_COMMA",
"T_END",
"T_WHITESPACE",
"T_ERROR",
"T_UNKNOWN",
NULL
};
typedef struct {
json_token_type_t ch2token[256];
char escape2char[256]; /* Decoding */
/* encode_buf is only allocated and used when
* encode_keep_buffer is set */
strbuf_t encode_buf;
int encode_sparse_convert;
int encode_sparse_ratio;
int encode_sparse_safe;
int encode_max_depth;
int encode_invalid_numbers; /* 2 => Encode as "null" */
int encode_number_precision;
int encode_keep_buffer;
int decode_invalid_numbers;
int decode_max_depth;
} json_config_t;
typedef struct {
const char *data;
const char *ptr;
strbuf_t *tmp; /* Temporary storage for strings */
json_config_t *cfg;
int current_depth;
} json_parse_t;
typedef struct {
json_token_type_t type;
int index;
union {
const char *string;
double number;
int boolean;
} value;
int string_len;
} json_token_t;
static const char *char2escape[256] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003",
"\\u0004", "\\u0005", "\\u0006", "\\u0007",
"\\b", "\\t", "\\n", "\\u000b",
"\\f", "\\r", "\\u000e", "\\u000f",
"\\u0010", "\\u0011", "\\u0012", "\\u0013",
"\\u0014", "\\u0015", "\\u0016", "\\u0017",
"\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f",
NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
/* ===== CONFIGURATION ===== */
static json_config_t *json_fetch_config(lua_State *l)
{
json_config_t *cfg;
cfg = lua_touserdata(l, lua_upvalueindex(1));
if (!cfg)
luaL_error(l, "BUG: Unable to fetch CJSON configuration");
return cfg;
}
/* Ensure the correct number of arguments have been provided.
* Pad with nil to allow other functions to simply check arg[i]
* to find whether an argument was provided */
static json_config_t *json_arg_init(lua_State *l, int args)
{
luaL_argcheck(l, lua_gettop(l) <= args, args + 1,
"found too many arguments");
while (lua_gettop(l) < args)
lua_pushnil(l);
return json_fetch_config(l);
}
/* Process integer options for configuration functions */
static int json_integer_option(lua_State *l, int optindex, int *setting,
int min, int max)
{
char errmsg[64];
int value;
if (!lua_isnil(l, optindex)) {
value = (int)luaL_checkinteger(l, optindex);
snprintf(errmsg, sizeof(errmsg), "expected integer between %d and %d", min, max);
luaL_argcheck(l, min <= value && value <= max, 1, errmsg);
*setting = value;
}
lua_pushinteger(l, *setting);
return 1;
}
/* Process enumerated arguments for a configuration function */
static int json_enum_option(lua_State *l, int optindex, int *setting,
const char **options, int bool_true)
{
static const char *bool_options[] = { "off", "on", NULL };
if (!options) {
options = bool_options;
bool_true = 1;
}
if (!lua_isnil(l, optindex)) {
if (bool_true && lua_isboolean(l, optindex))
*setting = lua_toboolean(l, optindex) * bool_true;