diff options
| author | Mark Pulford <mark@kyne.com.au> | 2011-12-30 14:17:44 +1030 |
|---|---|---|
| committer | Mark Pulford <mark@kyne.com.au> | 2011-12-30 14:17:44 +1030 |
| commit | 2416b145073211b840781da6abf4b6d97f4657a6 (patch) | |
| tree | 6e92a13a7cc8ef8357245bc3ef320f5841350991 | |
| parent | 6cc88e3ac5275868e168acaf60203563f131355b (diff) | |
| download | lua-cjson-2416b145073211b840781da6abf4b6d97f4657a6.tar.gz lua-cjson-2416b145073211b840781da6abf4b6d97f4657a6.tar.bz2 lua-cjson-2416b145073211b840781da6abf4b6d97f4657a6.zip | |
Add fpconv to work around comma decimal points
Create a separate buffer and translate comma <> dot before calling
strtod(), and after calling sprintf() as required.
- Add "update_locale" Lua API call and init locale on module load.
- Move sprintf format string to fpconv
Diffstat (limited to '')
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | fpconv.c | 155 | ||||
| -rw-r--r-- | fpconv.h | 11 | ||||
| -rw-r--r-- | lua-cjson-1.0devel-1.rockspec | 4 | ||||
| -rw-r--r-- | lua_cjson.c | 36 | ||||
| -rw-r--r-- | strbuf.h | 14 | ||||
| -rwxr-xr-x | tests/test.lua | 2 |
8 files changed, 206 insertions, 20 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 349342e..8d8a420 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -30,7 +30,7 @@ else() | |||
| 30 | set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") | 30 | set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") |
| 31 | endif() | 31 | endif() |
| 32 | 32 | ||
| 33 | add_library(cjson MODULE lua_cjson.c strbuf.c) | 33 | add_library(cjson MODULE lua_cjson.c strbuf.c fpconv.c) |
| 34 | set_target_properties(cjson PROPERTIES PREFIX "") | 34 | set_target_properties(cjson PROPERTIES PREFIX "") |
| 35 | install(TARGETS cjson DESTINATION "${_lua_module_dir}") | 35 | install(TARGETS cjson DESTINATION "${_lua_module_dir}") |
| 36 | 36 | ||
| @@ -35,7 +35,7 @@ INSTALL_CMD = install | |||
| 35 | ## End platform specific section | 35 | ## End platform specific section |
| 36 | 36 | ||
| 37 | BUILD_CFLAGS = -fpic -I$(LUA_INCLUDE_DIR) $(CJSON_CFLAGS) | 37 | BUILD_CFLAGS = -fpic -I$(LUA_INCLUDE_DIR) $(CJSON_CFLAGS) |
| 38 | OBJS := lua_cjson.o strbuf.o | 38 | OBJS := lua_cjson.o strbuf.o fpconv.o |
| 39 | 39 | ||
| 40 | .PHONY: all clean install package doc | 40 | .PHONY: all clean install package doc |
| 41 | 41 | ||
diff --git a/fpconv.c b/fpconv.c new file mode 100644 index 0000000..3ff79dc --- /dev/null +++ b/fpconv.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <assert.h> | ||
| 4 | #include <string.h> | ||
| 5 | |||
| 6 | #include "fpconv.h" | ||
| 7 | |||
| 8 | static char locale_decimal_point = '.'; | ||
| 9 | |||
| 10 | /* In theory multibyte decimal_points are possible, but | ||
| 11 | * Lua CJSON only supports UTF-8 and known locales only have | ||
| 12 | * single byte decimal points ([.,]). | ||
| 13 | * | ||
| 14 | * localconv() may not be thread safe, and nl_langinfo() is not | ||
| 15 | * supported on some platforms. Use sprintf() instead. */ | ||
| 16 | void fpconv_update_locale() | ||
| 17 | { | ||
| 18 | char buf[8]; | ||
| 19 | |||
| 20 | snprintf(buf, sizeof(buf), "%g", 0.5); | ||
| 21 | |||
| 22 | /* Failing this test might imply the platform has a buggy dtoa | ||
| 23 | * implementation or wide characters */ | ||
| 24 | if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { | ||
| 25 | fprintf(stderr, "Error: wide characters found or printf() bug."); | ||
| 26 | abort(); | ||
| 27 | } | ||
| 28 | |||
| 29 | locale_decimal_point = buf[1]; | ||
| 30 | } | ||
| 31 | |||
| 32 | /* Check for a valid number character: [-+0-9a-fA-FpPxX.] | ||
| 33 | * It doesn't matter if actual invalid characters are counted - strtod() | ||
| 34 | * will find the valid number if it exists. The risk is that slightly more | ||
| 35 | * memory might be allocated before a parse error occurs. */ | ||
| 36 | static int valid_number_character(char ch) | ||
| 37 | { | ||
| 38 | char lower_ch; | ||
| 39 | |||
| 40 | if ('0' <= ch && ch <= '9') | ||
| 41 | return 1; | ||
| 42 | if (ch == '-' || ch == '+' || ch == '.') | ||
| 43 | return 1; | ||
| 44 | |||
| 45 | /* Hex digits, exponent (e), base (p), "infinity",.. | ||
| 46 | * The main purpose is to not include a "comma". If any other invalid | ||
| 47 | * characters are included, the will only generate a parse error later. */ | ||
| 48 | lower_ch = ch | 0x20; | ||
| 49 | if ('a' <= lower_ch && lower_ch <= 'y') | ||
| 50 | return 1; | ||
| 51 | |||
| 52 | return 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* Calculate the size of the buffer required for a locale | ||
| 56 | * conversion. Returns 0 if conversion is not required */ | ||
| 57 | static int strtod_buffer_size(const char *s) | ||
| 58 | { | ||
| 59 | const char *p = s; | ||
| 60 | |||
| 61 | while (valid_number_character(*p)) | ||
| 62 | p++; | ||
| 63 | |||
| 64 | return p - s; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* Similar to strtod(), but must be passed the current locale's decimal point | ||
| 68 | * character. Guaranteed to be called at the start of any valid number in a string */ | ||
| 69 | double fpconv_strtod(const char *nptr, char **endptr) | ||
| 70 | { | ||
| 71 | char *num, *endnum, *dp; | ||
| 72 | int numlen; | ||
| 73 | double value; | ||
| 74 | |||
| 75 | /* System strtod() is fine when decimal point is '.' */ | ||
| 76 | if (locale_decimal_point == '.') | ||
| 77 | return strtod(nptr, endptr); | ||
| 78 | |||
| 79 | numlen = strtod_buffer_size(nptr); | ||
| 80 | if (!numlen) { | ||
| 81 | /* No valid characters found, standard strtod() return */ | ||
| 82 | *endptr = (char *)nptr; | ||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Duplicate number into buffer */ | ||
| 87 | num = malloc(numlen + 1); | ||
| 88 | if (!num) { | ||
| 89 | fprintf(stderr, "Out of memory"); | ||
| 90 | abort(); | ||
| 91 | } | ||
| 92 | memcpy(num, nptr, numlen); | ||
| 93 | num[numlen] = 0; | ||
| 94 | |||
| 95 | /* Update decimal point character if found */ | ||
| 96 | dp = strchr(num, '.'); | ||
| 97 | if (dp) | ||
| 98 | *dp = locale_decimal_point; | ||
| 99 | |||
| 100 | value = strtod(num, &endnum); | ||
| 101 | *endptr = (char *)&nptr[endnum - num]; | ||
| 102 | free(num); | ||
| 103 | |||
| 104 | return value; | ||
| 105 | } | ||
| 106 | |||
| 107 | /* "fmt" must point to a buffer of at least 6 characters */ | ||
| 108 | static void set_number_format(char *fmt, int precision) | ||
| 109 | { | ||
| 110 | int d1, d2, i; | ||
| 111 | |||
| 112 | assert(1 <= precision && precision <= 14); | ||
| 113 | |||
| 114 | /* Create printf format (%.14g) from precision */ | ||
| 115 | d1 = precision / 10; | ||
| 116 | d2 = precision % 10; | ||
| 117 | fmt[0] = '%'; | ||
| 118 | fmt[1] = '.'; | ||
| 119 | i = 2; | ||
| 120 | if (d1) { | ||
| 121 | fmt[i++] = '0' + d1; | ||
| 122 | } | ||
| 123 | fmt[i++] = '0' + d2; | ||
| 124 | fmt[i++] = 'g'; | ||
| 125 | fmt[i++] = 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* Assumes there is always at least 32 characters available in the target buffer */ | ||
| 129 | int fpconv_g_fmt(char *str, double num, int precision) | ||
| 130 | { | ||
| 131 | char buf[FPCONV_G_FMT_BUFSIZE]; | ||
| 132 | char fmt[6]; | ||
| 133 | int len; | ||
| 134 | char *b; | ||
| 135 | |||
| 136 | set_number_format(fmt, precision); | ||
| 137 | |||
| 138 | /* Pass through when decimal point character is dot. */ | ||
| 139 | if (locale_decimal_point == '.') | ||
| 140 | return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); | ||
| 141 | |||
| 142 | /* snprintf() to a buffer then translate for other decimal point characters */ | ||
| 143 | len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); | ||
| 144 | |||
| 145 | /* Returned 'len' includes the null terminator */ | ||
| 146 | b = buf; | ||
| 147 | do { | ||
| 148 | *str++ = (*b == locale_decimal_point ? '.' : *b); | ||
| 149 | } while(*b++); | ||
| 150 | |||
| 151 | return len; | ||
| 152 | } | ||
| 153 | |||
| 154 | /* vi:ai et sw=4 ts=4: | ||
| 155 | */ | ||
diff --git a/fpconv.h b/fpconv.h new file mode 100644 index 0000000..b8a6469 --- /dev/null +++ b/fpconv.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Lua CJSON floating point conversion routines */ | ||
| 2 | |||
| 3 | /* Buffer larger than required to store the largest %.14g number */ | ||
| 4 | # define FPCONV_G_FMT_BUFSIZE 32 | ||
| 5 | |||
| 6 | extern void fpconv_update_locale(); | ||
| 7 | extern int fpconv_g_fmt(char*, double, int); | ||
| 8 | extern double fpconv_strtod(const char*, char**); | ||
| 9 | |||
| 10 | /* vi:ai et sw=4 ts=4: | ||
| 11 | */ | ||
diff --git a/lua-cjson-1.0devel-1.rockspec b/lua-cjson-1.0devel-1.rockspec index 3890ec1..fa20c53 100644 --- a/lua-cjson-1.0devel-1.rockspec +++ b/lua-cjson-1.0devel-1.rockspec | |||
| @@ -23,11 +23,11 @@ build = { | |||
| 23 | type = "builtin", | 23 | type = "builtin", |
| 24 | modules = { | 24 | modules = { |
| 25 | cjson = { | 25 | cjson = { |
| 26 | sources = { "lua_cjson.c", "strbuf.c" }, | 26 | sources = { "lua_cjson.c", "strbuf.c", "fpconv.c" }, |
| 27 | defines = { | ||
| 27 | -- Optional workaround: | 28 | -- Optional workaround: |
| 28 | -- USE_INTERNAL_ISINF: Provide internal isinf() implementation. Required | 29 | -- USE_INTERNAL_ISINF: Provide internal isinf() implementation. Required |
| 29 | -- on some Solaris platforms. | 30 | -- on some Solaris platforms. |
| 30 | defines = { | ||
| 31 | -- LuaRocks does not support platform specific configuration for Solaris. | 31 | -- LuaRocks does not support platform specific configuration for Solaris. |
| 32 | -- Uncomment the line below on Solaris platforms. | 32 | -- Uncomment the line below on Solaris platforms. |
| 33 | -- "USE_INTERNAL_ISINF" | 33 | -- "USE_INTERNAL_ISINF" |
diff --git a/lua_cjson.c b/lua_cjson.c index f5ea0dd..8e9b237 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include <lauxlib.h> | 43 | #include <lauxlib.h> |
| 44 | 44 | ||
| 45 | #include "strbuf.h" | 45 | #include "strbuf.h" |
| 46 | #include "fpconv.h" | ||
| 46 | 47 | ||
| 47 | #ifndef CJSON_VERSION | 48 | #ifndef CJSON_VERSION |
| 48 | #define CJSON_VERSION "1.0devel" | 49 | #define CJSON_VERSION "1.0devel" |
| @@ -60,6 +61,7 @@ | |||
| 60 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 | 61 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 |
| 61 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 | 62 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 |
| 62 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 | 63 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 |
| 64 | #define DEFAULT_ENCODE_NUMBER_PRECISION 14 | ||
| 63 | 65 | ||
| 64 | typedef enum { | 66 | typedef enum { |
| 65 | T_OBJ_BEGIN, | 67 | T_OBJ_BEGIN, |
| @@ -104,7 +106,6 @@ typedef struct { | |||
| 104 | char *char2escape[256]; /* Encoding */ | 106 | char *char2escape[256]; /* Encoding */ |
| 105 | #endif | 107 | #endif |
| 106 | strbuf_t encode_buf; | 108 | strbuf_t encode_buf; |
| 107 | char number_fmt[8]; /* "%.XXg\0" */ | ||
| 108 | int current_depth; | 109 | int current_depth; |
| 109 | 110 | ||
| 110 | int encode_sparse_convert; | 111 | int encode_sparse_convert; |
| @@ -253,12 +254,6 @@ static int json_cfg_encode_max_depth(lua_State *l) | |||
| 253 | return 1; | 254 | return 1; |
| 254 | } | 255 | } |
| 255 | 256 | ||
| 256 | static void json_set_number_precision(json_config_t *cfg, int prec) | ||
| 257 | { | ||
| 258 | cfg->encode_number_precision = prec; | ||
| 259 | sprintf(cfg->number_fmt, "%%.%dg", prec); | ||
| 260 | } | ||
| 261 | |||
| 262 | /* Configures number precision when converting doubles to text */ | 257 | /* Configures number precision when converting doubles to text */ |
| 263 | static int json_cfg_encode_number_precision(lua_State *l) | 258 | static int json_cfg_encode_number_precision(lua_State *l) |
| 264 | { | 259 | { |
| @@ -272,7 +267,7 @@ static int json_cfg_encode_number_precision(lua_State *l) | |||
| 272 | precision = luaL_checkinteger(l, 1); | 267 | precision = luaL_checkinteger(l, 1); |
| 273 | luaL_argcheck(l, 1 <= precision && precision <= 14, 1, | 268 | luaL_argcheck(l, 1 <= precision && precision <= 14, 1, |
| 274 | "expected integer between 1 and 14"); | 269 | "expected integer between 1 and 14"); |
| 275 | json_set_number_precision(cfg, precision); | 270 | cfg->encode_number_precision = precision; |
| 276 | } | 271 | } |
| 277 | 272 | ||
| 278 | lua_pushinteger(l, cfg->encode_number_precision); | 273 | lua_pushinteger(l, cfg->encode_number_precision); |
| @@ -342,6 +337,13 @@ static int json_cfg_refuse_invalid_numbers(lua_State *l) | |||
| 342 | return 1; | 337 | return 1; |
| 343 | } | 338 | } |
| 344 | 339 | ||
| 340 | static int json_update_locale(lua_State *l) | ||
| 341 | { | ||
| 342 | fpconv_update_locale(); | ||
| 343 | |||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 345 | static int json_destroy_config(lua_State *l) | 347 | static int json_destroy_config(lua_State *l) |
| 346 | { | 348 | { |
| 347 | json_config_t *cfg; | 349 | json_config_t *cfg; |
| @@ -376,7 +378,7 @@ static void json_create_config(lua_State *l) | |||
| 376 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; | 378 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; |
| 377 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; | 379 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; |
| 378 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; | 380 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; |
| 379 | json_set_number_precision(cfg, 14); | 381 | cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; |
| 380 | 382 | ||
| 381 | /* Decoding init */ | 383 | /* Decoding init */ |
| 382 | 384 | ||
| @@ -562,6 +564,7 @@ static void json_append_number(lua_State *l, strbuf_t *json, int index, | |||
| 562 | json_config_t *cfg) | 564 | json_config_t *cfg) |
| 563 | { | 565 | { |
| 564 | double num = lua_tonumber(l, index); | 566 | double num = lua_tonumber(l, index); |
| 567 | int len; | ||
| 565 | 568 | ||
| 566 | if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num))) | 569 | if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num))) |
| 567 | json_encode_exception(l, cfg, index, "must not be NaN or Inf"); | 570 | json_encode_exception(l, cfg, index, "must not be NaN or Inf"); |
| @@ -571,11 +574,10 @@ static void json_append_number(lua_State *l, strbuf_t *json, int index, | |||
| 571 | strbuf_append_mem(json, "nan", 3); | 574 | strbuf_append_mem(json, "nan", 3); |
| 572 | } else { | 575 | } else { |
| 573 | /* Longest double printed with %.14g is 21 characters long: | 576 | /* Longest double printed with %.14g is 21 characters long: |
| 574 | * -1.7976931348623e+308 | 577 | * -1.7976931348623e+308 */ |
| 575 | * | 578 | strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); |
| 576 | * Use 32 to include the \0, and a few extra just in case.. | 579 | len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); |
| 577 | */ | 580 | strbuf_extend_length(json, len); |
| 578 | strbuf_append_fmt(json, 32, cfg->number_fmt, num); | ||
| 579 | } | 581 | } |
| 580 | } | 582 | } |
| 581 | 583 | ||
| @@ -963,7 +965,7 @@ static void json_next_number_token(json_parse_t *json, json_token_t *token) | |||
| 963 | 965 | ||
| 964 | token->type = T_NUMBER; | 966 | token->type = T_NUMBER; |
| 965 | startptr = &json->data[json->index]; | 967 | startptr = &json->data[json->index]; |
| 966 | token->value.number = strtod(&json->data[json->index], &endptr); | 968 | token->value.number = fpconv_strtod(&json->data[json->index], &endptr); |
| 967 | if (startptr == endptr) | 969 | if (startptr == endptr) |
| 968 | json_set_token_error(token, json, "invalid number"); | 970 | json_set_token_error(token, json, "invalid number"); |
| 969 | else | 971 | else |
| @@ -1254,9 +1256,13 @@ int luaopen_cjson(lua_State *l) | |||
| 1254 | { "encode_number_precision", json_cfg_encode_number_precision }, | 1256 | { "encode_number_precision", json_cfg_encode_number_precision }, |
| 1255 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, | 1257 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, |
| 1256 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, | 1258 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, |
| 1259 | { "update_locale", json_update_locale }, | ||
| 1257 | { NULL, NULL } | 1260 | { NULL, NULL } |
| 1258 | }; | 1261 | }; |
| 1259 | 1262 | ||
| 1263 | /* Update the current locale for g_fmt/strtod */ | ||
| 1264 | fpconv_update_locale(); | ||
| 1265 | |||
| 1260 | /* Use json_config_key as a pointer. | 1266 | /* Use json_config_key as a pointer. |
| 1261 | * It's faster than using a config string, and more unique */ | 1267 | * It's faster than using a config string, and more unique */ |
| 1262 | lua_pushlightuserdata(l, &json_config_key); | 1268 | lua_pushlightuserdata(l, &json_config_key); |
| @@ -62,7 +62,9 @@ extern void strbuf_resize(strbuf_t *s, int len); | |||
| 62 | static int strbuf_empty_length(strbuf_t *s); | 62 | static int strbuf_empty_length(strbuf_t *s); |
| 63 | static int strbuf_length(strbuf_t *s); | 63 | static int strbuf_length(strbuf_t *s); |
| 64 | static char *strbuf_string(strbuf_t *s, int *len); | 64 | static char *strbuf_string(strbuf_t *s, int *len); |
| 65 | static void strbuf_ensure_empty_length(strbuf_t *s, int len); | 65 | static void strbuf_ensure_empty_length(strbuf_t *s, int len); |
| 66 | static char *strbuf_empty_ptr(strbuf_t *s); | ||
| 67 | static void strbuf_extend_length(strbuf_t *s, int len); | ||
| 66 | 68 | ||
| 67 | /* Update */ | 69 | /* Update */ |
| 68 | extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); | 70 | extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); |
| @@ -96,6 +98,16 @@ static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) | |||
| 96 | strbuf_resize(s, s->length + len); | 98 | strbuf_resize(s, s->length + len); |
| 97 | } | 99 | } |
| 98 | 100 | ||
| 101 | static inline char *strbuf_empty_ptr(strbuf_t *s) | ||
| 102 | { | ||
| 103 | return s->buf + s->length; | ||
| 104 | } | ||
| 105 | |||
| 106 | static inline void strbuf_extend_length(strbuf_t *s, int len) | ||
| 107 | { | ||
| 108 | s->length += len; | ||
| 109 | } | ||
| 110 | |||
| 99 | static inline int strbuf_length(strbuf_t *s) | 111 | static inline int strbuf_length(strbuf_t *s) |
| 100 | { | 112 | { |
| 101 | return s->length; | 113 | return s->length; |
diff --git a/tests/test.lua b/tests/test.lua index bb696a2..bdae6ea 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
| @@ -211,12 +211,14 @@ local escape_tests = { | |||
| 211 | local locale_tests = { | 211 | local locale_tests = { |
| 212 | function () | 212 | function () |
| 213 | os.setlocale("cs_CZ") | 213 | os.setlocale("cs_CZ") |
| 214 | cjson.update_locale() | ||
| 214 | return "Setting locale to cs_CZ (comma separator)" | 215 | return "Setting locale to cs_CZ (comma separator)" |
| 215 | end, | 216 | end, |
| 216 | { json.encode, { 1.5 }, true, { '1.5' } }, | 217 | { json.encode, { 1.5 }, true, { '1.5' } }, |
| 217 | { json.decode, { "[ 10, \"test\" ]" }, true, { { 10, "test" } } }, | 218 | { json.decode, { "[ 10, \"test\" ]" }, true, { { 10, "test" } } }, |
| 218 | function () | 219 | function () |
| 219 | os.setlocale("C") | 220 | os.setlocale("C") |
| 221 | cjson.update_locale() | ||
| 220 | return "Reverting locale to POSIX" | 222 | return "Reverting locale to POSIX" |
| 221 | end | 223 | end |
| 222 | } | 224 | } |
