From c4b72f7e4770ce1a1730e509e49d4d6504eb7613 Mon Sep 17 00:00:00 2001 From: Brian Maher Date: Fri, 23 Jan 2015 03:24:30 -0800 Subject: [PATCH 1/5] First try at win32 support. --- fpconv.c | 6 ++++++ lua_cjson.c | 6 ++++++ strbuf.c | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/fpconv.c b/fpconv.c index 79908317..cab93af6 100644 --- a/fpconv.c +++ b/fpconv.c @@ -33,6 +33,12 @@ #include #include +#ifdef _MSC_VER +# define inline __inline +#else +# define inline inline +#endif + #include "fpconv.h" /* Lua CJSON assumes the locale is the same for all threads within a diff --git a/lua_cjson.c b/lua_cjson.c index c14a1c5c..26554bcd 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -43,6 +43,12 @@ #include #include +#ifdef _MSC_VER +# define inline __inline +#else +# define inline inline +#endif + #include "strbuf.h" #include "fpconv.h" diff --git a/strbuf.c b/strbuf.c index f0f7f4b9..a339e5c1 100644 --- a/strbuf.c +++ b/strbuf.c @@ -27,6 +27,12 @@ #include #include +#ifdef _MSC_VER +# define inline __inline +#else +# define inline inline +#endif + #include "strbuf.h" static void die(const char *fmt, ...) From 43d3dfc09a06cf93011b0d747d02f34b8d06de52 Mon Sep 17 00:00:00 2001 From: Brian Maher Date: Fri, 23 Jan 2015 04:41:40 -0800 Subject: [PATCH 2/5] More fixes for visual studio. --- fpconv.c | 3 +-- lua_cjson.c | 4 ++-- strbuf.c | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fpconv.c b/fpconv.c index cab93af6..61f90280 100644 --- a/fpconv.c +++ b/fpconv.c @@ -35,8 +35,7 @@ #ifdef _MSC_VER # define inline __inline -#else -# define inline inline +# define snprintf _snprintf #endif #include "fpconv.h" diff --git a/lua_cjson.c b/lua_cjson.c index 26554bcd..ad72d952 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -45,8 +45,8 @@ #ifdef _MSC_VER # define inline __inline -#else -# define inline inline +# define snprintf _snprintf +# define strncasecmp _strnicmp #endif #include "strbuf.h" diff --git a/strbuf.c b/strbuf.c index a339e5c1..df7d7ea6 100644 --- a/strbuf.c +++ b/strbuf.c @@ -29,8 +29,6 @@ #ifdef _MSC_VER # define inline __inline -#else -# define inline inline #endif #include "strbuf.h" From 3499130c852a993e4afc741aa2aa902959775b30 Mon Sep 17 00:00:00 2001 From: Brian Maher Date: Mon, 1 Jun 2015 12:37:01 -0700 Subject: [PATCH 3/5] Allow empty array to be encoded with a metatable containing __is_cjson_array=true. --- lua_cjson.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/lua_cjson.c b/lua_cjson.c index ad72d952..22615bfa 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -500,9 +500,25 @@ static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) int max; int items; - max = 0; + max = -1; items = 0; + if ( lua_getmetatable(l, -1) ) { + int is_array, has_is_array; + + lua_pushliteral(l, "__is_cjson_array"); + lua_rawget(l, -2); + has_is_array = lua_isboolean(l, -1); + is_array = has_is_array && lua_toboolean(l, -1); + lua_pop(l, 2); + + if ( has_is_array && ! is_array ) { + return -1; + } else { + max = 0; + } + } + lua_pushnil(l); /* table, startkey */ while (lua_next(l, -2) != 0) { @@ -691,7 +707,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, current_depth++; json_check_encode_depth(l, cfg, current_depth, json); len = lua_array_length(l, cfg, json); - if (len > 0) + if (len >= 0) json_append_array(l, cfg, current_depth, json, len); else json_append_object(l, cfg, current_depth, json); @@ -1210,6 +1226,16 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) /* Handle empty arrays */ if (token.type == T_ARR_END) { + // Mark as array: + luaL_getmetatable(l, "cjson.array"); + if ( lua_isnil(l, -1) ) { + lua_pop(l, 1); + luaL_newmetatable(l, "cjson.array"); + lua_pushboolean(l, 1); + lua_setfield(l, -2, "__is_cjson_array"); + } + lua_setmetatable(l, -2); + json_decode_ascend(json); return; } From 95110717a823c2030fcdb7f9c375a11e322097b3 Mon Sep 17 00:00:00 2001 From: Brian Maher Date: Thu, 3 Sep 2015 12:33:33 -0700 Subject: [PATCH 4/5] Replace __is_cjson_array with check for __name="array" and replace lightuserdata NULL with __name="null" meatable. This will allow better interchange with other modules that need to define "array" and "null" types. Note that you can also define custom implementations of "array" and "null" if you have access to the registry. --- lua_cjson.c | 65 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/lua_cjson.c b/lua_cjson.c index 22615bfa..fcf09d52 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -503,20 +503,13 @@ static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) max = -1; items = 0; - if ( lua_getmetatable(l, -1) ) { - int is_array, has_is_array; - - lua_pushliteral(l, "__is_cjson_array"); + if (lua_getmetatable(l, -1)) { + lua_pushliteral(l, "__name"); lua_rawget(l, -2); - has_is_array = lua_isboolean(l, -1); - is_array = has_is_array && lua_toboolean(l, -1); - lua_pop(l, 2); - - if ( has_is_array && ! is_array ) { - return -1; - } else { + if (lua_isstring(l, -1) && strcmp("array", lua_tostring(l, -1)) == 0) { max = 0; } + lua_pop(l, 2); } lua_pushnil(l); @@ -704,6 +697,16 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_append_mem(json, "false", 5); break; case LUA_TTABLE: + if (lua_getmetatable(l, -1)) { + lua_pushliteral(l, "__name"); + lua_rawget(l, -2); + if (lua_isstring(l, -1) && strcmp("null", lua_tostring(l, -1)) == 0) { + strbuf_append_mem(json, "null", 4); + lua_pop(l, 2); + break; + } + lua_pop(l, 2); + } current_depth++; json_check_encode_depth(l, cfg, current_depth, json); len = lua_array_length(l, cfg, json); @@ -1227,13 +1230,7 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) /* Handle empty arrays */ if (token.type == T_ARR_END) { // Mark as array: - luaL_getmetatable(l, "cjson.array"); - if ( lua_isnil(l, -1) ) { - lua_pop(l, 1); - luaL_newmetatable(l, "cjson.array"); - lua_pushboolean(l, 1); - lua_setfield(l, -2, "__is_cjson_array"); - } + luaL_getmetatable(l, "array"); lua_setmetatable(l, -2); json_decode_ascend(json); @@ -1280,9 +1277,9 @@ static void json_process_value(lua_State *l, json_parse_t *json, break;; case T_NULL: /* In Lua, setting "t[k] = nil" will delete k from the table. - * Hence a NULL pointer lightuserdata object is used instead */ - lua_pushlightuserdata(l, NULL); - break;; + * Hence a NULL userdata object is used instead */ + luaL_getmetatable(l, "null"); + break; default: json_throw_parse_error(l, json, "value", token); } @@ -1378,6 +1375,12 @@ static int json_protect_conversion(lua_State *l) return luaL_error(l, "Memory allocation error in CJSON protected call"); } +static int json_null_tostring(lua_State *l) +{ + lua_pushliteral(l, "null"); + return 1; +} + /* Return cjson module table */ static int lua_cjson_new(lua_State *l) { @@ -1398,6 +1401,24 @@ static int lua_cjson_new(lua_State *l) /* Initialise number conversions */ fpconv_init(); + /* Initialize "null" */ + if ( luaL_newmetatable(l, "null") ) { + lua_createtable(l, 0, 2); + lua_pushliteral(l, "null"); + lua_setfield(l, -2, "__name"); + lua_pushcfunction(l, json_null_tostring); + lua_setfield(l, -2, "__tostring"); + lua_setmetatable(l, -2); + } + lua_pop(l, 1); + + /* Initialize "array" metatable */ + if ( luaL_newmetatable(l, "array") ) { + lua_pushliteral(l, "array"); + lua_setfield(l, -2, "__name"); + } + lua_pop(l, 1); + /* cjson module table */ lua_newtable(l); @@ -1406,7 +1427,7 @@ static int lua_cjson_new(lua_State *l) luaL_setfuncs(l, reg, 1); /* Set cjson.null */ - lua_pushlightuserdata(l, NULL); + luaL_getmetatable(l, "null"); lua_setfield(l, -2, "null"); /* Set module name / version fields */ From 48ab7055a1fa13a8f7e9a6242237a40f68f0f847 Mon Sep 17 00:00:00 2001 From: Brian Maher Date: Tue, 16 Aug 2016 12:59:34 -0700 Subject: [PATCH 5/5] Add new decode_big_numbers_as_strings option that defaults to false. If set to true, then any numbers that may result in a precision loss will be preserved as a string. --- lua_cjson.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/lua_cjson.c b/lua_cjson.c index fcf09d52..e4c4e1f4 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -74,6 +74,7 @@ #define DEFAULT_DECODE_INVALID_NUMBERS 1 #define DEFAULT_ENCODE_KEEP_BUFFER 1 #define DEFAULT_ENCODE_NUMBER_PRECISION 14 +#define DEFAULT_DECODE_BIG_NUMBERS_AS_STRINGS 0 #ifdef DISABLE_INVALID_NUMBERS #undef DEFAULT_DECODE_INVALID_NUMBERS @@ -133,6 +134,7 @@ typedef struct { int decode_invalid_numbers; int decode_max_depth; + int decode_big_numbers_as_strings; } json_config_t; typedef struct { @@ -362,6 +364,15 @@ static int json_cfg_decode_invalid_numbers(lua_State *l) return 1; } +static int json_cfg_decode_big_numbers_as_strings(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->decode_big_numbers_as_strings, NULL, 1); + + return 1; +} + static int json_destroy_config(lua_State *l) { json_config_t *cfg; @@ -396,6 +407,7 @@ static void json_create_config(lua_State *l) cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; + cfg->decode_big_numbers_as_strings = DEFAULT_DECODE_BIG_NUMBERS_AS_STRINGS; #if DEFAULT_ENCODE_KEEP_BUFFER > 0 strbuf_init(&cfg->encode_buf, 0); @@ -1024,12 +1036,36 @@ static int json_is_invalid_number(json_parse_t *json) return 0; } + static void json_next_number_token(json_parse_t *json, json_token_t *token) { char *endptr; - token->type = T_NUMBER; token->value.number = fpconv_strtod(json->ptr, &endptr); + if (json->ptr == endptr) { + json_set_token_error(token, json, "invalid number"); + } + + // double has 53 bit significant, therefore 2^53=9007199254740992 + // is the largest integer that can be represented. For a floating + // point value, that consumes more than 16 characters may result + // in precision loss during conversion. Note that we are being + // pessimistic in that some floating point values that consume + // 17 characters may be represented without conversion loss. + if ( json->cfg->decode_big_numbers_as_strings && + ( endptr - json->ptr > 16 || + fabs(token->value.number) > 9007199254740991.0 ) ) + { + token->type = T_STRING; + strbuf_reset(json->tmp); + for (; json->ptr != endptr; json->ptr++ ) { + strbuf_append_char_unsafe(json->tmp, json->ptr[0]); + } + token->value.string = strbuf_string(json->tmp, &token->string_len); + return; + } + token->type = T_NUMBER; + if (json->ptr == endptr) json_set_token_error(token, json, "invalid number"); else @@ -1394,6 +1430,7 @@ static int lua_cjson_new(lua_State *l) { "encode_keep_buffer", json_cfg_encode_keep_buffer }, { "encode_invalid_numbers", json_cfg_encode_invalid_numbers }, { "decode_invalid_numbers", json_cfg_decode_invalid_numbers }, + { "decode_big_numbers_as_strings", json_cfg_decode_big_numbers_as_strings }, { "new", lua_cjson_new }, { NULL, NULL } };