webmcp

view libraries/json/json.c @ 166:7885d1ae35ff

Minor bugfixes in JSON library (including checking type of arguments)
author jbe
date Thu Jul 31 23:33:28 2014 +0200 (2014-07-31)
parents 0c230f701967
children 84497222db4e
line source
1 #include <lua.h>
2 #include <lauxlib.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <math.h>
7 // maximum number of nested JSON values (objects and arrays):
8 // NOTE: The Lua reference states that the stack may typically contain at least
9 // "a few thousand elements". Since every nested level consumes
10 // 3 elements on the Lua stack (the object/array, its shadow table,
11 // a string key or a placeholder), we limit the number of nested levels
12 // to 500. If a stack overflow would still happen in the import function,
13 // this is detected nevertheless and an error is thrown (instead of
14 // returning nil and an error string).
15 #define JSON_MAXDEPTH 500
17 // generate dummy memory addresses that represents null values:
18 char json_nullmark;
19 #define json_isnullmark(L, i) (lua_touserdata((L), (i)) == &json_nullmark)
20 #define json_pushnullmark(L) lua_pushlightuserdata((L), &json_nullmark)
22 // macros for usage of Lua registry:
23 #define JSON_REGENT char
24 #define JSON_REGPOINTER void *
25 #define json_regpointer(x) (&json_registry.x)
26 #define json_regfetchpointer(L, x) lua_rawgetp((L), LUA_REGISTRYINDEX, (x))
27 #define json_regfetch(L, x) json_regfetchpointer(L, json_regpointer(x))
28 #define json_regstore(L, x) lua_rawsetp(L, LUA_REGISTRYINDEX, json_regpointer(x))
30 // generate dummy memory addresses that represent Lua objects
31 // via lightuserdata keys and LUA_REGISTRYINDEX:
32 static struct {
33 JSON_REGENT shadowtbl; // ephemeron table that maps tables to their corresponding shadow table
34 JSON_REGENT objectmt; // metatable for JSON objects
35 JSON_REGENT arraymt; // metatable for JSON arrays
36 } json_registry;
38 // returns the string "<JSON null marker>":
39 static int json_nullmark_tostring(lua_State *L) {
40 lua_pushliteral(L, "<JSON null marker>");
41 return 1;
42 }
44 // marks a Lua table as JSON object or JSON array:
45 // (returns its modified argument or a new table if argument is nil)
46 static int json_mark(lua_State *L, JSON_REGPOINTER mt) {
47 // check if argument is nil
48 if (lua_isnoneornil(L, 1)) {
49 // create new table at stack position 1:
50 lua_settop(L, 0);
51 lua_newtable(L);
52 // create shadow table (leaving previously created table on stack position 1):
53 json_regfetch(L, shadowtbl);
54 lua_pushvalue(L, 1);
55 lua_newtable(L);
56 lua_rawset(L, -3);
57 } else {
58 // require argument to be a table:
59 luaL_checktype(L, 1, LUA_TTABLE);
60 // push shadow table on top of stack:
61 json_regfetch(L, shadowtbl);
62 lua_pushvalue(L, 1);
63 lua_rawget(L, -2);
64 // if shadow table does not exist:
65 if (lua_isnil(L, -1)) {
66 // create shadow table and leave it on top of stack:
67 lua_newtable(L);
68 lua_pushvalue(L, 1);
69 lua_pushvalue(L, -2);
70 lua_rawset(L, -5);
71 }
72 // move elements from original table to shadow table (that's expected on top of stack):
73 for(lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
74 lua_pushvalue(L, -2);
75 lua_pushnil(L);
76 lua_rawset(L, 1);
77 lua_pushvalue(L, -2);
78 lua_pushvalue(L, -2);
79 lua_rawset(L, -5);
80 }
81 }
82 // discard everything but table to return:
83 lua_settop(L, 1);
84 // set metatable:
85 json_regfetchpointer(L, mt);
86 lua_setmetatable(L, 1);
87 // return table:
88 return 1;
89 }
91 // marks a table as JSON object:
92 // (returns its modified argument or a new table if argument is nil)
93 static int json_object(lua_State *L) {
94 return json_mark(L, json_regpointer(objectmt));
95 }
97 // marks a table as JSON array:
98 // (returns its modified argument or a new table if argument is nil)
99 static int json_array(lua_State *L) {
100 return json_mark(L, json_regpointer(arraymt));
101 }
103 // internal states of JSON parser:
104 #define JSON_STATE_VALUE 0
105 #define JSON_STATE_OBJECT_KEY 1
106 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
107 #define JSON_STATE_OBJECT_VALUE 3
108 #define JSON_STATE_OBJECT_SEPARATOR 4
109 #define JSON_STATE_ARRAY_VALUE 5
110 #define JSON_STATE_ARRAY_SEPARATOR 6
111 #define JSON_STATE_END 7
113 // special Lua stack indicies for json_import function:
114 #define json_import_objectmt_idx 2
115 #define json_import_arraymt_idx 3
116 #define json_import_shadowtbl_idx 4
118 // decodes a JSON document:
119 static int json_import(lua_State *L) {
120 const char *str; // string to parse
121 size_t total; // total length of string to parse
122 size_t pos = 0; // current position in string to parse
123 size_t level = 0; // nested levels of objects/arrays currently being processed
124 int mode = JSON_STATE_VALUE; // state of parser (i.e. "what's expected next?")
125 char c; // variable to store a single character to be processed
126 luaL_Buffer luabuf; // Lua buffer to decode JSON string values
127 char *cbuf; // C buffer to decode JSON string values
128 size_t outlen; // maximum length or write position of C buffer
129 size_t arraylen; // variable to temporarily store the array length
130 // require string as argument and convert to C string with length information:
131 str = luaL_checklstring(L, 1, &total);
132 // if string contains a NULL byte, this is a syntax error
133 if (strlen(str) != total) goto json_import_syntax_error;
134 // stack shall contain one function argument:
135 lua_settop(L, 1);
136 // push objectmt onto stack position 2:
137 json_regfetch(L, objectmt);
138 // push arraymt onto stack position 3:
139 json_regfetch(L, arraymt);
140 // push shadowtbl onto stack position 4:
141 json_regfetch(L, shadowtbl);
142 // main loop of parser:
143 json_import_loop:
144 // skip whitespace and store next character in variable 'c':
145 while (c = str[pos],
146 c == ' ' ||
147 c == '\f' ||
148 c == '\n' ||
149 c == '\r' ||
150 c == '\t' ||
151 c == '\v'
152 ) pos++;
153 // switch statement to handle certain (single) characters:
154 switch (c) {
155 // handle end of JSON document:
156 case 0:
157 // if end of JSON document was expected, then return top element of stack as result:
158 if (mode == JSON_STATE_END) return 1;
159 // otherwise, the JSON document was malformed:
160 json_import_unexpected_eof:
161 lua_pushnil(L);
162 if (level == 0) lua_pushliteral(L, "Empty string");
163 else lua_pushliteral(L, "Unexpected end of JSON document");
164 return 2;
165 // new JSON object:
166 case '{':
167 // if a JSON object is not expected here, then return an error:
168 if (
169 mode != JSON_STATE_VALUE &&
170 mode != JSON_STATE_OBJECT_VALUE &&
171 mode != JSON_STATE_ARRAY_VALUE
172 ) goto json_import_syntax_error;
173 // create JSON object on stack:
174 lua_newtable(L);
175 // set metatable of JSON object:
176 lua_pushvalue(L, json_import_objectmt_idx);
177 lua_setmetatable(L, -2);
178 // create internal shadow table on stack:
179 lua_newtable(L);
180 // register internal shadow table:
181 lua_pushvalue(L, -2);
182 lua_pushvalue(L, -2);
183 lua_rawset(L, json_import_shadowtbl_idx);
184 // expect object key (or end of object) to follow:
185 mode = JSON_STATE_OBJECT_KEY;
186 // jump to common code for opening JSON object and JSON array:
187 goto json_import_open;
188 // new JSON array:
189 case '[':
190 // if a JSON array is not expected here, then return an error:
191 if (
192 mode != JSON_STATE_VALUE &&
193 mode != JSON_STATE_OBJECT_VALUE &&
194 mode != JSON_STATE_ARRAY_VALUE
195 ) goto json_import_syntax_error;
196 // create JSON array on stack:
197 lua_newtable(L);
198 // set metatable of JSON array:
199 lua_pushvalue(L, json_import_arraymt_idx);
200 lua_setmetatable(L, -2);
201 // create internal shadow table on stack:
202 lua_newtable(L);
203 // register internal shadow table:
204 lua_pushvalue(L, -2);
205 lua_pushvalue(L, -2);
206 lua_rawset(L, json_import_shadowtbl_idx);
207 // add nil as key (needed to keep stack balance) and as magic to detect arrays:
208 lua_pushnil(L);
209 // expect array value (or end of array) to follow:
210 mode = JSON_STATE_ARRAY_VALUE;
211 // continue with common code for opening JSON object and JSON array:
212 // common code for opening JSON object or JSON array:
213 json_import_open:
214 // limit nested levels:
215 if (level >= JSON_MAXDEPTH) {
216 lua_pushnil(L);
217 lua_pushfstring(L, "More than %d nested JSON levels", JSON_MAXDEPTH);
218 return 2;
219 }
220 // additional buffer overflow protection:
221 if (!lua_checkstack(L, LUA_MINSTACK))
222 return luaL_error(L, "Caught stack overflow in JSON import function (too many nested levels and stack size too small)");
223 // increment level:
224 level++;
225 // consume input character:
226 pos++;
227 goto json_import_loop;
228 // end of JSON object:
229 case '}':
230 // if end of JSON object is not expected here, then return an error:
231 if (
232 mode != JSON_STATE_OBJECT_KEY &&
233 mode != JSON_STATE_OBJECT_SEPARATOR
234 ) goto json_import_syntax_error;
235 // jump to common code for end of JSON object and JSON array:
236 goto json_import_close;
237 // end of JSON array:
238 case ']':
239 // if end of JSON array is not expected here, then return an error:
240 if (
241 mode != JSON_STATE_ARRAY_VALUE &&
242 mode != JSON_STATE_ARRAY_SEPARATOR
243 ) goto json_import_syntax_error;
244 // pop nil key/magic (that was needed to keep stack balance):
245 lua_pop(L, 1);
246 // continue with common code for end of JSON object and JSON array:
247 // common code for end of JSON object or JSON array:
248 json_import_close:
249 // consume input character:
250 pos++;
251 // pop shadow table:
252 lua_pop(L, 1);
253 // check if nested:
254 if (--level) {
255 // if nested,
256 // check if outer(!) structure is an array or object:
257 if (lua_isnil(L, -2)) {
258 // select array value processing:
259 mode = JSON_STATE_ARRAY_VALUE;
260 } else {
261 // select object value processing:
262 mode = JSON_STATE_OBJECT_VALUE;
263 }
264 // store value in outer structure:
265 goto json_import_process_value;
266 }
267 // if not nested, then expect end of JSON document and continue with loop:
268 mode = JSON_STATE_END;
269 goto json_import_loop;
270 // key terminator:
271 case ':':
272 // if key terminator is not expected here, then return an error:
273 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
274 goto json_import_syntax_error;
275 // consume input character:
276 pos++;
277 // expect object value to follow:
278 mode = JSON_STATE_OBJECT_VALUE;
279 // continue with loop:
280 goto json_import_loop;
281 // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
282 case ',':
283 // branch according to parser state:
284 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
285 // expect an object key to follow:
286 mode = JSON_STATE_OBJECT_KEY;
287 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
288 // expect an array value to follow:
289 mode = JSON_STATE_ARRAY_VALUE;
290 } else {
291 // if value terminator is not expected here, then return an error:
292 goto json_import_syntax_error;
293 }
294 // consume input character:
295 pos++;
296 // continue with loop:
297 goto json_import_loop;
298 // string literal:
299 case '"':
300 // consume quote character:
301 pos++;
302 // find last character in input string:
303 outlen = pos;
304 while ((c = str[outlen]) != '"') {
305 // consume one character:
306 outlen++;
307 // handle unexpected end of JSON document:
308 if (c == 0) goto json_import_unexpected_eof;
309 // consume one extra character when encountering an escaped quote:
310 else if (c == '\\' && str[outlen] == '"') outlen++;
311 }
312 // determine buffer length:
313 outlen -= pos;
314 // check if string is non empty:
315 if (outlen) {
316 // prepare buffer to decode string (with maximum possible length) and set write position to zero:
317 cbuf = luaL_buffinitsize(L, &luabuf, outlen);
318 outlen = 0;
319 // loop through the characters until encountering end quote:
320 while ((c = str[pos++]) != '"') {
321 // NOTE: unexpected end cannot happen anymore
322 if (c < 32 || c == 127) {
323 // do not allow ASCII control characters:
324 // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
325 // by this parser to allow different encodings than Unicode
326 lua_pushnil(L);
327 lua_pushliteral(L, "Unexpected control character in JSON string");
328 return 2;
329 } else if (c == '\\') {
330 // read next char after backslash escape:
331 c = str[pos++];
332 switch (c) {
333 // unexpected end-of-string:
334 case 0:
335 goto json_import_unexpected_eof;
336 // unescaping of quotation mark, slash, and backslash:
337 case '"':
338 case '/':
339 case '\\':
340 cbuf[outlen++] = c;
341 break;
342 // unescaping of backspace:
343 case 'b': cbuf[outlen++] = '\b'; break;
344 // unescaping of form-feed:
345 case 'f': cbuf[outlen++] = '\f'; break;
346 // unescaping of new-line:
347 case 'n': cbuf[outlen++] = '\n'; break;
348 // unescaping of carriage-return:
349 case 'r': cbuf[outlen++] = '\r'; break;
350 // unescaping of tabulator:
351 case 't': cbuf[outlen++] = '\t'; break;
352 // unescaping of UTF-16 characters
353 case 'u':
354 lua_pushnil(L);
355 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
356 return 2;
357 // unexpected escape sequence:
358 default:
359 lua_pushnil(L);
360 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
361 return 2;
362 }
363 } else {
364 // normal character:
365 cbuf[outlen++] = c;
366 }
367 }
368 // process buffer to Lua string:
369 luaL_pushresultsize(&luabuf, outlen);
370 } else {
371 // if JSON string is empty,
372 // push empty Lua string:
373 lua_pushliteral(L, "");
374 }
375 // continue with processing of decoded string:
376 goto json_import_process_value;
377 }
378 // process values whose type is is not deducible from a single character:
379 if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
380 // for numbers,
381 // use strtod() call to parse a (double precision) floating point number:
382 char *endptr;
383 double numval;
384 numval = strtod(str+pos, &endptr);
385 // catch parsing errors:
386 if (endptr == str+pos) goto json_import_syntax_error;
387 // consume characters that were parsed:
388 pos += endptr - (str+pos);
389 // push parsed (double precision) floating point number on Lua stack:
390 lua_pushnumber(L, numval);
391 } else if (!strncmp(str+pos, "true", 4)) {
392 // consume 4 input characters for "true":
393 pos += 4;
394 // put Lua true value onto stack:
395 lua_pushboolean(L, 1);
396 } else if (!strncmp(str+pos, "false", 5)) {
397 // consume 5 input characters for "false":
398 pos += 5;
399 // put Lua false value onto stack:
400 lua_pushboolean(L, 0);
401 } else if (!strncmp(str+pos, "null", 4)) {
402 // consume 4 input characters for "null":
403 pos += 4;
404 // different behavor for top-level and sub-levels:
405 if (level) {
406 // if sub-level,
407 // push special null-marker onto stack:
408 json_pushnullmark(L);
409 } else {
410 // if top-level,
411 // push nil onto stack:
412 lua_pushnil(L);
413 }
414 } else {
415 // all other cases are a syntax error:
416 goto json_import_syntax_error;
417 }
418 // process a decoded value or key value pair (expected on top of Lua stack):
419 json_import_process_value:
420 switch (mode) {
421 // an object key has been read:
422 case JSON_STATE_OBJECT_KEY:
423 // if an object key is not a string, then this is a syntax error:
424 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
425 // expect key terminator to follow:
426 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
427 // continue with loop:
428 goto json_import_loop;
429 // a key value pair has been read:
430 case JSON_STATE_OBJECT_VALUE:
431 // store key value pair in outer shadow table:
432 lua_rawset(L, -3);
433 // expect value terminator (or end of object) to follow:
434 mode = JSON_STATE_OBJECT_SEPARATOR;
435 // continue with loop:
436 goto json_import_loop;
437 // an array value has been read:
438 case JSON_STATE_ARRAY_VALUE:
439 // get current array length:
440 arraylen = lua_rawlen(L, -3);
441 // throw error if array would exceed INT_MAX elements:
442 // TODO: Lua 5.3 may support more elements
443 if (arraylen >= INT_MAX) {
444 lua_pushnil(L);
445 lua_pushfstring(L, "Array exceeded length of %d elements", INT_MAX);
446 }
447 // store value in outer shadow table:
448 lua_rawseti(L, -3, arraylen + 1);
449 // expect value terminator (or end of object) to follow:
450 mode = JSON_STATE_ARRAY_SEPARATOR;
451 // continue with loop
452 goto json_import_loop;
453 // a single value has been read:
454 case JSON_STATE_VALUE:
455 // leave value on top of stack, expect end of JSON document, and continue with loop:
456 mode = JSON_STATE_END;
457 goto json_import_loop;
458 }
459 // syntax error handling (reachable by goto statement):
460 json_import_syntax_error:
461 lua_pushnil(L);
462 lua_pushliteral(L, "Syntax error in JSON document");
463 return 2;
464 }
466 // special Lua stack indicies for json_path function:
467 #define json_path_shadowtbl_idx 1
469 // stack offset of arguments to json_path function:
470 #define json_path_idxshift 1
472 // gets a value or its type from a JSON document (passed as first argument)
473 // using a path (passed as variable number of keys after first argument):
474 static int json_path(lua_State *L, int type_mode) {
475 int stacktop; // stack index of top of stack (after shifting)
476 int idx = 2 + json_path_idxshift; // stack index of current argument to process
477 // insert shadowtbl into stack at position 1 (shifting the arguments):
478 json_regfetch(L, shadowtbl);
479 lua_insert(L, 1);
480 // store stack index of top of stack:
481 stacktop = lua_gettop(L);
482 // use first argument as "current value" (stored on top of stack):
483 lua_pushvalue(L, 1 + json_path_idxshift);
484 // process each "path key" (2nd argument and following arguments):
485 while (idx <= stacktop) {
486 // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned:
487 if (lua_isnil(L, -1)) return 1;
488 // try to get shadow table of "current value":
489 lua_pushvalue(L, -1);
490 lua_rawget(L, json_path_shadowtbl_idx);
491 if (lua_isnil(L, -1)) {
492 // if no shadow table is found,
493 if (lua_type(L, -1) == LUA_TTABLE) {
494 // and if "current value" is a table,
495 // drop nil from stack:
496 lua_pop(L, 1);
497 // get "next value" using the "path key":
498 lua_pushvalue(L, idx++);
499 lua_gettable(L, -2);
500 } else {
501 // if "current value" is not a table,
502 // then the path cannot be walked and nil (already on top of stack) is returned:
503 return 1;
504 }
505 } else {
506 // if a shadow table is found,
507 // set "current value" to its shadow table:
508 lua_replace(L, -2);
509 // get "next value" using the "path key":
510 lua_pushvalue(L, idx++);
511 lua_rawget(L, -2);
512 }
513 // the "next value" replaces the "current value":
514 lua_replace(L, -2);
515 }
516 if (!type_mode) {
517 // if a value (and not its type) was requested,
518 // check if value is the null-marker, and store nil on top of Lua stack in that case:
519 if (json_isnullmark(L, -1)) lua_pushnil(L);
520 } else {
521 // if the type was requested,
522 // check if value is the null-marker:
523 if (json_isnullmark(L, -1)) {
524 // if yes, store string "null" on top of Lua stack:
525 lua_pushliteral(L, "null");
526 } else {
527 // otherwise,
528 // check if metatable indicates "object" or "array":
529 if (lua_getmetatable(L, -1)) {
530 json_regfetch(L, objectmt);
531 if (lua_rawequal(L, -2, -1)) {
532 // if value has metatable for JSON objects,
533 // return string "object":
534 lua_pushliteral(L, "object");
535 return 1;
536 }
537 json_regfetch(L, arraymt);
538 if (lua_rawequal(L, -3, -1)) {
539 // if value has metatable for JSON arrays,
540 // return string "object":
541 lua_pushliteral(L, "array");
542 return 1;
543 }
544 // remove 3 metatables (one of the value, two for comparison) from stack:
545 lua_pop(L, 3);
546 }
547 // otherwise, get the Lua type:
548 lua_pushstring(L, lua_typename(L, lua_type(L, -1)));
549 }
550 }
551 // return the top most value on the Lua stack:
552 return 1;
553 }
555 // gets a value from a JSON document (passed as first argument)
556 // using a path (passed as variable number of keys after first argument):
557 static int json_get(lua_State *L) {
558 return json_path(L, 0);
559 }
561 // gets a value's type from a JSON document (passed as first argument)
562 // using a path (variable number of keys after first argument):
563 static int json_type(lua_State *L) {
564 return json_path(L, 1);
565 }
567 // returns the length of a JSON array (or zero for a table without numeric keys):
568 static int json_len(lua_State *L) {
569 // stack shall contain one function argument:
570 lua_settop(L, 1);
571 // try to get corresponding shadow table for first argument:
572 json_regfetch(L, shadowtbl);
573 lua_pushvalue(L, 1);
574 lua_rawget(L, -2);
575 // if shadow table does not exist, return length of argument, else length of shadow table:
576 lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
577 return 1;
578 }
580 static int json_index(lua_State *L) {
581 // stack shall contain two function arguments:
582 lua_settop(L, 2);
583 // get corresponding shadow table for first argument:
584 json_regfetch(L, shadowtbl);
585 lua_pushvalue(L, 1);
586 lua_rawget(L, -2);
587 // throw error if no shadow table was found:
588 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
589 // use key passed as second argument to lookup value in shadow table:
590 lua_pushvalue(L, 2);
591 lua_rawget(L, -2);
592 // if value is null-marker, then push nil onto stack:
593 if (json_isnullmark(L, -1)) lua_pushnil(L);
594 // return either looked up value, or nil
595 return 1;
596 }
598 static int json_newindex(lua_State *L) {
599 // stack shall contain three function arguments:
600 lua_settop(L, 3);
601 // get corresponding shadow table for first argument:
602 json_regfetch(L, shadowtbl);
603 lua_pushvalue(L, 1);
604 lua_rawget(L, -2);
605 // throw error if no shadow table was found:
606 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
607 // replace first argument with shadow table:
608 lua_replace(L, 1);
609 // reset stack and use second and third argument to write to shadow table:
610 lua_settop(L, 3);
611 lua_rawset(L, 1);
612 // return nothing:
613 return 0;
614 }
616 static int json_pairs_iterfunc(lua_State *L) {
617 // stack shall contain two function arguments:
618 lua_settop(L, 2);
619 // get corresponding shadow table for first argument:
620 json_regfetch(L, shadowtbl);
621 lua_pushvalue(L, 1);
622 lua_rawget(L, -2);
623 // throw error if no shadow table was found:
624 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
625 // get next key value pair from shadow table (using previous key from argument 2)
626 // and return nothing if there is no next pair:
627 lua_pushvalue(L, 2);
628 if (!lua_next(L, -2)) return 0;
629 // replace null-marker with nil:
630 if (json_isnullmark(L, -1)) {
631 lua_pop(L, 1);
632 lua_pushnil(L);
633 }
634 // return key and value (or key and nil, if null-marker was found):
635 return 2;
636 }
638 // returns a triple such that 'for key, value in pairs(obj) do ... end'
639 // iterates through all key value pairs (including JSON null keys represented as Lua nil):
640 static int json_pairs(lua_State *L) {
641 // return triple of function json_pairs_iterfunc, first argument, and nil:
642 lua_pushcfunction(L, json_pairs_iterfunc);
643 lua_pushvalue(L, 1);
644 lua_pushnil(L);
645 return 3;
646 }
648 static int json_ipairs_iterfunc(lua_State *L) {
649 lua_Integer idx;
650 // stack shall contain two function arguments:
651 lua_settop(L, 2);
652 // calculate new index by incrementing second argument:
653 idx = lua_tointeger(L, 2) + 1;
654 // get corresponding shadow table for first argument:
655 json_regfetch(L, shadowtbl);
656 lua_pushvalue(L, 1);
657 lua_rawget(L, -2);
658 // throw error if no shadow table was found:
659 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
660 // do integer lookup in shadow table:
661 lua_rawgeti(L, -1, idx);
662 // return nothing if there was no value:
663 if (lua_isnil(L, -1)) return 0;
664 // return new index and
665 // either the looked up value if it is not equal to the null-marker
666 // or nil instead of null-marker:
667 lua_pushinteger(L, idx);
668 if (json_isnullmark(L, -2)) lua_pushnil(L);
669 else lua_pushvalue(L, -2);
670 return 2;
671 }
673 // returns a triple such that 'for idx, value in ipairs(ary) do ... end'
674 // iterates through all values (including JSON null represented as Lua nil):
675 static int json_ipairs(lua_State *L) {
676 // return triple of function json_ipairs_iterfunc, first argument, and zero:
677 lua_pushcfunction(L, json_ipairs_iterfunc);
678 lua_pushvalue(L, 1);
679 lua_pushinteger(L, 0);
680 return 3;
681 }
683 typedef struct {
684 size_t length;
685 const char *data;
686 } json_key_t;
688 static int json_key_cmp(json_key_t *key1, json_key_t *key2) {
689 size_t pos = 0;
690 unsigned char c1, c2;
691 while (1) {
692 if (key1->length > pos) {
693 if (key2->length > pos) {
694 c1 = key1->data[pos];
695 c2 = key2->data[pos];
696 if (c1 < c2) return -1;
697 else if (c1 > c2) return 1;
698 } else {
699 return 1;
700 }
701 } else {
702 if (key2->length > pos) {
703 return -1;
704 } else {
705 return 0;
706 }
707 }
708 pos++;
709 }
710 }
712 #define JSON_TABLETYPE_UNKNOWN 0
713 #define JSON_TABLETYPE_OBJECT 1
714 #define JSON_TABLETYPE_ARRAY 2
716 #define json_export_internal_indentstring_idx 1
717 #define json_export_internal_level_idx 2
718 #define json_export_internal_value_idx 3
719 #define json_export_internal_tmp_idx 4
721 static int json_export_internal(lua_State *L) {
722 int level;
723 int pretty;
724 int i;
725 lua_Number num;
726 const char *str;
727 unsigned char c;
728 size_t strlen;
729 size_t pos = 0;
730 luaL_Buffer buf;
731 char hexcode[7]; // backslash, character 'u', 4 hex digits, and terminating NULL byte
732 int tabletype = JSON_TABLETYPE_UNKNOWN;
733 int anyelement = 0;
734 size_t keycount = 0;
735 size_t keypos = 0;
736 json_key_t *keybuf = NULL;
737 lua_Integer idx;
738 lua_settop(L, json_export_internal_value_idx);
739 if (json_isnullmark(L, json_export_internal_value_idx)) {
740 lua_pop(L, 1);
741 lua_pushnil(L);
742 }
743 switch (lua_type(L, json_export_internal_value_idx)) {
744 case LUA_TNIL:
745 lua_pushliteral(L, "null");
746 return 1;
747 case LUA_TNUMBER:
748 num = lua_tonumber(L, json_export_internal_value_idx);
749 if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value");
750 if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers");
751 lua_tostring(L, json_export_internal_value_idx);
752 return 1;
753 case LUA_TBOOLEAN:
754 if (lua_toboolean(L, json_export_internal_value_idx)) {
755 lua_pushliteral(L, "true");
756 } else {
757 lua_pushliteral(L, "false");
758 }
759 return 1;
760 case LUA_TSTRING:
761 str = lua_tolstring(L, 3, &strlen);
762 luaL_buffinit(L, &buf);
763 luaL_addchar(&buf, '"');
764 while (pos < strlen) {
765 c = str[pos++];
766 if (c == '"') luaL_addstring(&buf, "\\\"");
767 else if (c == '\\') luaL_addstring(&buf, "\\\\");
768 else if (c == 127) luaL_addstring(&buf, "\\u007F");
769 else if (c >= 32) luaL_addchar(&buf, c);
770 else if (c == '\b') luaL_addstring(&buf, "\\b");
771 else if (c == '\f') luaL_addstring(&buf, "\\f");
772 else if (c == '\n') luaL_addstring(&buf, "\\n");
773 else if (c == '\r') luaL_addstring(&buf, "\\r");
774 else if (c == '\t') luaL_addstring(&buf, "\\t");
775 else if (c == '\v') luaL_addstring(&buf, "\\v");
776 else {
777 sprintf(hexcode, "\\u%04X", c);
778 luaL_addstring(&buf, hexcode);
779 }
780 }
781 luaL_addchar(&buf, '"');
782 luaL_pushresult(&buf);
783 return 1;
784 case LUA_TTABLE:
785 if (lua_getmetatable(L, json_export_internal_value_idx)) {
786 json_regfetch(L, objectmt);
787 if (lua_rawequal(L, -2, -1)) {
788 tabletype = JSON_TABLETYPE_OBJECT;
789 } else {
790 json_regfetch(L, arraymt);
791 if (lua_rawequal(L, -3, -1)) {
792 tabletype = JSON_TABLETYPE_ARRAY;
793 } else {
794 return luaL_error(L, "JSON export not possible for tables with nonsupported metatable");
795 }
796 }
797 }
798 json_regfetch(L, shadowtbl);
799 lua_pushvalue(L, json_export_internal_value_idx);
800 lua_rawget(L, -2);
801 if (!lua_isnil(L, -1)) lua_replace(L, json_export_internal_value_idx);
802 lua_settop(L, json_export_internal_value_idx);
803 if (tabletype == JSON_TABLETYPE_UNKNOWN) {
804 for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) {
805 switch (lua_type(L, -2)) {
806 case LUA_TSTRING:
807 keycount++;
808 if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT;
809 else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error;
810 break;
811 case LUA_TNUMBER:
812 if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY;
813 else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error;
814 break;
815 }
816 }
817 }
818 pretty = lua_toboolean(L, json_export_internal_indentstring_idx);
819 level = lua_tointeger(L, json_export_internal_level_idx) + 1;
820 if (level > JSON_MAXDEPTH) {
821 return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH);
822 }
823 switch (tabletype) {
824 case JSON_TABLETYPE_OBJECT:
825 if (!keycount) {
826 for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) {
827 if (lua_type(L, -2) == LUA_TSTRING) keycount++;
828 }
829 }
830 if (keycount) {
831 keybuf = calloc(keycount, sizeof(json_key_t));
832 if (!keybuf) return luaL_error(L, "Memory allocation failed in JSON library");
833 for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) {
834 if (lua_type(L, -2) == LUA_TSTRING) {
835 json_key_t *key = keybuf + (keypos++);
836 key->data = lua_tolstring(L, -2, &key->length);
837 }
838 }
839 qsort(keybuf, keycount, sizeof(json_key_t), (void *)json_key_cmp);
840 }
841 luaL_buffinit(L, &buf);
842 luaL_addchar(&buf, '{');
843 for (keypos=0; keypos<keycount; keypos++) {
844 json_key_t *key = keybuf + keypos;
845 if (keypos) luaL_addchar(&buf, ',');
846 if (pretty) {
847 luaL_addchar(&buf, '\n');
848 for (i=0; i<level; i++) {
849 lua_pushvalue(L, json_export_internal_indentstring_idx);
850 luaL_addvalue(&buf);
851 }
852 }
853 lua_pushcfunction(L, json_export_internal);
854 lua_pushvalue(L, json_export_internal_indentstring_idx);
855 lua_pushinteger(L, level);
856 lua_pushlstring(L, key->data, key->length);
857 if (lua_pcall(L, 3, 1, 0)) {
858 if (keybuf) free(keybuf);
859 return lua_error(L);
860 }
861 luaL_addvalue(&buf);
862 luaL_addchar(&buf, ':');
863 if (pretty) luaL_addchar(&buf, ' ');
864 lua_pushcfunction(L, json_export_internal);
865 lua_pushvalue(L, json_export_internal_indentstring_idx);
866 lua_pushinteger(L, level);
867 lua_pushlstring(L, key->data, key->length);
868 lua_rawget(L, json_export_internal_value_idx);
869 if (lua_pcall(L, 3, 1, 0)) {
870 if (keybuf) free(keybuf);
871 return lua_error(L);
872 }
873 luaL_addvalue(&buf);
874 }
875 if (keybuf) free(keybuf);
876 if (pretty && keycount != 0) {
877 luaL_addchar(&buf, '\n');
878 for (i=0; i<level-1; i++) {
879 lua_pushvalue(L, json_export_internal_indentstring_idx);
880 luaL_addvalue(&buf);
881 }
882 }
883 luaL_addchar(&buf, '}');
884 if (pretty && level == 1) luaL_addchar(&buf, '\n');
885 luaL_pushresult(&buf);
886 return 1;
887 case JSON_TABLETYPE_ARRAY:
888 lua_settop(L, json_export_internal_tmp_idx);
889 luaL_buffinit(L, &buf);
890 luaL_addchar(&buf, '[');
891 for (idx = 1; ; idx++) {
892 lua_rawgeti(L, json_export_internal_value_idx, idx);
893 if (lua_isnil(L, -1)) {
894 lua_pop(L, 1);
895 break;
896 }
897 lua_replace(L, json_export_internal_tmp_idx);
898 if (anyelement) luaL_addchar(&buf, ',');
899 anyelement = 1;
900 if (pretty) {
901 luaL_addchar(&buf, '\n');
902 for (i=0; i<level; i++) {
903 lua_pushvalue(L, json_export_internal_indentstring_idx);
904 luaL_addvalue(&buf);
905 }
906 }
907 lua_pushcfunction(L, json_export_internal);
908 lua_pushvalue(L, json_export_internal_indentstring_idx);
909 lua_pushinteger(L, level);
910 lua_pushvalue(L, json_export_internal_tmp_idx);
911 lua_call(L, 3, 1);
912 luaL_addvalue(&buf);
913 }
914 if (pretty && anyelement) {
915 luaL_addchar(&buf, '\n');
916 for (i=0; i<level-1; i++) {
917 lua_pushvalue(L, json_export_internal_indentstring_idx);
918 luaL_addvalue(&buf);
919 }
920 }
921 luaL_addchar(&buf, ']');
922 if (pretty && level == 1) luaL_addchar(&buf, '\n');
923 luaL_pushresult(&buf);
924 return 1;
925 }
926 json_export_tabletype_error:
927 return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)");
928 }
929 return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, json_export_internal_value_idx)));
930 }
932 static int json_export(lua_State *L) {
933 lua_settop(L, 1);
934 lua_pushcfunction(L, json_export_internal);
935 lua_pushnil(L);
936 lua_pushinteger(L, 0);
937 lua_pushvalue(L, 1);
938 lua_call(L, 3, 1);
939 return 1;
940 }
942 static int json_pretty(lua_State *L) {
943 lua_settop(L, 2);
944 lua_pushcfunction(L, json_export_internal);
945 if (lua_isnil(L, 2)) lua_pushliteral(L, " ");
946 else lua_pushvalue(L, 2);
947 lua_pushinteger(L, 0);
948 lua_pushvalue(L, 1);
949 lua_call(L, 3, 1);
950 return 1;
951 }
953 // functions in library module:
954 static const struct luaL_Reg json_module_functions[] = {
955 {"object", json_object},
956 {"array", json_array},
957 {"import", json_import},
958 {"export", json_export},
959 {"pretty", json_pretty},
960 {"get", json_get},
961 {"type", json_type},
962 {NULL, NULL}
963 };
965 // metamethods for JSON objects, JSON arrays, and unknown JSON collections (object or array):
966 static const struct luaL_Reg json_metatable_functions[] = {
967 {"__len", json_len},
968 {"__index", json_index},
969 {"__newindex", json_newindex},
970 {"__pairs", json_pairs},
971 {"__ipairs", json_ipairs},
972 {"__tostring", json_export},
973 {NULL, NULL}
974 };
976 // metamethods for JSON null marker:
977 static const struct luaL_Reg json_nullmark_metamethods[] = {
978 {"__tostring", json_nullmark_tostring},
979 {NULL, NULL}
980 };
982 // initializes json library:
983 int luaopen_json(lua_State *L) {
984 // empty stack:
985 lua_settop(L, 0);
986 // push library module onto stack position 1:
987 lua_newtable(L);
988 // register library functions:
989 luaL_setfuncs(L, json_module_functions, 0);
990 // create and store objectmt:
991 lua_newtable(L);
992 luaL_setfuncs(L, json_metatable_functions, 0);
993 json_regstore(L, objectmt);
994 // create and store arraymt:
995 lua_newtable(L);
996 luaL_setfuncs(L, json_metatable_functions, 0);
997 json_regstore(L, arraymt);
998 // create and store ephemeron table to store shadow tables for each JSON object/array
999 // to allow NULL values returned as nil
1000 lua_newtable(L);
1001 lua_newtable(L); // metatable for ephemeron table
1002 lua_pushliteral(L, "__mode");
1003 lua_pushliteral(L, "k");
1004 lua_rawset(L, -3);
1005 lua_setmetatable(L, -2);
1006 json_regstore(L, shadowtbl);
1007 // set metatable of null marker and make it available through library module:
1008 json_pushnullmark(L);
1009 lua_newtable(L);
1010 luaL_setfuncs(L, json_nullmark_metamethods, 0);
1011 lua_setmetatable(L, -2);
1012 lua_setfield(L, 1, "null");
1013 // return library module (that's expected on top of stack):
1014 return 1;

Impressum / About Us