Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mpx/lua-cjson.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Pulford <mark@kyne.com.au>2012-01-12 14:58:39 +0400
committerMark Pulford <mark@kyne.com.au>2012-03-04 12:24:34 +0400
commit418ee3fe24150c59c9afa6746aab7f09edcb894c (patch)
treee23bb79ca8f0146bcc9714658eed6da76d1b293c
parent5e36beccab38fa40b2c324c9339bbd5f1a3f24e3 (diff)
Add option to encode invalid numbers as "null"
Deprecate and replace refuse_invalid_numbers() with encode_invalid_numbers() and decode_invalid_numbers().
-rw-r--r--lua_cjson.c174
-rw-r--r--manual.txt96
-rwxr-xr-xtests/test.lua21
3 files changed, 180 insertions, 111 deletions
diff --git a/lua_cjson.c b/lua_cjson.c
index bdfa7d4..1e9e91d 100644
--- a/lua_cjson.c
+++ b/lua_cjson.c
@@ -64,14 +64,14 @@
#define DEFAULT_SPARSE_SAFE 10
#define DEFAULT_ENCODE_MAX_DEPTH 20
#define DEFAULT_DECODE_MAX_DEPTH 20
-#define DEFAULT_ENCODE_REFUSE_BADNUM 1
-#define DEFAULT_DECODE_REFUSE_BADNUM 0
+#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_REFUSE_BADNUM
-#define DEFAULT_DECODE_REFUSE_BADNUM 1
+#undef DEFAULT_DECODE_INVALID_NUMBERS
+#define DEFAULT_DECODE_INVALID_NUMBERS 0
#endif
typedef enum {
@@ -121,11 +121,11 @@ typedef struct {
int encode_sparse_ratio;
int encode_sparse_safe;
int encode_max_depth;
- int encode_refuse_badnum;
+ int encode_invalid_numbers; /* 2 => Encode as "null" */
int encode_number_precision;
int encode_keep_buffer;
- int decode_refuse_badnum;
+ int decode_invalid_numbers;
int decode_max_depth;
} json_config_t;
@@ -259,6 +259,29 @@ static int json_integer_option(lua_State *l, int *setting, int min, int max)
return 1;
}
+/* Process enumerated arguments for a configuration function */
+static int json_enum_option(lua_State *l, int *setting,
+ const char **options, int bool_value)
+{
+ static const char *bool_options[] = { "off", "on", NULL };
+
+ if (!options) {
+ options = bool_options;
+ bool_value = 1;
+ }
+
+ if (lua_gettop(l)) {
+ if (lua_isboolean(l, 1))
+ *setting = lua_toboolean(l, 1) * bool_value;
+ else
+ *setting = luaL_checkoption(l, 1, NULL, options);
+ }
+
+ lua_pushstring(l, options[*setting]);
+
+ return 1;
+}
+
/* Configures the maximum number of nested arrays/objects allowed when
* encoding */
static int json_cfg_encode_max_depth(lua_State *l)
@@ -288,18 +311,12 @@ static int json_cfg_encode_number_precision(lua_State *l)
/* Configures JSON encoding buffer persistence */
static int json_cfg_encode_keep_buffer(lua_State *l)
{
- json_config_t *cfg;
+ json_config_t *cfg = json_fetch_config(l);
int old_value;
- json_verify_arg_count(l, 1);
- cfg = json_fetch_config(l);
-
old_value = cfg->encode_keep_buffer;
- if (lua_gettop(l)) {
- luaL_checktype(l, 1, LUA_TBOOLEAN);
- cfg->encode_keep_buffer = lua_toboolean(l, 1);
- }
+ json_enum_option(l, &cfg->encode_keep_buffer, NULL, 1);
/* Init / free the buffer if the setting has changed */
if (old_value ^ cfg->encode_keep_buffer) {
@@ -309,62 +326,76 @@ static int json_cfg_encode_keep_buffer(lua_State *l)
strbuf_free(&cfg->encode_buf);
}
- lua_pushboolean(l, cfg->encode_keep_buffer);
-
return 1;
}
-/* On argument: decode enum and set config variables
- * **options must point to a NULL terminated array of 4 enums
- * Returns: current enum value */
-static void json_enum_option(lua_State *l, const char **options,
- int *opt1, int *opt2)
+static int json_cfg_encode_invalid_numbers(lua_State *l)
{
- int setting;
+ static const char *options[] = { "off", "on", "null", NULL };
+ json_config_t *cfg = json_fetch_config(l);
- if (lua_gettop(l)) {
- if (lua_isboolean(l, 1))
- setting = lua_toboolean(l, 1) * 3;
- else
- setting = luaL_checkoption(l, 1, NULL, options);
+ json_enum_option(l, &cfg->encode_invalid_numbers, options, 1);
- *opt1 = setting & 1 ? 1 : 0;
- *opt2 = setting & 2 ? 1 : 0;
- } else {
- setting = *opt1 | (*opt2 << 1);
+#if DISABLE_INVALID_NUMBERS
+ if (cfg->encode_invalid_numbers == 1) {
+ cfg->encode_invalid_numbers = 0;
+ luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported.");
}
+#endif
- if (setting)
- lua_pushstring(l, options[setting]);
- else
- lua_pushboolean(l, 0);
+ return 1;
}
+static int json_cfg_decode_invalid_numbers(lua_State *l)
+{
+ json_config_t *cfg = json_fetch_config(l);
+
+ json_enum_option(l, &cfg->decode_invalid_numbers, NULL, 1);
+
+#if DISABLE_INVALID_NUMBERS
+ if (cfg->decode_invalid_numbers) {
+ cfg->decode_invalid_numbers = 0;
+ luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported.");
+ }
+#endif
-/* When enabled, rejects: NaN, Infinity, hexadecimal numbers */
+ return 1;
+}
+
+/* When enabled, rejects: NaN, Infinity, hexadecimal numbers.
+ *
+ * This function has been deprecated and may be removed in future. */
static int json_cfg_refuse_invalid_numbers(lua_State *l)
{
- static const char *options_enc_dec[] = { "none", "encode", "decode",
- "both", NULL };
- json_config_t *cfg;
+ static const char *options[] = { "none", "encode", "decode", "both", NULL };
+ json_config_t *cfg = json_fetch_config(l);
+ int have_arg, setting;
- json_verify_arg_count(l, 1);
- cfg = json_fetch_config(l);
+ have_arg = lua_gettop(l);
- json_enum_option(l, options_enc_dec,
- &cfg->encode_refuse_badnum,
- &cfg->decode_refuse_badnum);
+ /* Map config variables to options list index */
+ setting = !cfg->encode_invalid_numbers + /* bit 0 */
+ (!cfg->decode_invalid_numbers << 1); /* bit 1 */
+
+ json_enum_option(l, &setting, options, 3);
+
+ /* Map options list index to config variables
+ *
+ * Only update the config variables when an argument has been provided.
+ * Otherwise a "null" encoding setting may inadvertently be disabled. */
+ if (have_arg) {
+ cfg->encode_invalid_numbers = !(setting & 1);
+ cfg->decode_invalid_numbers = !(setting & 2);
#if DISABLE_INVALID_NUMBERS
- /* Some non-POSIX platforms don't handle double <-> string translations
- * for Infinity/NaN/hexadecimal properly. Throw an error if the
- * user attempts to enable them. */
- if (!cfg->encode_refuse_badnum || !cfg->decode_refuse_badnum) {
- cfg->encode_refuse_badnum = cfg->decode_refuse_badnum = 1;
- luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported.");
- }
+ if (cfg->encode_invalid_numbers || cfg->decode_invalid_numbers) {
+ cfg->encode_invalid_numbers = cfg->decode_invalid_numbers = 0;
+ luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported.");
+ }
#endif
+ }
+
return 1;
}
@@ -398,8 +429,8 @@ static void json_create_config(lua_State *l)
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_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM;
- cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM;
+ 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;
@@ -589,17 +620,28 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
double num = lua_tonumber(l, lindex);
int len;
- if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num)))
- json_encode_exception(l, cfg, json, lindex, "must not be NaN or Inf");
-
- if (isnan(num)) {
- /* Some platforms may print -nan, just hard code it */
- strbuf_append_mem(json, "nan", 3);
+ if (cfg->encode_invalid_numbers == 0) {
+ /* Prevent encoding invalid numbers */
+ if (isinf(num) || isnan(num))
+ json_encode_exception(l, cfg, json, lindex, "must not be NaN or Inf");
+ } else if (cfg->encode_invalid_numbers == 1) {
+ /* Encode invalid numbers, but handle "nan" separately
+ * since some platforms may encode as "-nan". */
+ if (isnan(num)) {
+ strbuf_append_mem(json, "nan", 3);
+ return;
+ }
} else {
- strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE);
- len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision);
- strbuf_extend_length(json, len);
+ /* Encode invalid numbers as "null" */
+ if (isinf(num) || isnan(num)) {
+ strbuf_append_mem(json, "null", 4);
+ return;
+ }
}
+
+ strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE);
+ len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision);
+ strbuf_extend_length(json, len);
}
static void json_append_object(lua_State *l, json_config_t *cfg,
@@ -1046,7 +1088,7 @@ static void json_next_token(json_parse_t *json, json_token_t *token)
json_next_string_token(json, token);
return;
} else if (ch == '-' || ('0' <= ch && ch <= '9')) {
- if (json->cfg->decode_refuse_badnum && json_is_invalid_number(json)) {
+ if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) {
json_set_token_error(token, json, "invalid number");
return;
}
@@ -1066,9 +1108,9 @@ static void json_next_token(json_parse_t *json, json_token_t *token)
token->type = T_NULL;
json->ptr += 4;
return;
- } else if (!json->cfg->decode_refuse_badnum &&
+ } else if (json->cfg->decode_invalid_numbers &&
json_is_invalid_number(json)) {
- /* When refuse_badnum is disabled, only attempt to process
+ /* When decode_invalid_numbers is enabled, only attempt to process
* numbers we know are invalid JSON (Inf, NaN, hex)
* This is required to generate an appropriate token error,
* otherwise all bad tokens will register as "invalid number"
@@ -1322,6 +1364,8 @@ static int lua_cjson_new(lua_State *l)
{ "decode_max_depth", json_cfg_decode_max_depth },
{ "encode_number_precision", json_cfg_encode_number_precision },
{ "encode_keep_buffer", json_cfg_encode_keep_buffer },
+ { "encode_invalid_numbers", json_cfg_encode_invalid_numbers },
+ { "decode_invalid_numbers", json_cfg_decode_invalid_numbers },
{ "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers },
{ "new", lua_cjson_new },
{ NULL, NULL }
diff --git a/manual.txt b/manual.txt
index 13454be..dce7090 100644
--- a/manual.txt
+++ b/manual.txt
@@ -117,8 +117,10 @@ USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing ++isinf++(3).
DISABLE_CJSON_GLOBAL:: Do not store module table in global "cjson"
variable. Redundant from Lua 5.2 onwards.
DISABLE_INVALID_NUMBERS:: Recommended on platforms where ++strtod++(3) /
- ++sprintf++(3) are not POSIX compliant (Eg, Windows MinGW). Restricts
- the +cjson.refuse_invalid_numbers+ runtime configuration to +true+.
+ ++sprintf++(3) are not POSIX compliant (Eg, Windows MinGW). Prevents
+ +cjson.encode_invalid_numbers+ and +cjson.decode_invalid_numbers+
+ from being enabled. However, +cjson.encode_invalid_numbers+ may be
+ set to +"null"+.
Built-in dtoa() support
@@ -154,11 +156,12 @@ text = cjson.encode(value)
value = cjson.decode(text)
-- Get and/or set Lua CJSON configuration
-setting = cjson.refuse_invalid_numbers([setting])
+setting = cjson.decode_invalid_numbers([setting])
+setting = cjson.encode_invalid_numbers([setting])
depth = cjson.encode_max_depth([depth])
+depth = cjson.decode_max_depth([depth])
convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]])
keep = cjson.encode_keep_buffer([keep])
-depth = cjson.decode_max_depth([depth])
------------
@@ -226,7 +229,7 @@ can be compared with +cjson.null+ for convenience.
By default, numbers incompatible with the JSON specification (NaN,
Infinity, Hexadecimal) can be decoded. This default can be changed
-with +cjson.refuse_invalid_numbers+.
+with +cjson.decode_invalid_numbers+.
.Example: Decoding
[source,lua]
@@ -240,6 +243,30 @@ numeric key will be stored as a Lua +string+. Any code assuming type
+number+ may break.
+[[decode_invalid_numbers]]
+decode_invalid_numbers
+~~~~~~~~~~~~~~~~~~~~~~
+
+[source,lua]
+------------
+setting = cjson.decode_invalid_numbers([setting])
+-- "setting" must be on of:
+-- "off", "on", false, true
+------------
+
+Lua CJSON can throw an error when trying to parse numbers outside of
+the JSON specification (_invalid numbers_):
+
+- Infinity
+- Not-a-number (NaN)
+- Hexadecimal
+
+By default Lua CJSON will decode _invalid numbers_.
+
+This setting is only changed when an argument is provided. The current
+setting is always returned.
+
+
[[decode_max_depth]]
decode_max_depth
~~~~~~~~~~~~~~~~
@@ -352,7 +379,7 @@ These defaults can be changed with:
- <<encode_max_depth,+cjson.encode_max_depth+>>
- <<encode_sparse_array,+cjson.encode_sparse_array+>>
-- <<refuse_invalid_numbers,+cjson.refuse_invalid_numbers+>>
+- <<encode_invalid_numbers,+cjson.encode_invalid_numbers+>>
.Example: Encoding
[source,lua]
@@ -361,6 +388,30 @@ json_text = cjson.encode(value)
-- Returns: '[true,{"foo":"bar"}]'
+[[encode_invalid_numbers]]
+encode_invalid_numbers
+~~~~~~~~~~~~~~~~~~~~~~
+[source,lua]
+------------
+setting = cjson.encode_invalid_numbers([setting])
+-- "setting" must be on of:
+-- "off", "on", "null", false, true
+------------
+
+By default, Lua CJSON will throw an error when trying to encode
+numbers outside of the JSON specification (_invalid numbers_):
+
+- Infinity
+- Not-a-number (NaN)
+
+When set to +"null"+, Lua CJSON will encode _invalid numbers_ as a
+JSON +null+ value. This allows Infinity and NaN to be represented as
+valid JSON.
+
+This setting is only changed when an argument is provided. The current
+setting is always returned.
+
+
encode_keep_buffer
~~~~~~~~~~~~~~~~~~
@@ -473,39 +524,6 @@ cjson.encode({ [1000] = "excessively sparse" })
-- Returns: '{"1000":"excessively sparse"}'
-[[refuse_invalid_numbers]]
-refuse_invalid_numbers
-~~~~~~~~~~~~~~~~~~~~~~
-
-[source,lua]
-------------
-setting = cjson.refuse_invalid_numbers([setting])
--- "setting" must be on of:
--- false, "encode", "decode", "both", true
-------------
-
-Lua CJSON can throw an error for numbers outside of the JSON
-specification (_invalid numbers_):
-
-- Infinity
-- NaN
-- Hexadecimal
-
-By default Lua CJSON will decode _invalid numbers_, but will refuse to
-encode them.
-
-This setting is only changed when an argument is provided. The current
-setting is always returned.
-
-This setting can be configured separately for encoding and/or
-decoding:
-
-[horizontal]
-Enabled:: An error will be generated if an _invalid number_ is found.
-Disabled for encoding:: NaN and Infinity can be encoded.
-Disabled for decoding:: All numbers supported by +strtod+(3) will be parsed.
-
-
API (Variables)
---------------
diff --git a/tests/test.lua b/tests/test.lua
index 0434a38..a4ebafe 100755
--- a/tests/test.lua
+++ b/tests/test.lua
@@ -138,22 +138,28 @@ local encode_error_tests = {
{ json.encode, { function () end },
false, { "Cannot serialise function: type not supported" } },
function ()
- json.refuse_invalid_numbers("encode")
- return 'Setting refuse_invalid_numbers("encode")'
+ json.encode_invalid_numbers(false)
+ return 'Setting encode_invalid_numbers(false)'
end,
{ json.encode, { NaN },
false, { "Cannot serialise number: must not be NaN or Inf" } },
{ json.encode, { Inf },
false, { "Cannot serialise number: must not be NaN or Inf" } },
function ()
- json.refuse_invalid_numbers(false)
- return 'Setting refuse_invalid_numbers(false).'
+ json.encode_invalid_numbers("null")
+ return 'Setting encode_invalid_numbers("null").'
+ end,
+ { json.encode, { NaN }, true, { "null" } },
+ { json.encode, { Inf }, true, { "null" } },
+ function ()
+ json.encode_invalid_numbers(true)
+ return 'Setting encode_invalid_numbers(true).'
end,
{ json.encode, { NaN }, true, { "nan" } },
{ json.encode, { Inf }, true, { "inf" } },
function ()
- json.refuse_invalid_numbers("encode")
- return 'Setting refuse_invalid_numbers("encode")'
+ json.encode_invalid_numbers(false)
+ return 'Setting encode_invalid_numbers(false)'
end,
}
@@ -233,7 +239,8 @@ util.run_test_group("encode error", encode_error_tests)
util.run_test_group("escape", escape_tests)
util.run_test_group("locale", locale_tests)
-json.refuse_invalid_numbers(false)
+json.encode_invalid_numbers(true)
+json.decode_invalid_numbers(true)
json.encode_max_depth(20)
for i = 1, #arg do
util.run_test("decode cycle " .. arg[i], test_decode_cycle, { arg[i] },