diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | README | 17 | ||||
-rw-r--r-- | lua_cjson.c | 61 | ||||
-rw-r--r-- | strbuf.c | 6 | ||||
-rw-r--r-- | strbuf.h | 5 | ||||
-rwxr-xr-x | tests/test.lua | 2 |
7 files changed, 75 insertions, 19 deletions
@@ -16,7 +16,7 @@ LUA_INCLUDE_DIR ?= $(PREFIX)/include | |||
16 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) | 16 | LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) |
17 | 17 | ||
18 | #CFLAGS ?= -g -Wall -pedantic -fno-inline | 18 | #CFLAGS ?= -g -Wall -pedantic -fno-inline |
19 | CFLAGS ?= -g -O2 -Wall -pedantic | 19 | CFLAGS ?= -g -O3 -Wall -pedantic |
20 | override CFLAGS += -fpic -I$(LUA_INCLUDE_DIR) -DVERSION=\"$(CJSON_VERSION)\" | 20 | override CFLAGS += -fpic -I$(LUA_INCLUDE_DIR) -DVERSION=\"$(CJSON_VERSION)\" |
21 | LDFLAGS += -lm | 21 | LDFLAGS += -lm |
22 | 22 | ||
@@ -1,6 +1,7 @@ | |||
1 | Version 1.0.1 (May 10 2011) | 1 | Version 1.0.1 (May 10 2011) |
2 | * Added build support for OSX | 2 | * Added build support for OSX |
3 | * Removed unnecessary whitespace from JSON output | 3 | * Removed unnecessary whitespace from JSON output |
4 | * Added cjson.encode_keep_buffer() | ||
4 | 5 | ||
5 | Version 1.0 (May 9 2011) | 6 | Version 1.0 (May 9 2011) |
6 | * Initial release. | 7 | * Initial release. |
@@ -34,8 +34,7 @@ Build requirements: | |||
34 | Or: | 34 | Or: |
35 | - LuaJIT (http://www.luajit.org/) | 35 | - LuaJIT (http://www.luajit.org/) |
36 | 36 | ||
37 | The included Makefile should be reviewed. Updated the Makefile to suit | 37 | Review and update the included Makefile to suit your platform. Then: |
38 | your platform and Lua header/library directories. Then: | ||
39 | 38 | ||
40 | # make | 39 | # make |
41 | # make install | 40 | # make install |
@@ -66,6 +65,7 @@ Synopsis | |||
66 | setting = cjson.refuse_invalid_numbers([setting]) | 65 | setting = cjson.refuse_invalid_numbers([setting]) |
67 | depth = cjson.encode_max_depth([depth]) | 66 | depth = cjson.encode_max_depth([depth]) |
68 | convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) | 67 | convert, ratio, safe = cjson.encode_sparse_array([convert[, ratio[, safe]]]) |
68 | keep = cjson.encode_keep_buffer([keep]) | ||
69 | 69 | ||
70 | 70 | ||
71 | Encoding | 71 | Encoding |
@@ -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 | Persistent encoding buffer | ||
208 | ------------------------- | ||
209 | |||
210 | keep = cjson.keep_encode_buffer([keep]) | ||
211 | -- "keep" must be a boolean | ||
212 | |||
213 | By default, CJSON will reuse the JSON encoding buffer to improve | ||
214 | performance. The buffer will grow to the largest size required and is | ||
215 | not freed until CJSON is garbage collected. Setting this option to | ||
216 | "false" will cause the buffer to be freed after each call to | ||
217 | cjson.encode(). | ||
218 | |||
219 | |||
207 | References | 220 | References |
208 | ========== | 221 | ========== |
209 | 222 | ||
diff --git a/lua_cjson.c b/lua_cjson.c index f888644..50f27c2 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #define DEFAULT_MAX_DEPTH 20 | 49 | #define DEFAULT_MAX_DEPTH 20 |
50 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 | 50 | #define DEFAULT_ENCODE_REFUSE_BADNUM 1 |
51 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 | 51 | #define DEFAULT_DECODE_REFUSE_BADNUM 0 |
52 | #define DEFAULT_ENCODE_KEEP_BUFFER 1 | ||
52 | 53 | ||
53 | typedef enum { | 54 | typedef enum { |
54 | T_OBJ_BEGIN, | 55 | T_OBJ_BEGIN, |
@@ -101,6 +102,7 @@ typedef struct { | |||
101 | int encode_max_depth; | 102 | int encode_max_depth; |
102 | int encode_refuse_badnum; | 103 | int encode_refuse_badnum; |
103 | int decode_refuse_badnum; | 104 | int decode_refuse_badnum; |
105 | int encode_keep_buffer; | ||
104 | } json_config_t; | 106 | } json_config_t; |
105 | 107 | ||
106 | typedef struct { | 108 | typedef struct { |
@@ -172,7 +174,7 @@ static json_config_t *json_fetch_config(lua_State *l) | |||
172 | lua_gettable(l, LUA_REGISTRYINDEX); | 174 | lua_gettable(l, LUA_REGISTRYINDEX); |
173 | cfg = lua_touserdata(l, -1); | 175 | cfg = lua_touserdata(l, -1); |
174 | if (!cfg) | 176 | if (!cfg) |
175 | luaL_error(l, "BUG: Unable to fetch cjson configuration"); | 177 | luaL_error(l, "BUG: Unable to fetch CJSON configuration"); |
176 | 178 | ||
177 | lua_pop(l, 1); | 179 | lua_pop(l, 1); |
178 | 180 | ||
@@ -239,6 +241,23 @@ static int json_cfg_encode_max_depth(lua_State *l) | |||
239 | return 1; | 241 | return 1; |
240 | } | 242 | } |
241 | 243 | ||
244 | /* Configures JSON encoding buffer persistence */ | ||
245 | static int json_cfg_encode_keep_buffer(lua_State *l) | ||
246 | { | ||
247 | json_config_t *cfg; | ||
248 | |||
249 | json_verify_arg_count(l, 1); | ||
250 | cfg = json_fetch_config(l); | ||
251 | |||
252 | if (lua_gettop(l)) { | ||
253 | luaL_checktype(l, 1, LUA_TBOOLEAN); | ||
254 | cfg->encode_keep_buffer = lua_toboolean(l, 1); | ||
255 | } | ||
256 | |||
257 | lua_pushboolean(l, cfg->encode_keep_buffer); | ||
258 | |||
259 | return 1; | ||
260 | } | ||
242 | 261 | ||
243 | /* On argument: decode enum and set config variables | 262 | /* On argument: decode enum and set config variables |
244 | * **options must point to a NULL terminated array of 4 enums | 263 | * **options must point to a NULL terminated array of 4 enums |
@@ -317,6 +336,7 @@ static void json_create_config(lua_State *l) | |||
317 | cfg->encode_max_depth = DEFAULT_MAX_DEPTH; | 336 | cfg->encode_max_depth = DEFAULT_MAX_DEPTH; |
318 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; | 337 | cfg->encode_refuse_badnum = DEFAULT_ENCODE_REFUSE_BADNUM; |
319 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; | 338 | cfg->decode_refuse_badnum = DEFAULT_DECODE_REFUSE_BADNUM; |
339 | cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; | ||
320 | 340 | ||
321 | /* Decoding init */ | 341 | /* Decoding init */ |
322 | 342 | ||
@@ -392,8 +412,11 @@ static void json_create_config(lua_State *l) | |||
392 | 412 | ||
393 | /* ===== ENCODING ===== */ | 413 | /* ===== ENCODING ===== */ |
394 | 414 | ||
395 | static void json_encode_exception(lua_State *l, int lindex, const char *reason) | 415 | static void json_encode_exception(lua_State *l, json_config_t *cfg, int lindex, |
416 | const char *reason) | ||
396 | { | 417 | { |
418 | if (!cfg->encode_keep_buffer) | ||
419 | strbuf_free(&cfg->encode_buf); | ||
397 | luaL_error(l, "Cannot serialise %s: %s", | 420 | luaL_error(l, "Cannot serialise %s: %s", |
398 | lua_typename(l, lua_type(l, lindex)), reason); | 421 | lua_typename(l, lua_type(l, lindex)), reason); |
399 | } | 422 | } |
@@ -469,7 +492,7 @@ static int lua_array_length(lua_State *l, json_config_t *cfg) | |||
469 | max > items * cfg->encode_sparse_ratio && | 492 | max > items * cfg->encode_sparse_ratio && |
470 | max > cfg->encode_sparse_safe) { | 493 | max > cfg->encode_sparse_safe) { |
471 | if (!cfg->encode_sparse_convert) | 494 | if (!cfg->encode_sparse_convert) |
472 | json_encode_exception(l, -1, "excessively sparse array"); | 495 | json_encode_exception(l, cfg, -1, "excessively sparse array"); |
473 | 496 | ||
474 | return -1; | 497 | return -1; |
475 | } | 498 | } |
@@ -482,6 +505,8 @@ static void json_encode_descend(lua_State *l, json_config_t *cfg) | |||
482 | cfg->current_depth++; | 505 | cfg->current_depth++; |
483 | 506 | ||
484 | if (cfg->current_depth > cfg->encode_max_depth) { | 507 | if (cfg->current_depth > cfg->encode_max_depth) { |
508 | if (!cfg->encode_keep_buffer) | ||
509 | strbuf_free(&cfg->encode_buf); | ||
485 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", | 510 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", |
486 | cfg->current_depth); | 511 | cfg->current_depth); |
487 | } | 512 | } |
@@ -520,12 +545,12 @@ static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json, | |||
520 | } | 545 | } |
521 | 546 | ||
522 | static void json_append_number(lua_State *l, strbuf_t *json, int index, | 547 | static void json_append_number(lua_State *l, strbuf_t *json, int index, |
523 | int refuse_badnum) | 548 | json_config_t *cfg) |
524 | { | 549 | { |
525 | double num = lua_tonumber(l, index); | 550 | double num = lua_tonumber(l, index); |
526 | 551 | ||
527 | if (refuse_badnum && (isinf(num) || isnan(num))) | 552 | if (cfg->encode_refuse_badnum && (isinf(num) || isnan(num))) |
528 | json_encode_exception(l, index, "must not be NaN or Inf"); | 553 | json_encode_exception(l, cfg, index, "must not be NaN or Inf"); |
529 | 554 | ||
530 | strbuf_append_number(json, num); | 555 | strbuf_append_number(json, num); |
531 | } | 556 | } |
@@ -553,13 +578,13 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
553 | keytype = lua_type(l, -2); | 578 | keytype = lua_type(l, -2); |
554 | if (keytype == LUA_TNUMBER) { | 579 | if (keytype == LUA_TNUMBER) { |
555 | strbuf_append_char(json, '"'); | 580 | strbuf_append_char(json, '"'); |
556 | json_append_number(l, json, -2, cfg->encode_refuse_badnum); | 581 | json_append_number(l, json, -2, cfg); |
557 | strbuf_append_mem(json, "\":", 2); | 582 | strbuf_append_mem(json, "\":", 2); |
558 | } else if (keytype == LUA_TSTRING) { | 583 | } else if (keytype == LUA_TSTRING) { |
559 | json_append_string(l, json, -2); | 584 | json_append_string(l, json, -2); |
560 | strbuf_append_char(json, ':'); | 585 | strbuf_append_char(json, ':'); |
561 | } else { | 586 | } else { |
562 | json_encode_exception(l, -2, | 587 | json_encode_exception(l, cfg, -2, |
563 | "table key must be a number or string"); | 588 | "table key must be a number or string"); |
564 | /* never returns */ | 589 | /* never returns */ |
565 | } | 590 | } |
@@ -585,7 +610,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
585 | json_append_string(l, json, -1); | 610 | json_append_string(l, json, -1); |
586 | break; | 611 | break; |
587 | case LUA_TNUMBER: | 612 | case LUA_TNUMBER: |
588 | json_append_number(l, json, -1, cfg->encode_refuse_badnum); | 613 | json_append_number(l, json, -1, cfg); |
589 | break; | 614 | break; |
590 | case LUA_TBOOLEAN: | 615 | case LUA_TBOOLEAN: |
591 | if (lua_toboolean(l, -1)) | 616 | if (lua_toboolean(l, -1)) |
@@ -611,7 +636,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
611 | default: | 636 | default: |
612 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, | 637 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, |
613 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ | 638 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ |
614 | json_encode_exception(l, -1, "type not supported"); | 639 | json_encode_exception(l, cfg, -1, "type not supported"); |
615 | /* never returns */ | 640 | /* never returns */ |
616 | } | 641 | } |
617 | } | 642 | } |
@@ -629,14 +654,21 @@ static int json_encode(lua_State *l) | |||
629 | cfg = json_fetch_config(l); | 654 | cfg = json_fetch_config(l); |
630 | cfg->current_depth = 0; | 655 | cfg->current_depth = 0; |
631 | 656 | ||
632 | /* Reset persistent encode_buf. Avoids temporary allocation | 657 | /* Reset the persistent buffer if it exists. |
633 | * for a single call. */ | 658 | * Otherwise allocate a new buffer. */ |
634 | strbuf_reset(&cfg->encode_buf); | 659 | if (strbuf_allocated(&cfg->encode_buf)) |
660 | strbuf_reset(&cfg->encode_buf); | ||
661 | else | ||
662 | strbuf_init(&cfg->encode_buf, 0); | ||
663 | |||
635 | json_append_data(l, cfg, &cfg->encode_buf); | 664 | json_append_data(l, cfg, &cfg->encode_buf); |
636 | json = strbuf_string(&cfg->encode_buf, &len); | 665 | json = strbuf_string(&cfg->encode_buf, &len); |
637 | 666 | ||
638 | lua_pushlstring(l, json, len); | 667 | lua_pushlstring(l, json, len); |
639 | 668 | ||
669 | if (!cfg->encode_keep_buffer) | ||
670 | strbuf_free(&cfg->encode_buf); | ||
671 | |||
640 | return 1; | 672 | return 1; |
641 | } | 673 | } |
642 | 674 | ||
@@ -1176,7 +1208,7 @@ static int json_decode(lua_State *l) | |||
1176 | /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) | 1208 | /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) |
1177 | * | 1209 | * |
1178 | * CJSON can support any simple data type, hence only the first | 1210 | * CJSON can support any simple data type, hence only the first |
1179 | * character is guaranteed to be ASCII (at worst: "). This is | 1211 | * character is guaranteed to be ASCII (at worst: '"'). This is |
1180 | * still enough to detect whether the wrong encoding is in use. */ | 1212 | * still enough to detect whether the wrong encoding is in use. */ |
1181 | if (len >= 2 && (!json[0] || !json[1])) | 1213 | if (len >= 2 && (!json[0] || !json[1])) |
1182 | luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); | 1214 | luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); |
@@ -1195,6 +1227,7 @@ int luaopen_cjson(lua_State *l) | |||
1195 | { "decode", json_decode }, | 1227 | { "decode", json_decode }, |
1196 | { "encode_sparse_array", json_cfg_encode_sparse_array }, | 1228 | { "encode_sparse_array", json_cfg_encode_sparse_array }, |
1197 | { "encode_max_depth", json_cfg_encode_max_depth }, | 1229 | { "encode_max_depth", json_cfg_encode_max_depth }, |
1230 | { "encode_keep_buffer", json_cfg_encode_keep_buffer }, | ||
1198 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, | 1231 | { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, |
1199 | { NULL, NULL } | 1232 | { NULL, NULL } |
1200 | }; | 1233 | }; |
@@ -99,12 +99,16 @@ static inline void debug_stats(strbuf_t *s) | |||
99 | } | 99 | } |
100 | } | 100 | } |
101 | 101 | ||
102 | /* If strbuf_t has not been dynamically allocated, strbuf_free() can | ||
103 | * be called any number of times strbuf_init() */ | ||
102 | void strbuf_free(strbuf_t *s) | 104 | void strbuf_free(strbuf_t *s) |
103 | { | 105 | { |
104 | debug_stats(s); | 106 | debug_stats(s); |
105 | 107 | ||
106 | if (s->buf) | 108 | if (s->buf) { |
107 | free(s->buf); | 109 | free(s->buf); |
110 | s->buf = NULL; | ||
111 | } | ||
108 | if (s->dynamic) | 112 | if (s->dynamic) |
109 | free(s); | 113 | free(s); |
110 | } | 114 | } |
@@ -78,6 +78,11 @@ static inline void strbuf_reset(strbuf_t *s) | |||
78 | s->length = 0; | 78 | s->length = 0; |
79 | } | 79 | } |
80 | 80 | ||
81 | static inline int strbuf_allocated(strbuf_t *s) | ||
82 | { | ||
83 | return s->buf != NULL; | ||
84 | } | ||
85 | |||
81 | /* Return bytes remaining in the string buffer | 86 | /* Return bytes remaining in the string buffer |
82 | * Ensure there is space for a NULL terminator. */ | 87 | * Ensure there is space for a NULL terminator. */ |
83 | static inline int strbuf_empty_length(strbuf_t *s) | 88 | static inline int strbuf_empty_length(strbuf_t *s) |
diff --git a/tests/test.lua b/tests/test.lua index 1408bb9..4bb62be 100755 --- a/tests/test.lua +++ b/tests/test.lua | |||
@@ -181,7 +181,7 @@ local decode_error_tests = { | |||
181 | { json.decode, { '[ 0.4eg10 ]' }, | 181 | { json.decode, { '[ 0.4eg10 ]' }, |
182 | false, { "Expected comma or array end but found invalid token at character 6" } }, | 182 | false, { "Expected comma or array end but found invalid token at character 6" } }, |
183 | { json.decode, { json_nested }, | 183 | { json.decode, { json_nested }, |
184 | false, { "stack overflow (too many nested data structures)" } } | 184 | false, { "Too many nested data structures" } } |
185 | } | 185 | } |
186 | 186 | ||
187 | local escape_tests = { | 187 | local escape_tests = { |