diff options
| author | Mark Pulford <mark@kyne.com.au> | 2011-05-29 18:03:22 +0930 |
|---|---|---|
| committer | Mark Pulford <mark@kyne.com.au> | 2011-05-29 18:03:22 +0930 |
| commit | 3d1c5e19f45cf484774926ba6e2555d1c8e4c39b (patch) | |
| tree | f6cd7cc888ec643d025a37810fe7f15cc91d7381 | |
| parent | c0b473a8e974407dc308ce0fd0058136b9faa90c (diff) | |
| download | lua-cjson-3d1c5e19f45cf484774926ba6e2555d1c8e4c39b.tar.gz lua-cjson-3d1c5e19f45cf484774926ba6e2555d1c8e4c39b.tar.bz2 lua-cjson-3d1c5e19f45cf484774926ba6e2555d1c8e4c39b.zip | |
Add support for runtime number precision config
Add cjson.encode_number_precision(). Reducing the number precision from
14 to 3 can increase performance up to 50% with number heavy
conversions.
Diffstat (limited to '')
| -rw-r--r-- | README | 13 | ||||
| -rw-r--r-- | lua_cjson.c | 43 | ||||
| -rw-r--r-- | strbuf.c | 27 | ||||
| -rw-r--r-- | strbuf.h | 4 | ||||
| -rw-r--r-- | tests/numbers.json | 7 |
5 files changed, 77 insertions, 17 deletions
| @@ -204,6 +204,19 @@ the application. Eg: | |||
| 204 | a = {}; b = { a }; a[1] = b | 204 | a = {}; b = { a }; a[1] = b |
| 205 | 205 | ||
| 206 | 206 | ||
| 207 | Number precision | ||
| 208 | ---------------- | ||
| 209 | |||
| 210 | precision = cjson.encode_number_precision([precision]) | ||
| 211 | -- "precision" must be between 1 and 14 (inclusive) | ||
| 212 | |||
| 213 | By default CJSON will use up to 14 digits for precision when | ||
| 214 | converting a number to text. | ||
| 215 | |||
| 216 | Reducing number precision to 3 can improve performance of number | ||
| 217 | heavy conversions by up to 50%. | ||
| 218 | |||
| 219 | |||
| 207 | Persistent encoding buffer | 220 | Persistent encoding buffer |
| 208 | ------------------------- | 221 | ------------------------- |
| 209 | 222 | ||
diff --git a/lua_cjson.c b/lua_cjson.c index 2228fee..4b1915a 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -26,9 +26,10 @@ | |||
| 26 | * - JSON "null" values are represented as lightuserdata since Lua | 26 | * - JSON "null" values are represented as lightuserdata since Lua |
| 27 | * tables cannot contain "nil". Compare with cjson.null. | 27 | * tables cannot contain "nil". Compare with cjson.null. |
| 28 | * - Invalid UTF-8 characters are not detected and will be passed | 28 | * - Invalid UTF-8 characters are not detected and will be passed |
| 29 | * untouched. | 29 | * untouched. If required, UTF-8 error checking should be done |
| 30 | * outside this library. | ||
| 30 | * - Javascript comments are not part of the JSON spec, and are not | 31 | * - Javascript comments are not part of the JSON spec, and are not |
| 31 | * supported. | 32 | * currently supported. |
| 32 | * | 33 | * |
| 33 | * Note: Decoding is slower than encoding. Lua spends significant | 34 | * Note: Decoding is slower than encoding. Lua spends significant |
| 34 | * time (30%) managing tables when parsing JSON since it is | 35 | * time (30%) managing tables when parsing JSON since it is |
| @@ -94,6 +95,7 @@ typedef struct { | |||
| 94 | char *char2escape[256]; /* Encoding */ | 95 | char *char2escape[256]; /* Encoding */ |
| 95 | #endif | 96 | #endif |
| 96 | strbuf_t encode_buf; | 97 | strbuf_t encode_buf; |
| 98 | char number_fmt[8]; /* "%.XXg\0" */ | ||
| 97 | int current_depth; | 99 | int current_depth; |
| 98 | 100 | ||
| 99 | int encode_sparse_convert; | 101 | int encode_sparse_convert; |
| @@ -103,6 +105,7 @@ typedef struct { | |||
| 103 | int encode_refuse_badnum; | 105 | int encode_refuse_badnum; |
| 104 | int decode_refuse_badnum; | 106 | int decode_refuse_badnum; |
| 105 | int encode_keep_buffer; | 107 | int encode_keep_buffer; |
| 108 | int encode_number_precision; | ||
| 106 | } json_config_t; | 109 | } json_config_t; |
| 107 | 110 | ||
| 108 | typedef struct { | 111 | typedef struct { |
| @@ -241,6 +244,33 @@ static int json_cfg_encode_max_depth(lua_State *l) | |||
| 241 | return 1; | 244 | return 1; |
| 242 | } | 245 | } |
| 243 | 246 | ||
| 247 | static void json_set_number_precision(json_config_t *cfg, int prec) | ||
| 248 | { | ||
| 249 | cfg->encode_number_precision = prec; | ||
| 250 | sprintf(cfg->number_fmt, "%%.%dg", prec); | ||
| 251 | } | ||
| 252 | |||
| 253 | /* Configures number precision when converting doubles to text */ | ||
| 254 | static int json_cfg_encode_number_precision(lua_State *l) | ||
| 255 | { | ||
| 256 | json_config_t *cfg; | ||
| 257 | int precision; | ||
| 258 | |||
| 259 | json_verify_arg_count(l, 1); | ||
| 260 | cfg = json_fetch_config(l); | ||
| 261 | |||
| 262 | if (lua_gettop(l)) { | ||
| 263 | precision = luaL_checkinteger(l, 1); | ||
| 264 | luaL_argcheck(l, 1 <= precision && precision <= 14, 1, | ||
| 265 | "expected integer between 1 and 14"); | ||
| 266 | json_set_number_precision(cfg, precision); | ||
| 267 | } | ||
| 268 | |||
| 269 | lua_pushinteger(l, cfg->encode_number_precision); | ||
| 270 | |||
| 271 | return 1; | ||
| 272 | } | ||
| 273 | |||
| 244 | /* Configures JSON encoding buffer persistence */ | 274 | /* Configures JSON encoding buffer persistence */ |
| 245 | static int json_cfg_encode_keep_buffer(lua_State *l) | 275 | static int json_cfg_encode_keep_buffer(lua_State *l) |
| 246 | { | 276 | { |
| @@ -337,6 +367,7 @@ static void json_create_config(lua_State *l) | |||
| 337 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; | 367 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; |
| 338 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; | 368 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; |
| 339 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; | 369 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; |
| 370 | json_set_number_precision(cfg, 14); | ||
| 340 | 371 | ||
| 341 | /* Decoding init */ | 372 | /* Decoding init */ |
| 342 | 373 | ||
| @@ -552,7 +583,12 @@ static void json_append_number(lua_State *l, strbuf_t *json, int index, | |||
| 552 | if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num))) | 583 | if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num))) |
| 553 | json_encode_exception(l, cfg, index, "must not be NaN or Inf"); | 584 | json_encode_exception(l, cfg, index, "must not be NaN or Inf"); |
| 554 | 585 | ||
| 555 | strbuf_append_number(json, num); | 586 | /* Lowest double printed with %.14g is 21 characters long: |
| 587 | * -1.7976931348623e+308 | ||
| 588 | * | ||
| 589 | * Use 32 to include the \0, and a few extra just in case.. | ||
| 590 | */ | ||
| 591 | strbuf_append_fmt(json, 32, cfg->number_fmt, num); | ||
| 556 | } | 592 | } |
| 557 | 593 | ||
| 558 | static void json_append_object(lua_State *l, json_config_t *cfg, | 594 | static void json_append_object(lua_State *l, json_config_t *cfg, |
| @@ -1227,6 +1263,7 @@ int luaopen_cjson(lua_State *l) | |||
| 1227 | { "decode", json_decode }, | 1263 | { "decode", json_decode }, |
| 1228 | { "encode_sparse_array", json_cfg_encode_sparse_array }, | 1264 | { "encode_sparse_array", json_cfg_encode_sparse_array }, |
| 1229 | { "encode_max_depth", json_cfg_encode_max_depth }, | 1265 | { "encode_max_depth", json_cfg_encode_max_depth }, |
| 1266 | { "encode_number_precision", json_cfg_encode_number_precision }, | ||
| 1230 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, | 1267 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, |
| 1231 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, | 1268 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, |
| 1232 | { NULL, NULL } | 1269 | { NULL, NULL } |
| @@ -197,25 +197,28 @@ void strbuf_append_string(strbuf_t *s, const char *str) | |||
| 197 | } | 197 | } |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | void strbuf_append_number(strbuf_t *s, double number) | 200 | /* strbuf_append_fmt() should only be used when an upper bound |
| 201 | * is known for the output string. */ | ||
| 202 | void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) | ||
| 201 | { | 203 | { |
| 202 | int len; | 204 | va_list arg; |
| 205 | int fmt_len; | ||
| 203 | 206 | ||
| 204 | /* Lowest double printed with %.14g is 21 characters long: | 207 | strbuf_ensure_empty_length(s, len); |
| 205 | * -1.7976931348623e+308 | ||
| 206 | * | ||
| 207 | * Use 32 to include the \0, and a few extra just in case.. | ||
| 208 | */ | ||
| 209 | strbuf_ensure_empty_length(s, 32); | ||
| 210 | 208 | ||
| 211 | len = sprintf(s->buf + s->length, "%.14g", number); | 209 | va_start(arg, fmt); |
| 212 | if (len < 0) | 210 | fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); |
| 211 | va_end(arg); | ||
| 212 | |||
| 213 | if (fmt_len < 0) | ||
| 213 | die("BUG: Unable to convert number"); /* This should never happen.. */ | 214 | die("BUG: Unable to convert number"); /* This should never happen.. */ |
| 214 | 215 | ||
| 215 | s->length += len; | 216 | s->length += fmt_len; |
| 216 | } | 217 | } |
| 217 | 218 | ||
| 218 | void strbuf_append_fmt(strbuf_t *s, const char *fmt, ...) | 219 | /* strbuf_append_fmt_retry() can be used when the there is no known |
| 220 | * upper bound for the output string. */ | ||
| 221 | void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) | ||
| 219 | { | 222 | { |
| 220 | va_list arg; | 223 | va_list arg; |
| 221 | int fmt_len, try; | 224 | int fmt_len, try; |
| @@ -65,10 +65,10 @@ 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 | 66 | ||
| 67 | /* Update */ | 67 | /* Update */ |
| 68 | extern void strbuf_append_fmt(strbuf_t *s, const char *format, ...); | 68 | extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); |
| 69 | extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); | ||
| 69 | static void strbuf_append_mem(strbuf_t *s, const char *c, int len); | 70 | static void strbuf_append_mem(strbuf_t *s, const char *c, int len); |
| 70 | extern void strbuf_append_string(strbuf_t *s, const char *str); | 71 | extern void strbuf_append_string(strbuf_t *s, const char *str); |
| 71 | extern void strbuf_append_number(strbuf_t *s, double number); | ||
| 72 | static void strbuf_append_char(strbuf_t *s, const char c); | 72 | static void strbuf_append_char(strbuf_t *s, const char c); |
| 73 | static void strbuf_ensure_null(strbuf_t *s); | 73 | static void strbuf_ensure_null(strbuf_t *s); |
| 74 | 74 | ||
diff --git a/tests/numbers.json b/tests/numbers.json new file mode 100644 index 0000000..ef11a26 --- /dev/null +++ b/tests/numbers.json | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | [ 0.110001000000000000000001, | ||
| 2 | 0.12345678910111213141516, | ||
| 3 | 0.412454033640, | ||
| 4 | 2.6651441426902251886502972498731, | ||
| 5 | 2.718281828459045235360287471352, | ||
| 6 | 3.141592653589793238462643383279, | ||
| 7 | 2.14069263277926 ] | ||
