aboutsummaryrefslogtreecommitdiff
path: root/lua_cjson.c
diff options
context:
space:
mode:
authorMark Pulford <mark@kyne.com.au>2011-05-01 04:16:24 +0930
committerMark Pulford <mark@kyne.com.au>2011-05-01 04:16:24 +0930
commit2fc7477b155cdecfee3b0a47203c0706c27db73e (patch)
tree840705c2246f4f88fff11f23310648112cf9f77f /lua_cjson.c
parentaf1fe38223e23b2bb90f65bc6dd1b7e0dd5cb5cd (diff)
downloadlua-cjson-2fc7477b155cdecfee3b0a47203c0706c27db73e.tar.gz
lua-cjson-2fc7477b155cdecfee3b0a47203c0706c27db73e.tar.bz2
lua-cjson-2fc7477b155cdecfee3b0a47203c0706c27db73e.zip
Move static configuration into runtime userdata
Allow maximum nesting depth and sparse array ratio to be configured at runtime via the sparse_ratio() and max_depth() functions. Throw exceptions when encoding excessively nested structures.
Diffstat (limited to 'lua_cjson.c')
-rw-r--r--lua_cjson.c367
1 files changed, 230 insertions, 137 deletions
diff --git a/lua_cjson.c b/lua_cjson.c
index b87aebf..b2869c1 100644
--- a/lua_cjson.c
+++ b/lua_cjson.c
@@ -20,31 +20,197 @@
20 20
21/* FIXME: 21/* FIXME:
22 * - Option to encode non-printable characters? Only \" \\ are required 22 * - Option to encode non-printable characters? Only \" \\ are required
23 * - Protect against cycles when encoding JSON from a data structure
24 * - Max depth? Notice cycles?
25 */ 23 */
26 24
27#include <assert.h> 25#include <assert.h>
28#include <string.h> 26#include <string.h>
29#include <math.h> 27#include <math.h>
30
31#include <pthread.h>
32
33#include <lua.h> 28#include <lua.h>
34#include <lauxlib.h> 29#include <lauxlib.h>
35 30
36#include "strbuf.h" 31#include "strbuf.h"
37 32
38/* Encode very sparse arrays as objects */ 33
39#ifndef VERY_SPARSE_ARRAY_RATIO 34#define CJSON_CONFIG_KEY "cjson_configdata"
40#define VERY_SPARSE_ARRAY_RATIO 2 35#define DEFAULT_SPARSE_RATIO 2
41#endif 36#define DEFAULT_MAX_DEPTH 20
37
38typedef enum {
39 T_OBJ_BEGIN,
40 T_OBJ_END,
41 T_ARR_BEGIN,
42 T_ARR_END,
43 T_STRING,
44 T_NUMBER,
45 T_BOOLEAN,
46 T_NULL,
47 T_COLON,
48 T_COMMA,
49 T_END,
50 T_WHITESPACE,
51 T_ERROR,
52 T_UNKNOWN
53} json_token_type_t;
54
55static const char *json_token_type_name[] = {
56 "T_OBJ_BEGIN",
57 "T_OBJ_END",
58 "T_ARR_BEGIN",
59 "T_ARR_END",
60 "T_STRING",
61 "T_NUMBER",
62 "T_BOOLEAN",
63 "T_NULL",
64 "T_COLON",
65 "T_COMMA",
66 "T_END",
67 "T_WHITESPACE",
68 "T_ERROR",
69 "T_UNKNOWN",
70 NULL
71};
72
73typedef struct {
74 json_token_type_t ch2token[256];
75 char ch2escape[256];
76 int sparse_ratio;
77 int max_depth;
78 int current_depth;
79} json_config_t;
80
81typedef struct {
82 const char *data;
83 int index;
84 strbuf_t *tmp; /* Temporary storage for strings */
85 json_config_t *cfg;
86} json_parse_t;
87
88typedef struct {
89 json_token_type_t type;
90 int index;
91 union {
92 char *string;
93 double number;
94 int boolean;
95 } value;
96 int length; /* FIXME: Merge into union? Won't save memory, but more logical */
97} json_token_t;
98
99/* ===== CONFIGURATION ===== */
100
101static json_config_t *json_fetch_config(lua_State *l)
102{
103 json_config_t *cfg;
104
105 lua_getfield(l, LUA_REGISTRYINDEX, CJSON_CONFIG_KEY);
106 cfg = lua_touserdata(l, -1);
107 if (!cfg)
108 luaL_error(l, "BUG: Unable to fetch cjson configuration");
109
110 lua_pop(l, 1);
111
112 return cfg;
113}
114
115static int json_sparse_ratio(lua_State *l)
116{
117 json_config_t *cfg;
118 int sparse_ratio;
119 int args;
120
121 args = lua_gettop(l);
122 luaL_argcheck(l, args <= 1, 2, "found too many arguments");
123
124 cfg = json_fetch_config(l);
125
126 if (args == 1) {
127 sparse_ratio = luaL_checkinteger(l, 1);
128 luaL_argcheck(l, sparse_ratio >= 0, 1,
129 "expected zero or positive integer");
130 cfg->sparse_ratio = sparse_ratio;
131 }
132
133 lua_pushinteger(l, cfg->sparse_ratio);
134
135 return 1;
136}
137
138static int json_max_depth(lua_State *l)
139{
140 json_config_t *cfg;
141 int max_depth;
142 int args;
143
144 args = lua_gettop(l);
145 luaL_argcheck(l, args <= 1, 2, "found too many arguments");
146
147 cfg = json_fetch_config(l);
148
149 if (args == 1) {
150 max_depth = luaL_checkinteger(l, 1);
151 luaL_argcheck(l, max_depth > 0, 1, "expected positive integer");
152 cfg->max_depth = max_depth;
153 }
154
155 lua_pushinteger(l, cfg->max_depth);
156
157 return 1;
158}
159
160static void json_create_config(lua_State *l)
161{
162 json_config_t *cfg;
163 int i;
164
165 cfg = lua_newuserdata(l, sizeof(*cfg));
166
167 /* Tag all characters as an error */
168 for (i = 0; i < 256; i++)
169 cfg->ch2token[i] = T_ERROR;
170
171 /* Set tokens that require no further processing */
172 cfg->ch2token['{'] = T_OBJ_BEGIN;
173 cfg->ch2token['}'] = T_OBJ_END;
174 cfg->ch2token['['] = T_ARR_BEGIN;
175 cfg->ch2token[']'] = T_ARR_END;
176 cfg->ch2token[','] = T_COMMA;
177 cfg->ch2token[':'] = T_COLON;
178 cfg->ch2token['\0'] = T_END;
179 cfg->ch2token[' '] = T_WHITESPACE;
180 cfg->ch2token['\t'] = T_WHITESPACE;
181 cfg->ch2token['\n'] = T_WHITESPACE;
182 cfg->ch2token['\r'] = T_WHITESPACE;
183
184 /* Update characters that require further processing */
185 cfg->ch2token['n'] = T_UNKNOWN;
186 cfg->ch2token['t'] = T_UNKNOWN;
187 cfg->ch2token['f'] = T_UNKNOWN;
188 cfg->ch2token['"'] = T_UNKNOWN;
189 cfg->ch2token['-'] = T_UNKNOWN;
190 for (i = 0; i < 10; i++)
191 cfg->ch2token['0' + i] = T_UNKNOWN;
192
193 for (i = 0; i < 256; i++)
194 cfg->ch2escape[i] = 0; /* String error */
195
196 cfg->ch2escape['"'] = '"';
197 cfg->ch2escape['\\'] = '\\';
198 cfg->ch2escape['/'] = '/';
199 cfg->ch2escape['b'] = '\b';
200 cfg->ch2escape['t'] = '\t';
201 cfg->ch2escape['n'] = '\n';
202 cfg->ch2escape['f'] = '\f';
203 cfg->ch2escape['r'] = '\r';
204 cfg->ch2escape['u'] = 'u'; /* This needs to be parsed as unicode */
205
206 cfg->sparse_ratio = DEFAULT_SPARSE_RATIO;
207 cfg->max_depth = DEFAULT_MAX_DEPTH;
208}
42 209
43/* ===== ENCODING ===== */ 210/* ===== ENCODING ===== */
44 211
45static void json_encode_exception(lua_State *l, strbuf_t *json, 212static void json_encode_type_exception(lua_State *l, strbuf_t *json,
46 char *location, int lindex) 213 char *location, int lindex)
47
48{ 214{
49 strbuf_free(json); 215 strbuf_free(json);
50 216
@@ -113,7 +279,7 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
113 * -1 object (not a pure array) 279 * -1 object (not a pure array)
114 * >=0 elements in array 280 * >=0 elements in array
115 */ 281 */
116static int lua_array_length(lua_State *l) 282static int lua_array_length(lua_State *l, int sparse_ratio)
117{ 283{
118 double k; 284 double k;
119 int max; 285 int max;
@@ -143,25 +309,38 @@ static int lua_array_length(lua_State *l)
143 return -1; 309 return -1;
144 } 310 }
145 311
146#ifdef VERY_SPARSE_ARRAY_RATIO 312 /* Encode very sparse arrays as objects (if enabled) */
147 /* Encode very sparse arrays as objects */ 313 if (sparse_ratio > 0 && max > items * sparse_ratio)
148 if (max > items * VERY_SPARSE_ARRAY_RATIO)
149 return -1; 314 return -1;
150#endif
151 315
152 return max; 316 return max;
153} 317}
154 318
155static void json_append_data(lua_State *l, strbuf_t *json); 319static void json_encode_descend(lua_State *l, json_config_t *cfg,
320 strbuf_t *json)
321{
322 cfg->current_depth++;
323
324 if (cfg->current_depth > cfg->max_depth) {
325 strbuf_free(json);
326 luaL_error(l, "Cannot serialise, excessive nesting (%d)",
327 cfg->current_depth);
328 }
329}
330
331static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json);
156 332
157/* json_append_array args: 333/* json_append_array args:
158 * - lua_State 334 * - lua_State
159 * - JSON strbuf 335 * - JSON strbuf
160 * - Size of passwd Lua array (top of stack) */ 336 * - Size of passwd Lua array (top of stack) */
161static void json_append_array(lua_State *l, strbuf_t *json, int array_length) 337static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json,
338 int array_length)
162{ 339{
163 int comma, i; 340 int comma, i;
164 341
342 json_encode_descend(l, cfg, json);
343
165 strbuf_append_string(json, "[ "); 344 strbuf_append_string(json, "[ ");
166 345
167 comma = 0; 346 comma = 0;
@@ -172,17 +351,22 @@ static void json_append_array(lua_State *l, strbuf_t *json, int array_length)
172 comma = 1; 351 comma = 1;
173 352
174 lua_rawgeti(l, -1, i); 353 lua_rawgeti(l, -1, i);
175 json_append_data(l, json); 354 json_append_data(l, cfg, json);
176 lua_pop(l, 1); 355 lua_pop(l, 1);
177 } 356 }
178 357
179 strbuf_append_string(json, " ]"); 358 strbuf_append_string(json, " ]");
359
360 cfg->current_depth--;
180} 361}
181 362
182static void json_append_object(lua_State *l, strbuf_t *json) 363static void json_append_object(lua_State *l, json_config_t *cfg,
364 strbuf_t *json)
183{ 365{
184 int comma, keytype; 366 int comma, keytype;
185 367
368 json_encode_descend(l, cfg, json);
369
186 /* Object */ 370 /* Object */
187 strbuf_append_string(json, "{ "); 371 strbuf_append_string(json, "{ ");
188 372
@@ -204,21 +388,23 @@ static void json_append_object(lua_State *l, strbuf_t *json)
204 json_append_string(l, json, -2); 388 json_append_string(l, json, -2);
205 strbuf_append_string(json, ": "); 389 strbuf_append_string(json, ": ");
206 } else { 390 } else {
207 json_encode_exception(l, json, "table key", -2); 391 json_encode_type_exception(l, json, "table key", -2);
208 /* never returns */ 392 /* never returns */
209 } 393 }
210 394
211 /* table, key, value */ 395 /* table, key, value */
212 json_append_data(l, json); 396 json_append_data(l, cfg, json);
213 lua_pop(l, 1); 397 lua_pop(l, 1);
214 /* table, key */ 398 /* table, key */
215 } 399 }
216 400
217 strbuf_append_string(json, " }"); 401 strbuf_append_string(json, " }");
402
403 cfg->current_depth--;
218} 404}
219 405
220/* Serialise Lua data into JSON string. */ 406/* Serialise Lua data into JSON string. */
221static void json_append_data(lua_State *l, strbuf_t *json) 407static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json)
222{ 408{
223 int len; 409 int len;
224 410
@@ -236,11 +422,11 @@ static void json_append_data(lua_State *l, strbuf_t *json)
236 strbuf_append_string(json, "false"); 422 strbuf_append_string(json, "false");
237 break; 423 break;
238 case LUA_TTABLE: 424 case LUA_TTABLE:
239 len = lua_array_length(l); 425 len = lua_array_length(l, cfg->sparse_ratio);
240 if (len > 0) 426 if (len > 0)
241 json_append_array(l, json, len); 427 json_append_array(l, cfg, json, len);
242 else 428 else
243 json_append_object(l, json); 429 json_append_object(l, cfg, json);
244 break; 430 break;
245 case LUA_TNIL: 431 case LUA_TNIL:
246 strbuf_append_string(json, "null"); 432 strbuf_append_string(json, "null");
@@ -253,21 +439,25 @@ static void json_append_data(lua_State *l, strbuf_t *json)
253 default: 439 default:
254 /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, 440 /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
255 * and LUA_TLIGHTUSERDATA) cannot be serialised */ 441 * and LUA_TLIGHTUSERDATA) cannot be serialised */
256 json_encode_exception(l, json, "value", -1); 442 json_encode_type_exception(l, json, "value", -1);
257 /* never returns */ 443 /* never returns */
258 } 444 }
259} 445}
260 446
261static int json_encode(lua_State *l) 447static int json_encode(lua_State *l)
262{ 448{
449 json_config_t *cfg;
263 strbuf_t buf; 450 strbuf_t buf;
264 char *json; 451 char *json;
265 int len; 452 int len;
266 453
267 luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); 454 luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
268 455
456 cfg = json_fetch_config(l);
457 cfg->current_depth = 0;
458
269 strbuf_init(&buf, 0); 459 strbuf_init(&buf, 0);
270 json_append_data(l, &buf); 460 json_append_data(l, cfg, &buf);
271 json = strbuf_free_to_string(&buf, &len); 461 json = strbuf_free_to_string(&buf, &len);
272 462
273 lua_pushlstring(l, json, len); 463 lua_pushlstring(l, json, len);
@@ -278,107 +468,8 @@ static int json_encode(lua_State *l)
278 468
279/* ===== DECODING ===== */ 469/* ===== DECODING ===== */
280 470
281typedef struct {
282 const char *data;
283 int index;
284 strbuf_t *tmp; /* Temporary storage for strings */
285} json_parse_t;
286
287typedef enum {
288 T_OBJ_BEGIN,
289 T_OBJ_END,
290 T_ARR_BEGIN,
291 T_ARR_END,
292 T_STRING,
293 T_NUMBER,
294 T_BOOLEAN,
295 T_NULL,
296 T_COLON,
297 T_COMMA,
298 T_END,
299 T_WHITESPACE,
300 T_ERROR,
301 T_UNKNOWN
302} json_token_type_t;
303
304static const char *json_token_type_name[] = {
305 "T_OBJ_BEGIN",
306 "T_OBJ_END",
307 "T_ARR_BEGIN",
308 "T_ARR_END",
309 "T_STRING",
310 "T_NUMBER",
311 "T_BOOLEAN",
312 "T_NULL",
313 "T_COLON",
314 "T_COMMA",
315 "T_END",
316 "T_WHITESPACE",
317 "T_ERROR",
318 "T_UNKNOWN",
319 NULL
320};
321
322typedef struct {
323 json_token_type_t type;
324 int index;
325 union {
326 char *string;
327 double number;
328 int boolean;
329 } value;
330 int length; /* FIXME: Merge into union? Won't save memory, but more logical */
331} json_token_t;
332
333static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *token); 471static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *token);
334 472
335static json_token_type_t json_ch2token[256];
336static char json_ch2escape[256];
337
338static void json_global_init()
339{
340 int i;
341
342 /* Tag all characters as an error */
343 for (i = 0; i < 256; i++)
344 json_ch2token[i] = T_ERROR;
345
346 /* Set tokens that require no further processing */
347 json_ch2token['{'] = T_OBJ_BEGIN;
348 json_ch2token['}'] = T_OBJ_END;
349 json_ch2token['['] = T_ARR_BEGIN;
350 json_ch2token[']'] = T_ARR_END;
351 json_ch2token[','] = T_COMMA;
352 json_ch2token[':'] = T_COLON;
353 json_ch2token['\0'] = T_END;
354 json_ch2token[' '] = T_WHITESPACE;
355 json_ch2token['\t'] = T_WHITESPACE;
356 json_ch2token['\n'] = T_WHITESPACE;
357 json_ch2token['\r'] = T_WHITESPACE;
358
359 /* Update characters that require further processing */
360 json_ch2token['n'] = T_UNKNOWN;
361 json_ch2token['t'] = T_UNKNOWN;
362 json_ch2token['f'] = T_UNKNOWN;
363 json_ch2token['"'] = T_UNKNOWN;
364 json_ch2token['-'] = T_UNKNOWN;
365 for (i = 0; i < 10; i++)
366 json_ch2token['0' + i] = T_UNKNOWN;
367
368 for (i = 0; i < 256; i++)
369 json_ch2escape[i] = 0; /* String error */
370
371 json_ch2escape['"'] = '"';
372 json_ch2escape['\\'] = '\\';
373 json_ch2escape['/'] = '/';
374 json_ch2escape['b'] = '\b';
375 json_ch2escape['t'] = '\t';
376 json_ch2escape['n'] = '\n';
377 json_ch2escape['f'] = '\f';
378 json_ch2escape['r'] = '\r';
379 json_ch2escape['u'] = 'u'; /* This needs to be parsed as unicode */
380}
381
382static inline int hexdigit2int(char hex) 473static inline int hexdigit2int(char hex)
383{ 474{
384 if ('0' <= hex && hex <= '9') 475 if ('0' <= hex && hex <= '9')
@@ -473,6 +564,7 @@ static int json_append_unicode_escape(json_parse_t *json)
473 564
474static void json_next_string_token(json_parse_t *json, json_token_t *token) 565static void json_next_string_token(json_parse_t *json, json_token_t *token)
475{ 566{
567 char *ch2escape = json->cfg->ch2escape;
476 char ch; 568 char ch;
477 569
478 /* Caller must ensure a string is next */ 570 /* Caller must ensure a string is next */
@@ -498,7 +590,7 @@ static void json_next_string_token(json_parse_t *json, json_token_t *token)
498 ch = json->data[json->index]; 590 ch = json->data[json->index];
499 591
500 /* Translate escape code and append to tmp string */ 592 /* Translate escape code and append to tmp string */
501 ch = json_ch2escape[(unsigned char)ch]; 593 ch = ch2escape[(unsigned char)ch];
502 if (ch == 'u') { 594 if (ch == 'u') {
503 if (json_append_unicode_escape(json) < 0) 595 if (json_append_unicode_escape(json) < 0)
504 continue; 596 continue;
@@ -561,12 +653,13 @@ static void json_next_number_token(json_parse_t *json, json_token_t *token)
561 */ 653 */
562static void json_next_token(json_parse_t *json, json_token_t *token) 654static void json_next_token(json_parse_t *json, json_token_t *token)
563{ 655{
656 json_token_type_t *ch2token = json->cfg->ch2token;
564 int ch; 657 int ch;
565 658
566 /* Eat whitespace. FIXME: UGLY */ 659 /* Eat whitespace. FIXME: UGLY */
567 token->type = json_ch2token[(unsigned char)json->data[json->index]]; 660 token->type = ch2token[(unsigned char)json->data[json->index]];
568 while (token->type == T_WHITESPACE) 661 while (token->type == T_WHITESPACE)
569 token->type = json_ch2token[(unsigned char)json->data[++json->index]]; 662 token->type = ch2token[(unsigned char)json->data[++json->index]];
570 663
571 token->index = json->index; 664 token->index = json->index;
572 665
@@ -735,6 +828,7 @@ static void lua_json_decode(lua_State *l, const char *json_text)
735 json_parse_t json; 828 json_parse_t json;
736 json_token_t token; 829 json_token_t token;
737 830
831 json.cfg = json_fetch_config(l);
738 json.data = json_text; 832 json.data = json_text;
739 json.index = 0; 833 json.index = 0;
740 json.tmp = strbuf_new(0); 834 json.tmp = strbuf_new(0);
@@ -765,26 +859,25 @@ static int json_decode(lua_State *l)
765 859
766/* ===== INITIALISATION ===== */ 860/* ===== INITIALISATION ===== */
767 861
768/* FIXME: Rewrite to keep lookup tables within Lua (userdata?)
769 * Remove pthread dependency */
770static pthread_once_t json_global_init_once = PTHREAD_ONCE_INIT;
771
772int luaopen_cjson(lua_State *l) 862int luaopen_cjson(lua_State *l)
773{ 863{
774 luaL_Reg reg[] = { 864 luaL_Reg reg[] = {
775 { "encode", json_encode }, 865 { "encode", json_encode },
776 { "decode", json_decode }, 866 { "decode", json_decode },
867 { "sparse_ratio", json_sparse_ratio },
868 { "max_depth", json_max_depth },
777 { NULL, NULL } 869 { NULL, NULL }
778 }; 870 };
779 871
872 json_create_config(l);
873 lua_setfield(l, LUA_REGISTRYINDEX, CJSON_CONFIG_KEY);
874
780 luaL_register(l, "cjson", reg); 875 luaL_register(l, "cjson", reg);
781 876
782 /* Set cjson.null */ 877 /* Set cjson.null */
783 lua_pushlightuserdata(l, NULL); 878 lua_pushlightuserdata(l, NULL);
784 lua_setfield(l, -2, "null"); 879 lua_setfield(l, -2, "null");
785 880
786 pthread_once(&json_global_init_once, json_global_init);
787
788 /* Return cjson table */ 881 /* Return cjson table */
789 return 1; 882 return 1;
790} 883}