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.
-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 ] | ||