1429 lines · 40.7 KB · C
RawDownload
/* 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.1devel"#endif /* XXX(mischief): workaround for missing isinf/isnan in ape */#define isnan isNaN#define USE_INTERNAL_ISINF /* 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 = 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;        else            *setting = luaL_checkoption(l, optindex, NULL, options);    }     if (bool_true && (*setting == 0 || *setting == bool_true))        lua_pushboolean(l, *setting);    else        lua_pushstring(l, options[*setting]);     return 1;} /* Configures handling of extremely sparse arrays: * convert: Convert extremely sparse arrays into objects? Otherwise error. * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio * safe: Always use an array when the max index <= safe */static int json_cfg_encode_sparse_array(lua_State *l){    json_config_t *cfg = json_arg_init(l, 3);     json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1);    json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX);    json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX);     return 3;} /* Configures the maximum number of nested arrays/objects allowed when * encoding */static int json_cfg_encode_max_depth(lua_State *l){    json_config_t *cfg = json_arg_init(l, 1);     return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX);} /* Configures the maximum number of nested arrays/objects allowed when * encoding */static int json_cfg_decode_max_depth(lua_State *l){    json_config_t *cfg = json_arg_init(l, 1);     return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX);} /* Configures number precision when converting doubles to text */static int json_cfg_encode_number_precision(lua_State *l){    json_config_t *cfg = json_arg_init(l, 1);     return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14);} /* Configures JSON encoding buffer persistence */static int json_cfg_encode_keep_buffer(lua_State *l){    json_config_t *cfg = json_arg_init(l, 1);    int old_value;     old_value = cfg->encode_keep_buffer;     json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1);     /* Init / free the buffer if the setting has changed */    if (old_value ^ cfg->encode_keep_buffer) {        if (cfg->encode_keep_buffer)            strbuf_init(&cfg->encode_buf, 0);        else            strbuf_free(&cfg->encode_buf);    }     return 1;} #if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV)void json_verify_invalid_number_setting(lua_State *l, int *setting){    if (*setting == 1) {        *setting = 0;        luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported.");    }}#else#define json_verify_invalid_number_setting(l, s)    do { } while(0)#endif static int json_cfg_encode_invalid_numbers(lua_State *l){    static const char *options[] = { "off", "on", "null", NULL };    json_config_t *cfg = json_arg_init(l, 1);     json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1);     json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);     return 1;} static int json_cfg_decode_invalid_numbers(lua_State *l){    json_config_t *cfg = json_arg_init(l, 1);     json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1);     json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);     return 1;} static int json_destroy_config(lua_State *l){    json_config_t *cfg;     cfg = lua_touserdata(l, 1);    if (cfg)        strbuf_free(&cfg->encode_buf);    cfg = NULL;     return 0;} static void json_create_config(lua_State *l){    json_config_t *cfg;    int i;     cfg = lua_newuserdata(l, sizeof(*cfg));     /* Create GC method to clean up strbuf */    lua_newtable(l);    lua_pushcfunction(l, json_destroy_config);    lua_setfield(l, -2, "__gc");    lua_setmetatable(l, -2);     cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT;    cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO;    cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE;    cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH;    cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH;    cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS;    cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS;    cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER;    cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; #if DEFAULT_ENCODE_KEEP_BUFFER > 0    strbuf_init(&cfg->encode_buf, 0);#endif     /* Decoding init */     /* Tag all characters as an error */    for (i = 0; i < 256; i++)        cfg->ch2token[i] = T_ERROR;     /* Set tokens that require no further processing */    cfg->ch2token['{'] = T_OBJ_BEGIN;    cfg->ch2token['}'] = T_OBJ_END;    cfg->ch2token['['] = T_ARR_BEGIN;    cfg->ch2token[']'] = T_ARR_END;    cfg->ch2token[','] = T_COMMA;    cfg->ch2token[':'] = T_COLON;    cfg->ch2token['\0'] = T_END;    cfg->ch2token[' '] = T_WHITESPACE;    cfg->ch2token['\t'] = T_WHITESPACE;    cfg->ch2token['\n'] = T_WHITESPACE;    cfg->ch2token['\r'] = T_WHITESPACE;     /* Update characters that require further processing */    cfg->ch2token['f'] = T_UNKNOWN;     /* false? */    cfg->ch2token['i'] = T_UNKNOWN;     /* inf, ininity? */    cfg->ch2token['I'] = T_UNKNOWN;    cfg->ch2token['n'] = T_UNKNOWN;     /* null, nan? */    cfg->ch2token['N'] = T_UNKNOWN;    cfg->ch2token['t'] = T_UNKNOWN;     /* true? */    cfg->ch2token['"'] = T_UNKNOWN;     /* string? */    cfg->ch2token['+'] = T_UNKNOWN;     /* number? */    cfg->ch2token['-'] = T_UNKNOWN;    for (i = 0; i < 10; i++)        cfg->ch2token['0' + i] = T_UNKNOWN;     /* Lookup table for parsing escape characters */    for (i = 0; i < 256; i++)        cfg->escape2char[i] = 0;          /* String error */    cfg->escape2char['"'] = '"';    cfg->escape2char['\\'] = '\\';    cfg->escape2char['/'] = '/';    cfg->escape2char['b'] = '\b';    cfg->escape2char['t'] = '\t';    cfg->escape2char['n'] = '\n';    cfg->escape2char['f'] = '\f';    cfg->escape2char['r'] = '\r';    cfg->escape2char['u'] = 'u';          /* Unicode parsing required */} /* ===== ENCODING ===== */ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex,                                  const char *reason){    if (!cfg->encode_keep_buffer)        strbuf_free(json);    luaL_error(l, "Cannot serialise %s: %s",                  lua_typename(l, lua_type(l, lindex)), reason);} /* json_append_string args: * - lua_State * - JSON strbuf * - String (Lua stack index) * * Returns nothing. Doesn't remove string from Lua stack */static void json_append_string(lua_State *l, strbuf_t *json, int lindex){    const char *escstr;    int i;    const char *str;    size_t len;     str = lua_tolstring(l, lindex, &len);     /* Worst case is len * 6 (all unicode escapes).     * This buffer is reused constantly for small strings     * If there are any excess pages, they won't be hit anyway.     * This gains ~5% speedup. */    strbuf_ensure_empty_length(json, len * 6 + 2);     strbuf_append_char_unsafe(json, '\"');    for (i = 0; i < len; i++) {        escstr = char2escape[(unsigned char)str[i]];        if (escstr)            strbuf_append_string(json, escstr);        else            strbuf_append_char_unsafe(json, str[i]);    }    strbuf_append_char_unsafe(json, '\"');} /* Find the size of the array on the top of the Lua stack * -1   object (not a pure array) * >=0  elements in array */static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json){    double k;    int max;    int items; 

Lines 1–500 of 1429.