aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Pulford <mark@kyne.com.au>2011-05-29 18:03:22 +0930
committerMark Pulford <mark@kyne.com.au>2011-05-29 18:03:22 +0930
commit3d1c5e19f45cf484774926ba6e2555d1c8e4c39b (patch)
treef6cd7cc888ec643d025a37810fe7f15cc91d7381
parentc0b473a8e974407dc308ce0fd0058136b9faa90c (diff)
downloadlua-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--README13
-rw-r--r--lua_cjson.c43
-rw-r--r--strbuf.c27
-rw-r--r--strbuf.h4
-rw-r--r--tests/numbers.json7
5 files changed, 77 insertions, 17 deletions
diff --git a/README b/README
index 3fa7595..b4f5bcd 100644
--- a/README
+++ b/README
@@ -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
207Number precision
208----------------
209
210 precision = cjson.encode_number_precision([precision])
211 -- "precision" must be between 1 and 14 (inclusive)
212
213By default CJSON will use up to 14 digits for precision when
214converting a number to text.
215
216Reducing number precision to 3 can improve performance of number
217heavy conversions by up to 50%.
218
219
207Persistent encoding buffer 220Persistent 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
108typedef struct { 111typedef 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
247static 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 */
254static 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 */
245static int json_cfg_encode_keep_buffer(lua_State *l) 275static 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
558static void json_append_object(lua_State *l, json_config_t *cfg, 594static 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 }
diff --git a/strbuf.c b/strbuf.c
index c59b6f6..976925a 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -197,25 +197,28 @@ void strbuf_append_string(strbuf_t *s, const char *str)
197 } 197 }
198} 198}
199 199
200void 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. */
202void 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
218void 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. */
221void 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;
diff --git a/strbuf.h b/strbuf.h
index 90ef844..f856543 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -65,10 +65,10 @@ static char *strbuf_string(strbuf_t *s, int *len);
65static void strbuf_ensure_empty_length(strbuf_t *s, int len); 65static void strbuf_ensure_empty_length(strbuf_t *s, int len);
66 66
67/* Update */ 67/* Update */
68extern void strbuf_append_fmt(strbuf_t *s, const char *format, ...); 68extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
69extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
69static void strbuf_append_mem(strbuf_t *s, const char *c, int len); 70static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
70extern void strbuf_append_string(strbuf_t *s, const char *str); 71extern void strbuf_append_string(strbuf_t *s, const char *str);
71extern void strbuf_append_number(strbuf_t *s, double number);
72static void strbuf_append_char(strbuf_t *s, const char c); 72static void strbuf_append_char(strbuf_t *s, const char c);
73static void strbuf_ensure_null(strbuf_t *s); 73static 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 ]