diff options
| author | Mark Pulford <mark@kyne.com.au> | 2011-05-07 21:01:21 +0930 |
|---|---|---|
| committer | Mark Pulford <mark@kyne.com.au> | 2011-05-07 21:01:21 +0930 |
| commit | 3d014d14bcf3dce3e5dbb9c193d689e46d333798 (patch) | |
| tree | ced2ed81700778d505b5992f15af8fb5659a44ff | |
| parent | d75e1cb7f0418615e208f49c6209b7c144591f14 (diff) | |
| download | lua-cjson-3d014d14bcf3dce3e5dbb9c193d689e46d333798.tar.gz lua-cjson-3d014d14bcf3dce3e5dbb9c193d689e46d333798.tar.bz2 lua-cjson-3d014d14bcf3dce3e5dbb9c193d689e46d333798.zip | |
Rework runtime config options
- Rename API for consistency:
- sparse_ratio() -> encode_sparse_array()
- max_depth() -> encode_max_depth()
- invalid_numbers() -> refuse_invalid_numbers()
- Adjust sparse array handling:
- Add "safe" option to allow small sparse arrays regardless of the
ratio.
- Generate an error by default instead of converting an array into an
object (POLA).
- Update invalid number handling:
- Allow decoding invalid numbers by default since many JSON
implementations output NaN/Infinity.
- Throw an error by default when attempting to encode NaN/Infinity
since the RFC explicitly states it is not permitted.
- Support specifying invalid number configuration separately for
encode/decode.
Diffstat (limited to '')
| -rw-r--r-- | lua_cjson.c | 174 |
1 files changed, 118 insertions, 56 deletions
diff --git a/lua_cjson.c b/lua_cjson.c index 8727364..3af8157 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -43,8 +43,12 @@ | |||
| 43 | 43 | ||
| 44 | #include "strbuf.h" | 44 | #include "strbuf.h" |
| 45 | 45 | ||
| 46 | #define DEFAULT_SPARSE_CONVERT 0 | ||
| 46 | #define DEFAULT_SPARSE_RATIO 2 | 47 | #define DEFAULT_SPARSE_RATIO 2 |
| 48 | #define DEFAULT_SPARSE_SAFE 10 | ||
| 47 | #define DEFAULT_MAX_DEPTH 20 | 49 | #define DEFAULT_MAX_DEPTH 20 |
| 50 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 | ||
| 51 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 | ||
| 48 | 52 | ||
| 49 | typedef enum { | 53 | typedef enum { |
| 50 | T_OBJ_BEGIN, | 54 | T_OBJ_BEGIN, |
| @@ -89,10 +93,14 @@ typedef struct { | |||
| 89 | char *char2escape[256]; /* Encoding */ | 93 | char *char2escape[256]; /* Encoding */ |
| 90 | #endif | 94 | #endif |
| 91 | strbuf_t encode_buf; | 95 | strbuf_t encode_buf; |
| 92 | int sparse_ratio; | ||
| 93 | int max_depth; | ||
| 94 | int current_depth; | 96 | int current_depth; |
| 95 | int strict_numbers; | 97 | |
| 98 | int encode_sparse_convert; | ||
| 99 | int encode_sparse_ratio; | ||
| 100 | int encode_sparse_safe; | ||
| 101 | int encode_max_depth; | ||
| 102 | int encode_refuse_badnum; | ||
| 103 | int decode_refuse_badnum; | ||
| 96 | } json_config_t; | 104 | } json_config_t; |
| 97 | 105 | ||
| 98 | typedef struct { | 106 | typedef struct { |
| @@ -171,66 +179,107 @@ static json_config_t *json_fetch_config(lua_State *l) | |||
| 171 | return cfg; | 179 | return cfg; |
| 172 | } | 180 | } |
| 173 | 181 | ||
| 174 | /* Checks whether a config variable needs to be updated. | 182 | static void json_verify_arg_count(lua_State *l, int args) |
| 175 | * Also return cfg pointer */ | ||
| 176 | static int cfg_update_requested(lua_State *l, json_config_t **cfg) | ||
| 177 | { | 183 | { |
| 178 | int args; | 184 | luaL_argcheck(l, lua_gettop(l) <= args, args + 1, |
| 185 | "found too many arguments"); | ||
| 186 | } | ||
| 179 | 187 | ||
| 180 | args = lua_gettop(l); | 188 | /* Configures handling of extremely sparse arrays: |
| 181 | luaL_argcheck(l, args <= 1, 2, "found too many arguments"); | 189 | * convert: Convert extremely sparse arrays into objects? Otherwise error. |
| 190 | * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio | ||
| 191 | * safe: Always use an array when the max index <= safe */ | ||
| 192 | static int json_cfg_encode_sparse_array(lua_State *l) | ||
| 193 | { | ||
| 194 | json_config_t *cfg; | ||
| 195 | int val; | ||
| 182 | 196 | ||
| 183 | *cfg = json_fetch_config(l); | 197 | json_verify_arg_count(l, 3); |
| 198 | cfg = json_fetch_config(l); | ||
| 184 | 199 | ||
| 185 | return args; | 200 | switch (lua_gettop(l)) { |
| 201 | case 3: | ||
| 202 | val = luaL_checkinteger(l, 3); | ||
| 203 | luaL_argcheck(l, val >= 0, 3, "expected integer >= 0"); | ||
| 204 | cfg->encode_sparse_safe = val; | ||
| 205 | case 2: | ||
| 206 | val = luaL_checkinteger(l, 2); | ||
| 207 | luaL_argcheck(l, val >= 0, 2, "expected integer >= 0"); | ||
| 208 | cfg->encode_sparse_ratio = val; | ||
| 209 | case 1: | ||
| 210 | luaL_argcheck(l, lua_isboolean(l, 1), 1, "expected boolean"); | ||
| 211 | cfg->encode_sparse_convert = lua_toboolean(l, 1); | ||
| 212 | } | ||
| 213 | |||
| 214 | lua_pushboolean(l, cfg->encode_sparse_convert); | ||
| 215 | lua_pushinteger(l, cfg->encode_sparse_ratio); | ||
| 216 | lua_pushinteger(l, cfg->encode_sparse_safe); | ||
| 217 | |||
| 218 | return 3; | ||
| 186 | } | 219 | } |
| 187 | 220 | ||
| 188 | static int json_sparse_ratio(lua_State *l) | 221 | /* Configures the maximum number of nested arrays/objects allowed when |
| 222 | * encoding */ | ||
| 223 | static int json_cfg_encode_max_depth(lua_State *l) | ||
| 189 | { | 224 | { |
| 190 | json_config_t *cfg; | 225 | json_config_t *cfg; |
| 191 | int sparse_ratio; | 226 | int depth; |
| 227 | |||
| 228 | json_verify_arg_count(l, 1); | ||
| 229 | cfg = json_fetch_config(l); | ||
| 192 | 230 | ||
| 193 | if (cfg_update_requested(l, &cfg)) { | 231 | if (lua_gettop(l)) { |
| 194 | sparse_ratio = luaL_checkinteger(l, 1); | 232 | depth = luaL_checkinteger(l, 1); |
| 195 | luaL_argcheck(l, sparse_ratio >= 0, 1, | 233 | luaL_argcheck(l, depth > 0, 1, "expected positive integer"); |
| 196 | "expected zero or positive integer"); | 234 | cfg->encode_max_depth = depth; |
| 197 | cfg->sparse_ratio = sparse_ratio; | ||
| 198 | } | 235 | } |
| 199 | 236 | ||
| 200 | lua_pushinteger(l, cfg->sparse_ratio); | 237 | lua_pushinteger(l, cfg->encode_max_depth); |
| 201 | 238 | ||
| 202 | return 1; | 239 | return 1; |
| 203 | } | 240 | } |
| 204 | 241 | ||
| 205 | static int json_max_depth(lua_State *l) | 242 | |
| 243 | /* On argument: decode enum and set config variables | ||
| 244 | * **options must point to a NULL terminated array of 4 enums | ||
| 245 | * Returns: current enum value */ | ||
| 246 | static void json_enum_option(lua_State *l, const char **options, | ||
| 247 | int *opt1, int *opt2) | ||
| 206 | { | 248 | { |
| 207 | json_config_t *cfg; | 249 | int setting; |
| 208 | int max_depth; | ||
| 209 | 250 | ||
| 210 | if (cfg_update_requested(l, &cfg)) { | 251 | if (lua_gettop(l)) { |
| 211 | max_depth = luaL_checkinteger(l, 1); | 252 | if (lua_isboolean(l, 1)) |
| 212 | luaL_argcheck(l, max_depth > 0, 1, "expected positive integer"); | 253 | setting = lua_toboolean(l, 1) * 3; |
| 213 | cfg->max_depth = max_depth; | 254 | else |
| 214 | } | 255 | setting = luaL_checkoption(l, 1, NULL, options); |
| 215 | 256 | ||
| 216 | lua_pushinteger(l, cfg->max_depth); | 257 | *opt1 = setting & 1 ? 1 : 0; |
| 258 | *opt2 = setting & 2 ? 1 : 0; | ||
| 259 | } else { | ||
| 260 | setting = *opt1 | (*opt2 << 1); | ||
| 261 | } | ||
| 217 | 262 | ||
| 218 | return 1; | 263 | if (setting) |
| 264 | lua_pushstring(l, options[setting]); | ||
| 265 | else | ||
| 266 | lua_pushboolean(l, 0); | ||
| 219 | } | 267 | } |
| 220 | 268 | ||
| 221 | /* When disabled, supports: | 269 | |
| 222 | * - encoding/decoding NaN/Infinity. | 270 | /* When enabled, rejects: NaN, Infinity, hexidecimal numbers */ |
| 223 | * - decoding hexidecimal numbers. */ | 271 | static int json_cfg_refuse_invalid_numbers(lua_State *l) |
| 224 | static int json_strict_numbers(lua_State *l) | ||
| 225 | { | 272 | { |
| 273 | static const char *options_enc_dec[] = { "none", "encode", "decode", | ||
| 274 | "both", NULL }; | ||
| 226 | json_config_t *cfg; | 275 | json_config_t *cfg; |
| 227 | 276 | ||
| 228 | if (cfg_update_requested(l, &cfg)) { | 277 | json_verify_arg_count(l, 1); |
| 229 | luaL_argcheck(l, lua_isboolean(l, 1), 1, "expected boolean"); | 278 | cfg = json_fetch_config(l); |
| 230 | cfg->strict_numbers = lua_toboolean(l, 1); | ||
| 231 | } | ||
| 232 | 279 | ||
| 233 | lua_pushboolean(l, cfg->strict_numbers); | 280 | json_enum_option(l, options_enc_dec, |
| 281 | &cfg->encode_refuse_badnum, | ||
| 282 | &cfg->decode_refuse_badnum); | ||
| 234 | 283 | ||
| 235 | return 1; | 284 | return 1; |
| 236 | } | 285 | } |
| @@ -262,9 +311,12 @@ static void json_create_config(lua_State *l) | |||
| 262 | 311 | ||
| 263 | strbuf_init(&cfg->encode_buf, 0); | 312 | strbuf_init(&cfg->encode_buf, 0); |
| 264 | 313 | ||
| 265 | cfg->sparse_ratio = DEFAULT_SPARSE_RATIO; | 314 | cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; |
| 266 | cfg->max_depth = DEFAULT_MAX_DEPTH; | 315 | cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; |
| 267 | cfg->strict_numbers = 1; | 316 | cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; |
| 317 | cfg->encode_max_depth = DEFAULT_MAX_DEPTH; | ||
| 318 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; | ||
| 319 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; | ||
| 268 | 320 | ||
| 269 | /* Decoding init */ | 321 | /* Decoding init */ |
| 270 | 322 | ||
| @@ -382,7 +434,7 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | |||
| 382 | * -1 object (not a pure array) | 434 | * -1 object (not a pure array) |
| 383 | * >=0 elements in array | 435 | * >=0 elements in array |
| 384 | */ | 436 | */ |
| 385 | static int lua_array_length(lua_State *l, int sparse_ratio) | 437 | static int lua_array_length(lua_State *l, json_config_t *cfg) |
| 386 | { | 438 | { |
| 387 | double k; | 439 | double k; |
| 388 | int max; | 440 | int max; |
| @@ -413,8 +465,14 @@ static int lua_array_length(lua_State *l, int sparse_ratio) | |||
| 413 | } | 465 | } |
| 414 | 466 | ||
| 415 | /* Encode very sparse arrays as objects (if enabled) */ | 467 | /* Encode very sparse arrays as objects (if enabled) */ |
| 416 | if (sparse_ratio > 0 && max > items * sparse_ratio) | 468 | if (cfg->encode_sparse_ratio > 0 && |
| 469 | max > items * cfg->encode_sparse_ratio && | ||
| 470 | max > cfg->encode_sparse_safe) { | ||
| 471 | if (!cfg->encode_sparse_convert) | ||
| 472 | json_encode_exception(l, -1, "excessively sparse array"); | ||
| 473 | |||
| 417 | return -1; | 474 | return -1; |
| 475 | } | ||
| 418 | 476 | ||
| 419 | return max; | 477 | return max; |
| 420 | } | 478 | } |
| @@ -423,7 +481,7 @@ static void json_encode_descend(lua_State *l, json_config_t *cfg) | |||
| 423 | { | 481 | { |
| 424 | cfg->current_depth++; | 482 | cfg->current_depth++; |
| 425 | 483 | ||
| 426 | if (cfg->current_depth > cfg->max_depth) { | 484 | if (cfg->current_depth > cfg->encode_max_depth) { |
| 427 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", | 485 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", |
| 428 | cfg->current_depth); | 486 | cfg->current_depth); |
| 429 | } | 487 | } |
| @@ -462,11 +520,11 @@ static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json, | |||
| 462 | } | 520 | } |
| 463 | 521 | ||
| 464 | static void json_append_number(lua_State *l, strbuf_t *json, int index, | 522 | static void json_append_number(lua_State *l, strbuf_t *json, int index, |
| 465 | int strict) | 523 | int refuse_badnum) |
| 466 | { | 524 | { |
| 467 | double num = lua_tonumber(l, index); | 525 | double num = lua_tonumber(l, index); |
| 468 | 526 | ||
| 469 | if (strict && (isinf(num) || isnan(num))) | 527 | if (refuse_badnum && (isinf(num) || isnan(num))) |
| 470 | json_encode_exception(l, index, "must not be NaN or Inf"); | 528 | json_encode_exception(l, index, "must not be NaN or Inf"); |
| 471 | 529 | ||
| 472 | strbuf_append_number(json, num); | 530 | strbuf_append_number(json, num); |
| @@ -497,7 +555,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
| 497 | /* Can't just use json_append_string() below since it would | 555 | /* Can't just use json_append_string() below since it would |
| 498 | * convert the value in the callers data structure. */ | 556 | * convert the value in the callers data structure. */ |
| 499 | strbuf_append_char(json, '"'); | 557 | strbuf_append_char(json, '"'); |
| 500 | json_append_number(l, json, -2, cfg->strict_numbers); | 558 | json_append_number(l, json, -2, cfg->encode_refuse_badnum); |
| 501 | strbuf_append_mem(json, "\": ", 3); | 559 | strbuf_append_mem(json, "\": ", 3); |
| 502 | } else if (keytype == LUA_TSTRING) { | 560 | } else if (keytype == LUA_TSTRING) { |
| 503 | json_append_string(l, json, -2); | 561 | json_append_string(l, json, -2); |
| @@ -529,7 +587,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
| 529 | json_append_string(l, json, -1); | 587 | json_append_string(l, json, -1); |
| 530 | break; | 588 | break; |
| 531 | case LUA_TNUMBER: | 589 | case LUA_TNUMBER: |
| 532 | json_append_number(l, json, -1, cfg->strict_numbers); | 590 | json_append_number(l, json, -1, cfg->encode_refuse_badnum); |
| 533 | break; | 591 | break; |
| 534 | case LUA_TBOOLEAN: | 592 | case LUA_TBOOLEAN: |
| 535 | if (lua_toboolean(l, -1)) | 593 | if (lua_toboolean(l, -1)) |
| @@ -538,7 +596,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
| 538 | strbuf_append_mem(json, "false", 5); | 596 | strbuf_append_mem(json, "false", 5); |
| 539 | break; | 597 | break; |
| 540 | case LUA_TTABLE: | 598 | case LUA_TTABLE: |
| 541 | len = lua_array_length(l, cfg->sparse_ratio); | 599 | len = lua_array_length(l, cfg); |
| 542 | if (len > 0) | 600 | if (len > 0) |
| 543 | json_append_array(l, cfg, json, len); | 601 | json_append_array(l, cfg, json, len); |
| 544 | else | 602 | else |
| @@ -566,6 +624,8 @@ static int json_encode(lua_State *l) | |||
| 566 | char *json; | 624 | char *json; |
| 567 | int len; | 625 | int len; |
| 568 | 626 | ||
| 627 | /* Can't use json_verify_arg_count() since we need to ensure | ||
| 628 | * there is only 1 argument */ | ||
| 569 | luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); | 629 | luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); |
| 570 | 630 | ||
| 571 | cfg = json_fetch_config(l); | 631 | cfg = json_fetch_config(l); |
| @@ -853,7 +913,7 @@ static void json_next_token(json_parse_t *json, json_token_t *token) | |||
| 853 | json_next_string_token(json, token); | 913 | json_next_string_token(json, token); |
| 854 | return; | 914 | return; |
| 855 | } else if (ch == '-' || ('0' <= ch && ch <= '9')) { | 915 | } else if (ch == '-' || ('0' <= ch && ch <= '9')) { |
| 856 | if (json->cfg->strict_numbers && json_is_invalid_number(json)) { | 916 | if (json->cfg->decode_refuse_badnum && json_is_invalid_number(json)) { |
| 857 | json_set_token_error(token, json, "invalid number"); | 917 | json_set_token_error(token, json, "invalid number"); |
| 858 | return; | 918 | return; |
| 859 | } | 919 | } |
| @@ -873,8 +933,9 @@ static void json_next_token(json_parse_t *json, json_token_t *token) | |||
| 873 | token->type = T_NULL; | 933 | token->type = T_NULL; |
| 874 | json->index += 4; | 934 | json->index += 4; |
| 875 | return; | 935 | return; |
| 876 | } else if (!json->cfg->strict_numbers && json_is_invalid_number(json)) { | 936 | } else if (!json->cfg->decode_refuse_badnum && |
| 877 | /* When strict_numbers is disabled, only attempt to process | 937 | json_is_invalid_number(json)) { |
| 938 | /* When refuse_badnum is disabled, only attempt to process | ||
| 878 | * numbers we know are invalid JSON (Inf, NaN, hex) | 939 | * numbers we know are invalid JSON (Inf, NaN, hex) |
| 879 | * This is required to generate an appropriate token error, | 940 | * This is required to generate an appropriate token error, |
| 880 | * otherwise all bad tokens will register as "invalid number" | 941 | * otherwise all bad tokens will register as "invalid number" |
| @@ -1055,7 +1116,8 @@ static int json_decode(lua_State *l) | |||
| 1055 | const char *json; | 1116 | const char *json; |
| 1056 | size_t len; | 1117 | size_t len; |
| 1057 | 1118 | ||
| 1058 | luaL_argcheck(l, lua_gettop(l) <= 1, 2, "found too many arguments"); | 1119 | json_verify_arg_count(l, 1); |
| 1120 | |||
| 1059 | json = luaL_checklstring(l, 1, &len); | 1121 | json = luaL_checklstring(l, 1, &len); |
| 1060 | 1122 | ||
| 1061 | lua_json_decode(l, json, len); | 1123 | lua_json_decode(l, json, len); |
| @@ -1070,9 +1132,9 @@ int luaopen_cjson(lua_State *l) | |||
| 1070 | luaL_Reg reg[] = { | 1132 | luaL_Reg reg[] = { |
| 1071 | { "encode", json_encode }, | 1133 | { "encode", json_encode }, |
| 1072 | { "decode", json_decode }, | 1134 | { "decode", json_decode }, |
| 1073 | { "sparse_ratio", json_sparse_ratio }, | 1135 | { "encode_sparse_array", json_cfg_encode_sparse_array }, |
| 1074 | { "max_depth", json_max_depth }, | 1136 | { "encode_max_depth", json_cfg_encode_max_depth }, |
| 1075 | { "strict_numbers", json_strict_numbers }, | 1137 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, |
| 1076 | { NULL, NULL } | 1138 | { NULL, NULL } |
| 1077 | }; | 1139 | }; |
| 1078 | 1140 | ||
