webmcp

view libraries/json/json.c @ 188:433fa98329ec

Bugfix in json.export(...); Better argument checking for json.export(...)
author jbe
date Sun Aug 10 19:18:25 2014 +0200 (2014-08-10)
parents 00fee67a0c27
children 44344b4a52c1
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: json_import can store 2^32 / 3 levels on stack swap (using
9 // also negative indicies after integer wraparound), and
10 // json_export can store even more levels, so 1024^3 =
11 // 1073741824 is a safe value and allows practically unlimited
12 // levels for JSON documents <= 2 GiB.
13 #define JSON_MAXDEPTH (1024*1024*1024)
15 // generate dummy memory addresses that represents null values:
16 char json_nullmark;
17 #define json_isnullmark(L, i) (lua_touserdata((L), (i)) == &json_nullmark)
18 #define json_pushnullmark(L) lua_pushlightuserdata((L), &json_nullmark)
20 // macros for usage of Lua registry:
21 #define JSON_REGENT char
22 #define JSON_REGPOINTER void *
23 #define json_regpointer(x) (&json_registry.x)
24 #define json_regfetchpointer(L, x) lua_rawgetp((L), LUA_REGISTRYINDEX, (x))
25 #define json_regfetch(L, x) json_regfetchpointer(L, json_regpointer(x))
26 #define json_regstore(L, x) lua_rawsetp(L, LUA_REGISTRYINDEX, json_regpointer(x))
28 // generate dummy memory addresses that represent Lua objects
29 // via lightuserdata keys and LUA_REGISTRYINDEX:
30 static struct {
31 JSON_REGENT shadowtbl; // ephemeron table that maps tables to their corresponding shadow table
32 JSON_REGENT objectmt; // metatable for JSON objects
33 JSON_REGENT arraymt; // metatable for JSON arrays
34 } json_registry;
36 // returns the string "<JSON null marker>":
37 static int json_nullmark_tostring(lua_State *L) {
38 lua_pushliteral(L, "<JSON null marker>");
39 return 1;
40 }
42 #define json_convert_source_idx 1
43 #define json_convert_iterator_idx 2
44 #define json_convert_output_idx 3
45 #define json_convert_shadow_idx 4
46 #define json_convert_iterfun_idx 5
47 #define json_convert_itertbl_idx 6
49 // converts a Lua table (or any other iterable value) to a JSON object or JSON array:
50 // (does never modify the argument, returns an empty object or array if argument is nil)
51 static int json_convert(lua_State *L, int array) {
52 int arrayidx = 0;
53 // determine is argument is given:
54 if (lua_isnoneornil(L, json_convert_source_idx)) {
55 // if no argument is given (or if argument is nil),
56 // create table with shadow table, and leave first table on top of stack:
57 json_regfetch(L, shadowtbl);
58 lua_newtable(L);
59 lua_pushvalue(L, -1);
60 lua_newtable(L);
61 lua_rawset(L, -4);
62 } else {
63 // if an argument was given,
64 // push its iterator function on stack position 2 if existent,
65 // else push null for normal tables:
66 lua_settop(L, 1);
67 if (lua_getmetatable(L, json_convert_source_idx)) {
68 lua_getfield(L, -1, array ? "__ipairs" : "__pairs");
69 if (lua_isnil(L, -1)) luaL_checktype(L, 1, LUA_TTABLE);
70 else if (lua_type(L, -1) != LUA_TFUNCTION)
71 return luaL_error(L, "%s metamethod is not a function", array ? "__ipairs" : "__pairs");
72 lua_replace(L, -2);
73 } else {
74 lua_pushnil(L);
75 }
76 // create result table on stack position 3:
77 lua_newtable(L);
78 // create shadow table on stack position 4:
79 json_regfetch(L, shadowtbl);
80 lua_newtable(L);
81 lua_pushvalue(L, json_convert_output_idx);
82 lua_pushvalue(L, -2);
83 lua_rawset(L, -4);
84 lua_replace(L, -2);
85 // check if iterator function exists:
86 if (lua_isnil(L, json_convert_iterator_idx)) {
87 // if there is no iterator function,
88 // distinguish between objects and arrays:
89 if (array == 0) {
90 // for an object, copy all string key value pairs to shadow table:
91 for (lua_pushnil(L); lua_next(L, json_convert_source_idx); lua_pop(L, 1)) {
92 if (lua_type(L, -2) == LUA_TSTRING) {
93 lua_pushvalue(L, -2);
94 lua_pushvalue(L, -2);
95 lua_rawset(L, json_convert_shadow_idx);
96 }
97 }
98 } else {
99 // for an array, copy consecutive integer value pairs to shadow table:
100 while (1) {
101 // throw error if array would exceed INT_MAX elements:
102 // TODO: Lua 5.3 may support more elements
103 if (arrayidx == INT_MAX) {
104 lua_pushnumber(L, (size_t)INT_MAX+1);
105 lua_rawget(L, json_convert_source_idx);
106 if (lua_isnil(L, -1)) break;
107 return luaL_error(L, "Array exceeded length of %d elements", INT_MAX);
108 }
109 // get next array entry:
110 arrayidx++;
111 lua_rawgeti(L, json_convert_source_idx, arrayidx);
112 // break if value is nil:
113 if (lua_isnil(L, -1)) break;
114 // store value in shadow table:
115 lua_rawseti(L, json_convert_shadow_idx, arrayidx);
116 }
117 }
118 } else {
119 // if there is an iterator function,
120 // call iterator function with source value (first argument)
121 // and store 3 result values on stack positions 5 through 7:
122 lua_pushvalue(L, json_convert_iterator_idx);
123 lua_pushvalue(L, 1);
124 lua_call(L, 1, 3);
125 // iterate through key value pairs and store some of them in shadow table
126 // while replacing nil values with null-marker:
127 while (1) {
128 // call iterfun function:
129 lua_pushvalue(L, json_convert_iterfun_idx);
130 lua_pushvalue(L, json_convert_itertbl_idx);
131 lua_pushvalue(L, -3);
132 lua_remove(L, -4);
133 lua_call(L, 2, 2);
134 // break iteration loop if key is nil:
135 if (lua_isnil(L, -2)) break;
136 // store key value pair only if key type is correct:
137 if (lua_type(L, -2) == (array ? LUA_TNUMBER : LUA_TSTRING)) {
138 // if key type is correct,
139 // push key onto stack:
140 lua_pushvalue(L, -2);
141 // if value is nil, push null-marker onto stack (as value):
142 if (lua_isnil(L, -2)) json_pushnullmark(L);
143 // else push value onto stack:
144 else lua_pushvalue(L, -2);
145 // set key value pair in shadow table:
146 lua_rawset(L, json_convert_shadow_idx);
147 }
148 // pop value from stack, but leave key on stack:
149 lua_pop(L, 1);
150 }
151 }
152 // let result table be on top of stack:
153 lua_settop(L, json_convert_output_idx);
154 }
155 // set metatable (for result table on top of stack):
156 if (array == 0) json_regfetch(L, objectmt);
157 else json_regfetch(L, arraymt);
158 lua_setmetatable(L, -2);
159 // return table on top of stack:
160 return 1;
161 }
163 // converts a Lua table (or any other iterable value) to a JSON object:
164 // (does never modify the argument, returns an empty object or array if argument is nil)
165 static int json_object(lua_State *L) {
166 return json_convert(L, 0);
167 }
169 // converts a Lua table (or any other iterable value) to a JSON array:
170 // (does never modify the argument, returns an empty object or array if argument is nil)
171 static int json_array(lua_State *L) {
172 return json_convert(L, 1);
173 }
175 // internal states of JSON parser:
176 #define JSON_STATE_VALUE 0
177 #define JSON_STATE_OBJECT_KEY 1
178 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
179 #define JSON_STATE_OBJECT_VALUE 3
180 #define JSON_STATE_OBJECT_SEPARATOR 4
181 #define JSON_STATE_ARRAY_VALUE 5
182 #define JSON_STATE_ARRAY_SEPARATOR 6
183 #define JSON_STATE_END 7
185 // special Lua stack indicies for json_import function:
186 #define json_import_objectmt_idx 2
187 #define json_import_arraymt_idx 3
188 #define json_import_shadowtbl_idx 4
189 #define json_import_stackswap_idx 5
191 // macros for hex decoding:
192 #define json_utf16_surrogate(x) ((x) >= 0xD800 && (x) <= 0xDFFF)
193 #define json_utf16_lead(x) ((x) >= 0xD800 && (x) <= 0xDBFF)
194 #define json_utf16_tail(x) ((x) >= 0xDC00 && (x) <= 0xDFFF)
195 #define json_import_readhex(x) \
196 do { \
197 x = 0; \
198 for (i=0; i<4; i++) { \
199 x <<= 4; \
200 c = str[pos++]; \
201 if (c >= '0' && c <= '9') x += c - '0'; \
202 else if (c >= 'A' && c <= 'F') x += c - 'A' + 10; \
203 else if (c >= 'a' && c <= 'f') x += c - 'a' + 10; \
204 else if (c == 0) goto json_import_unexpected_eof; \
205 else goto json_import_unexpected_escape; \
206 } \
207 } while (0)
209 // decodes a JSON document:
210 static int json_import(lua_State *L) {
211 int stackswapidx = 0; // elements in stack swap table
212 int i; // loop variable
213 const char *str; // string to parse
214 size_t total; // total length of string to parse
215 size_t pos = 0; // current position in string to parse
216 size_t level = 0; // nested levels of objects/arrays currently being processed
217 int mode = JSON_STATE_VALUE; // state of parser (i.e. "what's expected next?")
218 unsigned char c; // variable to store a single character to be processed (unsigned!)
219 luaL_Buffer luabuf; // Lua buffer to decode JSON string values
220 char *cbuf; // C buffer to decode JSON string values
221 size_t outlen; // maximum length or write position of C buffer
222 long codepoint; // decoded UTF-16 character or higher codepoint
223 long utf16tail; // second decoded UTF-16 character (surrogate tail)
224 size_t arraylen; // variable to temporarily store the array length
225 // require string as argument and convert to C string with length information:
226 str = luaL_checklstring(L, 1, &total);
227 // if string contains a NULL byte, this is a syntax error
228 if (strlen(str) != total) goto json_import_syntax_error;
229 // stack shall contain one function argument:
230 lua_settop(L, 1);
231 // push objectmt onto stack position 2:
232 json_regfetch(L, objectmt);
233 // push arraymt onto stack position 3:
234 json_regfetch(L, arraymt);
235 // push shadowtbl onto stack position 4:
236 json_regfetch(L, shadowtbl);
237 // push table for stack swapping onto stack position 5:
238 // (needed to avoid Lua stack overflows)
239 lua_newtable(L);
240 // main loop of parser:
241 json_import_loop:
242 // skip whitespace and store next character in variable 'c':
243 while (c = str[pos],
244 c == ' ' ||
245 c == '\f' ||
246 c == '\n' ||
247 c == '\r' ||
248 c == '\t' ||
249 c == '\v'
250 ) pos++;
251 // NOTE: variable c needs to be unsigned in the following code
252 // switch statement to handle certain (single) characters:
253 switch (c) {
254 // handle end of JSON document:
255 case 0:
256 // if end of JSON document was expected, then return top element of stack as result:
257 if (mode == JSON_STATE_END) return 1;
258 // otherwise, the JSON document was malformed:
259 if (level == 0) {
260 lua_pushnil(L);
261 lua_pushliteral(L, "Empty string");
262 } else {
263 json_import_unexpected_eof:
264 lua_pushnil(L);
265 lua_pushliteral(L, "Unexpected end of JSON document");
266 }
267 return 2;
268 // new JSON object or JSON array:
269 case '{':
270 case '[':
271 // if an encountered JSON object is not expected here, then return an error:
272 if (
273 c == '{' &&
274 mode != JSON_STATE_VALUE &&
275 mode != JSON_STATE_OBJECT_VALUE &&
276 mode != JSON_STATE_ARRAY_VALUE
277 ) goto json_import_syntax_error;
278 // if an encountered JSON array is not expected here, then return an error:
279 if (
280 c == '[' &&
281 mode != JSON_STATE_VALUE &&
282 mode != JSON_STATE_OBJECT_VALUE &&
283 mode != JSON_STATE_ARRAY_VALUE
284 ) goto json_import_syntax_error;
285 // consume input character:
286 pos++;
287 // limit nested levels:
288 if (level >= JSON_MAXDEPTH) {
289 lua_pushnil(L);
290 lua_pushfstring(L, "More than %d nested JSON levels", JSON_MAXDEPTH);
291 return 2;
292 }
293 // swap Lua stack entries for previous level to swap table:
294 // (avoids depth limitations due to Lua stack size)
295 if (level) {
296 lua_rawseti(L, json_import_stackswap_idx, ++stackswapidx);
297 lua_rawseti(L, json_import_stackswap_idx, ++stackswapidx);
298 lua_rawseti(L, json_import_stackswap_idx, ++stackswapidx);
299 }
300 // increment level:
301 level++;
302 // create JSON object or JSON array on stack:
303 lua_newtable(L);
304 // set metatable of JSON object or JSON array:
305 lua_pushvalue(L, c == '{' ? json_import_objectmt_idx : json_import_arraymt_idx);
306 lua_setmetatable(L, -2);
307 // create internal shadow table on stack:
308 lua_newtable(L);
309 // register internal shadow table:
310 lua_pushvalue(L, -2);
311 lua_pushvalue(L, -2);
312 lua_rawset(L, json_import_shadowtbl_idx);
313 // distinguish between JSON objects and JSON arrays:
314 if (c == '{') {
315 // if JSON object,
316 // expect object key (or end of object) to follow:
317 mode = JSON_STATE_OBJECT_KEY;
318 } else {
319 // if JSON array,
320 // expect array value (or end of array) to follow:
321 mode = JSON_STATE_ARRAY_VALUE;
322 // add nil as key (needed to keep stack balance) and as magic to detect arrays:
323 if (c == '[') lua_pushnil(L);
324 }
325 goto json_import_loop;
326 // end of JSON object:
327 case '}':
328 // if end of JSON object is not expected here, then return an error:
329 if (
330 mode != JSON_STATE_OBJECT_KEY &&
331 mode != JSON_STATE_OBJECT_SEPARATOR
332 ) goto json_import_syntax_error;
333 // jump to common code for end of JSON object and JSON array:
334 goto json_import_close;
335 // end of JSON array:
336 case ']':
337 // if end of JSON array is not expected here, then return an error:
338 if (
339 mode != JSON_STATE_ARRAY_VALUE &&
340 mode != JSON_STATE_ARRAY_SEPARATOR
341 ) goto json_import_syntax_error;
342 // pop nil key/magic (that was needed to keep stack balance):
343 lua_pop(L, 1);
344 // continue with common code for end of JSON object and JSON array:
345 // common code for end of JSON object or JSON array:
346 json_import_close:
347 // consume input character:
348 pos++;
349 // pop shadow table:
350 lua_pop(L, 1);
351 // check if nested:
352 if (--level) {
353 // if nested,
354 // restore previous stack elements from stack swap:
355 lua_rawgeti(L, json_import_stackswap_idx, stackswapidx--);
356 lua_insert(L, -2);
357 lua_rawgeti(L, json_import_stackswap_idx, stackswapidx--);
358 lua_insert(L, -2);
359 lua_rawgeti(L, json_import_stackswap_idx, stackswapidx--);
360 lua_insert(L, -2);
361 // check if outer(!) structure is an array or object:
362 if (lua_isnil(L, -2)) {
363 // select array value processing:
364 mode = JSON_STATE_ARRAY_VALUE;
365 } else {
366 // select object value processing:
367 mode = JSON_STATE_OBJECT_VALUE;
368 }
369 // store value in outer structure:
370 goto json_import_process_value;
371 }
372 // if not nested, then expect end of JSON document and continue with loop:
373 mode = JSON_STATE_END;
374 goto json_import_loop;
375 // key terminator:
376 case ':':
377 // if key terminator is not expected here, then return an error:
378 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
379 goto json_import_syntax_error;
380 // consume input character:
381 pos++;
382 // expect object value to follow:
383 mode = JSON_STATE_OBJECT_VALUE;
384 // continue with loop:
385 goto json_import_loop;
386 // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
387 case ',':
388 // branch according to parser state:
389 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
390 // expect an object key to follow:
391 mode = JSON_STATE_OBJECT_KEY;
392 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
393 // expect an array value to follow:
394 mode = JSON_STATE_ARRAY_VALUE;
395 } else {
396 // if value terminator is not expected here, then return an error:
397 goto json_import_syntax_error;
398 }
399 // consume input character:
400 pos++;
401 // continue with loop:
402 goto json_import_loop;
403 // string literal:
404 case '"':
405 // consume quote character:
406 pos++;
407 // find last character in input string:
408 outlen = pos;
409 while ((c = str[outlen]) != '"') {
410 // consume one character:
411 outlen++;
412 // handle unexpected end of JSON document:
413 if (c == 0) goto json_import_unexpected_eof;
414 // consume one extra character when encountering an escaped quote:
415 else if (c == '\\' && str[outlen] == '"') outlen++;
416 }
417 // determine buffer length:
418 outlen -= pos;
419 // check if string is non empty:
420 if (outlen) {
421 // prepare buffer to decode string (with maximum possible length) and set write position to zero:
422 cbuf = luaL_buffinitsize(L, &luabuf, outlen);
423 outlen = 0;
424 // loop through the characters until encountering end quote:
425 while ((c = str[pos++]) != '"') {
426 // NOTE: unexpected end cannot happen anymore
427 if (c < 32 || c == 127) {
428 // do not allow ASCII control characters:
429 // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
430 // by this parser to allow different encodings than Unicode
431 lua_pushnil(L);
432 lua_pushliteral(L, "Unexpected control character in JSON string");
433 return 2;
434 } else if (c == '\\') {
435 // read next char after backslash escape:
436 c = str[pos++];
437 switch (c) {
438 // unexpected end-of-string:
439 case 0:
440 goto json_import_unexpected_eof;
441 // unescaping of quotation mark, slash, and backslash:
442 case '"':
443 case '/':
444 case '\\':
445 cbuf[outlen++] = c;
446 break;
447 // unescaping of backspace:
448 case 'b': cbuf[outlen++] = '\b'; break;
449 // unescaping of form-feed:
450 case 'f': cbuf[outlen++] = '\f'; break;
451 // unescaping of new-line:
452 case 'n': cbuf[outlen++] = '\n'; break;
453 // unescaping of carriage-return:
454 case 'r': cbuf[outlen++] = '\r'; break;
455 // unescaping of tabulator:
456 case 't': cbuf[outlen++] = '\t'; break;
457 // unescaping of UTF-16 characters
458 case 'u':
459 // decode 4 hex nibbles:
460 json_import_readhex(codepoint);
461 // handle surrogate character:
462 if (json_utf16_surrogate(codepoint)) {
463 // check if first surrogate is in valid range:
464 if (json_utf16_lead(codepoint)) {
465 // require second surrogate:
466 if ((c = str[pos++]) != '\\' || (c = str[pos++]) != 'u') {
467 if (c == 0) goto json_import_unexpected_eof;
468 else goto json_import_wrong_surrogate;
469 }
470 // read 4 hex nibbles of second surrogate character:
471 json_import_readhex(utf16tail);
472 // check if second surrogate is in valid range:
473 if (!json_utf16_tail(utf16tail)) goto json_import_wrong_surrogate;
474 // calculate codepoint:
475 codepoint = 0x10000 + (utf16tail - 0xDC00) + (codepoint - 0xD800) * 0x400;
476 } else {
477 // throw error for wrong surrogates:
478 json_import_wrong_surrogate:
479 lua_pushnil(L);
480 lua_pushliteral(L, "Illegal UTF-16 surrogate in JSON string escape sequence");
481 return 2;
482 }
483 }
484 // encode as UTF-8:
485 if (codepoint < 0x80) {
486 cbuf[outlen++] = (char)codepoint;
487 } else if (codepoint < 0x800) {
488 cbuf[outlen++] = (char)(0xc0 | (codepoint >> 6));
489 cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f));
490 } else if (codepoint < 0x10000) {
491 cbuf[outlen++] = (char)(0xe0 | (codepoint >> 12));
492 cbuf[outlen++] = (char)(0x80 | ((codepoint >> 6) & 0x3f));
493 cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f));
494 } else {
495 cbuf[outlen++] = (char)(0xf0 | (codepoint >> 18));
496 cbuf[outlen++] = (char)(0x80 | ((codepoint >> 12) & 0x3f));
497 cbuf[outlen++] = (char)(0x80 | ((codepoint >> 6) & 0x3f));
498 cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f));
499 }
500 break;
501 // unexpected escape sequence:
502 default:
503 json_import_unexpected_escape:
504 lua_pushnil(L);
505 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
506 return 2;
507 }
508 } else {
509 // normal character:
510 cbuf[outlen++] = c;
511 }
512 }
513 // process buffer to Lua string:
514 luaL_pushresultsize(&luabuf, outlen);
515 } else {
516 // if JSON string is empty,
517 // push empty Lua string:
518 lua_pushliteral(L, "");
519 // consume closing quote:
520 pos++;
521 }
522 // continue with processing of decoded string:
523 goto json_import_process_value;
524 }
525 // process values whose type is is not deducible from a single character:
526 if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
527 // for numbers,
528 // use strtod() call to parse a (double precision) floating point number:
529 double numval;
530 char *endptr;
531 numval = strtod(str+pos, &endptr);
532 // catch parsing errors:
533 if (endptr == str+pos) goto json_import_syntax_error;
534 // consume characters that were parsed:
535 pos += endptr - (str+pos);
536 // push parsed (double precision) floating point number on Lua stack:
537 lua_pushnumber(L, numval);
538 } else if (!strncmp(str+pos, "true", 4)) {
539 // consume 4 input characters for "true":
540 pos += 4;
541 // put Lua true value onto stack:
542 lua_pushboolean(L, 1);
543 } else if (!strncmp(str+pos, "false", 5)) {
544 // consume 5 input characters for "false":
545 pos += 5;
546 // put Lua false value onto stack:
547 lua_pushboolean(L, 0);
548 } else if (!strncmp(str+pos, "null", 4)) {
549 // consume 4 input characters for "null":
550 pos += 4;
551 // different behavor for top-level and sub-levels:
552 if (level) {
553 // if sub-level,
554 // push special null-marker onto stack:
555 json_pushnullmark(L);
556 } else {
557 // if top-level,
558 // push nil onto stack:
559 lua_pushnil(L);
560 }
561 } else {
562 // all other cases are a syntax error:
563 goto json_import_syntax_error;
564 }
565 // process a decoded value or key value pair (expected on top of Lua stack):
566 json_import_process_value:
567 switch (mode) {
568 // an object key has been read:
569 case JSON_STATE_OBJECT_KEY:
570 // if an object key is not a string, then this is a syntax error:
571 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
572 // expect key terminator to follow:
573 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
574 // continue with loop:
575 goto json_import_loop;
576 // a key value pair has been read:
577 case JSON_STATE_OBJECT_VALUE:
578 // store key value pair in outer shadow table:
579 lua_rawset(L, -3);
580 // expect value terminator (or end of object) to follow:
581 mode = JSON_STATE_OBJECT_SEPARATOR;
582 // continue with loop:
583 goto json_import_loop;
584 // an array value has been read:
585 case JSON_STATE_ARRAY_VALUE:
586 // get current array length:
587 arraylen = lua_rawlen(L, -3);
588 // throw error if array would exceed INT_MAX elements:
589 // TODO: Lua 5.3 may support more elements
590 if (arraylen >= INT_MAX) {
591 lua_pushnil(L);
592 lua_pushfstring(L, "Array exceeded length of %d elements", INT_MAX);
593 }
594 // store value in outer shadow table:
595 lua_rawseti(L, -3, arraylen + 1);
596 // expect value terminator (or end of object) to follow:
597 mode = JSON_STATE_ARRAY_SEPARATOR;
598 // continue with loop
599 goto json_import_loop;
600 // a single value has been read:
601 case JSON_STATE_VALUE:
602 // leave value on top of stack, expect end of JSON document, and continue with loop:
603 mode = JSON_STATE_END;
604 goto json_import_loop;
605 }
606 // syntax error handling (reachable by goto statement):
607 json_import_syntax_error:
608 lua_pushnil(L);
609 lua_pushliteral(L, "Syntax error in JSON document");
610 return 2;
611 }
613 // special Lua stack indicies for json_path function:
614 #define json_path_shadowtbl_idx 1
616 // stack offset of arguments to json_path function:
617 #define json_path_idxshift 1
619 // gets a value or its type from a JSON document (passed as first argument)
620 // using a path (passed as variable number of keys after the first argument):
621 static int json_path(lua_State *L, int type_mode) {
622 int stacktop; // stack index of top of stack (after shifting)
623 int idx = 2 + json_path_idxshift; // stack index of current argument to process
624 // require at least one argument:
625 luaL_checkany(L, 1);
626 // insert shadowtbl into stack at position 1 (shifting the arguments):
627 json_regfetch(L, shadowtbl);
628 lua_insert(L, 1);
629 // store stack index of top of stack:
630 stacktop = lua_gettop(L);
631 // use first argument as "current value" (stored on top of stack):
632 lua_pushvalue(L, 1 + json_path_idxshift);
633 // process each "path key" (2nd argument and following arguments):
634 while (idx <= stacktop) {
635 // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned:
636 if (lua_isnil(L, -1)) return 1;
637 // try to get shadow table of "current value":
638 lua_pushvalue(L, -1);
639 lua_rawget(L, json_path_shadowtbl_idx);
640 if (lua_isnil(L, -1)) {
641 // if no shadow table is found,
642 if (lua_type(L, -1) == LUA_TTABLE) {
643 // and if "current value" is a table,
644 // drop nil from stack:
645 lua_pop(L, 1);
646 // get "next value" using the "path key":
647 lua_pushvalue(L, idx++);
648 lua_gettable(L, -2);
649 } else {
650 // if "current value" is not a table,
651 // then the path cannot be walked and nil (already on top of stack) is returned:
652 return 1;
653 }
654 } else {
655 // if a shadow table is found,
656 // set "current value" to its shadow table:
657 lua_replace(L, -2);
658 // get "next value" using the "path key":
659 lua_pushvalue(L, idx++);
660 lua_rawget(L, -2);
661 }
662 // the "next value" replaces the "current value":
663 lua_replace(L, -2);
664 }
665 if (!type_mode) {
666 // if a value (and not its type) was requested,
667 // check if value is the null-marker, and store nil on top of Lua stack in that case:
668 if (json_isnullmark(L, -1)) lua_pushnil(L);
669 } else {
670 // if the type was requested,
671 // check if value is the null-marker:
672 if (json_isnullmark(L, -1)) {
673 // if yes, store string "null" on top of Lua stack:
674 lua_pushliteral(L, "null");
675 } else {
676 // otherwise,
677 // check if metatable indicates "object" or "array":
678 if (lua_getmetatable(L, -1)) {
679 json_regfetch(L, objectmt);
680 if (lua_rawequal(L, -2, -1)) {
681 // if value has metatable for JSON objects,
682 // return string "object":
683 lua_pushliteral(L, "object");
684 return 1;
685 }
686 json_regfetch(L, arraymt);
687 if (lua_rawequal(L, -3, -1)) {
688 // if value has metatable for JSON arrays,
689 // return string "object":
690 lua_pushliteral(L, "array");
691 return 1;
692 }
693 // remove 3 metatables (one of the value, two for comparison) from stack:
694 lua_pop(L, 3);
695 }
696 // otherwise, get the Lua type:
697 lua_pushstring(L, lua_typename(L, lua_type(L, -1)));
698 }
699 }
700 // return the top most value on the Lua stack:
701 return 1;
702 }
704 // gets a value from a JSON document (passed as first argument)
705 // using a path (passed as variable number of keys after the first argument):
706 static int json_get(lua_State *L) {
707 return json_path(L, 0);
708 }
710 // gets a value's type from a JSON document (passed as first argument)
711 // using a path (passed as variable number of keys after first the argument):
712 static int json_type(lua_State *L) {
713 return json_path(L, 1);
714 }
716 // special Lua stack indicies for json_set function:
717 #define json_set_shadowtbl_idx 1
718 #define json_set_objectmt_idx 2
719 #define json_set_arraymt_idx 3
721 // stack offset of arguments to json_set function:
722 #define json_set_idxshift 3
724 // sets a value (passed as second argument) in a JSON document (passed as first argument)
725 // using a path (passed as variable number of keys starting at third argument):
726 static int json_set(lua_State *L) {
727 int stacktop; // stack index of top of stack (after shifting)
728 int idx = 3; // stack index of current argument to process
729 // require at least two arguments:
730 luaL_checkany(L, 1);
731 luaL_checkany(L, 2);
732 // insert shadowtbl into stack at position 1 (shifting the arguments):
733 json_regfetch(L, shadowtbl);
734 lua_insert(L, 1);
735 // insert objectmt into stack at position 2 (shifting the arguments):
736 json_regfetch(L, objectmt);
737 lua_insert(L, 2);
738 // insert arraymt into stack at position 3 (shifting the arguments):
739 json_regfetch(L, arraymt);
740 lua_insert(L, 3);
741 // store stack index of top of stack:
742 stacktop = lua_gettop(L);
743 // use nil as initial "parent value":
744 lua_pushnil(L);
745 // use first argument as "current value":
746 lua_pushvalue(L, 1 + json_set_idxshift);
747 // set all necessary values in path:
748 for (idx = 3 + json_set_idxshift; idx<=stacktop; idx++) {
749 // push metatable of "current value" onto stack:
750 if (!lua_getmetatable(L, -1)) lua_pushnil(L);
751 // distinguish according to type of path key:
752 switch (lua_type(L, idx)) {
753 case LUA_TSTRING:
754 // if path key is a string,
755 // check if "current value" is a JSON object (or table without metatable):
756 if (
757 lua_rawequal(L, -1, json_set_objectmt_idx) ||
758 (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE)
759 ) {
760 // if "current value" is acceptable,
761 // pop metatable and leave "current value" on top of stack:
762 lua_pop(L, 1);
763 } else {
764 // if "current value" is not acceptable:
765 // pop metatable and "current value":
766 lua_pop(L, 2);
767 // throw error if parent element does not exist:
768 if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON object");
769 // push new JSON object as "current value" onto stack:
770 lua_newtable(L);
771 // create and register shadow table:
772 lua_pushvalue(L, -1);
773 lua_newtable(L);
774 lua_rawset(L, json_set_shadowtbl_idx);
775 // set metatable of JSON object:
776 lua_pushvalue(L, json_set_objectmt_idx);
777 lua_setmetatable(L, -2);
778 // set entry in "parent value":
779 lua_pushvalue(L, idx-1);
780 lua_pushvalue(L, -2);
781 lua_settable(L, -4);
782 }
783 break;
784 case LUA_TNUMBER:
785 // if path key is a number,
786 // check if "current value" is a JSON array (or table without metatable):
787 if (
788 lua_rawequal(L, -1, json_set_arraymt_idx) ||
789 (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE)
790 ) {
791 // if "current value" is acceptable,
792 // pop metatable and leave "current value" on top of stack:
793 lua_pop(L, 1);
794 } else {
795 // if "current value" is not acceptable:
796 // pop metatable and "current value":
797 lua_pop(L, 2);
798 // throw error if parent element does not exist:
799 if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON array");
800 // push new JSON array as "current value" onto stack:
801 lua_newtable(L);
802 // create and register shadow table:
803 lua_pushvalue(L, -1);
804 lua_newtable(L);
805 lua_rawset(L, json_set_shadowtbl_idx);
806 // set metatable of JSON array:
807 lua_pushvalue(L, json_set_arraymt_idx);
808 lua_setmetatable(L, -2);
809 // set entry in "parent value":
810 lua_pushvalue(L, idx-1);
811 lua_pushvalue(L, -2);
812 lua_settable(L, -4);
813 }
814 break;
815 default:
816 return luaL_error(L, "Invalid path key of type %s", lua_typename(L, lua_type(L, idx)));
817 }
818 // check if last path element is being processed:
819 if (idx == stacktop) {
820 // if the last path element is being processed,
821 // set last path value in "current value" container:
822 lua_pushvalue(L, idx);
823 lua_pushvalue(L, 2 + json_set_idxshift);
824 lua_settable(L, -3);
825 } else {
826 // if the processed path element is not the last,
827 // use old "current value" as new "parent value"
828 lua_remove(L, -2);
829 // push new "current value" onto stack by performing a lookup:
830 lua_pushvalue(L, idx);
831 lua_gettable(L, -2);
832 }
833 }
834 // return first argument for convenience:
835 lua_settop(L, 1 + json_set_idxshift);
836 return 1;
837 }
839 // returns the length of a JSON array (or zero for a table without numeric keys):
840 static int json_len(lua_State *L) {
841 // stack shall contain one function argument:
842 lua_settop(L, 1);
843 // try to get corresponding shadow table for first argument:
844 json_regfetch(L, shadowtbl);
845 lua_pushvalue(L, 1);
846 lua_rawget(L, -2);
847 // if shadow table does not exist, return length of argument, else length of shadow table:
848 lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
849 return 1;
850 }
852 // __index metamethod for JSON objects and JSON arrays:
853 static int json_index(lua_State *L) {
854 // stack shall contain two function arguments:
855 lua_settop(L, 2);
856 // get corresponding shadow table for first argument:
857 json_regfetch(L, shadowtbl);
858 lua_pushvalue(L, 1);
859 lua_rawget(L, -2);
860 // throw error if no shadow table was found:
861 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
862 // use key passed as second argument to lookup value in shadow table:
863 lua_pushvalue(L, 2);
864 lua_rawget(L, -2);
865 // if value is null-marker, then push nil onto stack:
866 if (json_isnullmark(L, -1)) lua_pushnil(L);
867 // return either looked up value, or nil
868 return 1;
869 }
871 // __newindex metamethod for JSON objects and JSON arrays:
872 static int json_newindex(lua_State *L) {
873 // stack shall contain three function arguments:
874 lua_settop(L, 3);
875 // get corresponding shadow table for first argument:
876 json_regfetch(L, shadowtbl);
877 lua_pushvalue(L, 1);
878 lua_rawget(L, -2);
879 // throw error if no shadow table was found:
880 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
881 // replace first argument with shadow table:
882 lua_replace(L, 1);
883 // reset stack and use second and third argument to write to shadow table:
884 lua_settop(L, 3);
885 lua_rawset(L, 1);
886 // return nothing:
887 return 0;
888 }
890 // function returned as first value by json_pairs function:
891 static int json_pairs_iterfunc(lua_State *L) {
892 // stack shall contain two function arguments:
893 lua_settop(L, 2);
894 // get corresponding shadow table for first argument:
895 json_regfetch(L, shadowtbl);
896 lua_pushvalue(L, 1);
897 lua_rawget(L, -2);
898 // throw error if no shadow table was found:
899 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
900 // get next key value pair from shadow table (using previous key from argument 2)
901 // and return nothing if there is no next pair:
902 lua_pushvalue(L, 2);
903 if (!lua_next(L, -2)) return 0;
904 // replace null-marker with nil:
905 if (json_isnullmark(L, -1)) {
906 lua_pop(L, 1);
907 lua_pushnil(L);
908 }
909 // return key and value (or key and nil, if null-marker was found):
910 return 2;
911 }
913 // returns a triple such that 'for key, value in pairs(obj) do ... end'
914 // iterates through all key value pairs (including JSON null values represented as Lua nil):
915 static int json_pairs(lua_State *L) {
916 // require one argument to function
917 luaL_checkany(L, 1);
918 // return triple of function json_pairs_iterfunc, first argument, and nil:
919 lua_pushcfunction(L, json_pairs_iterfunc);
920 lua_pushvalue(L, 1);
921 lua_pushnil(L);
922 return 3;
923 }
925 // function returned as first value by json_ipairs function:
926 static int json_ipairs_iterfunc(lua_State *L) {
927 lua_Integer idx;
928 // stack shall contain two function arguments:
929 lua_settop(L, 2);
930 // calculate new index by incrementing second argument:
931 idx = lua_tointeger(L, 2) + 1;
932 // get corresponding shadow table for first argument:
933 json_regfetch(L, shadowtbl);
934 lua_pushvalue(L, 1);
935 lua_rawget(L, -2);
936 // throw error if no shadow table was found:
937 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
938 // do integer lookup in shadow table:
939 lua_rawgeti(L, -1, idx);
940 // return nothing if there was no value:
941 if (lua_isnil(L, -1)) return 0;
942 // return new index and
943 // either the looked up value if it is not equal to the null-marker
944 // or nil instead of null-marker:
945 lua_pushinteger(L, idx);
946 if (json_isnullmark(L, -2)) lua_pushnil(L);
947 else lua_pushvalue(L, -2);
948 return 2;
949 }
951 // returns a triple such that 'for idx, value in ipairs(ary) do ... end'
952 // iterates through all values (including JSON null values represented as Lua nil):
953 static int json_ipairs(lua_State *L) {
954 // require one argument to function
955 luaL_checkany(L, 1);
956 // return triple of function json_ipairs_iterfunc, first argument, and zero:
957 lua_pushcfunction(L, json_ipairs_iterfunc);
958 lua_pushvalue(L, 1);
959 lua_pushinteger(L, 0);
960 return 3;
961 }
963 // datatype representing a table key:
964 // (used for sorting)
965 typedef struct {
966 size_t length;
967 const char *data;
968 } json_key_t;
970 // comparation function for table keys to be passed to qsort function:
971 static int json_key_cmp(json_key_t *key1, json_key_t *key2) {
972 size_t pos = 0;
973 unsigned char c1, c2;
974 while (1) {
975 if (key1->length > pos) {
976 if (key2->length > pos) {
977 c1 = key1->data[pos];
978 c2 = key2->data[pos];
979 if (c1 < c2) return -1;
980 else if (c1 > c2) return 1;
981 } else {
982 return 1;
983 }
984 } else {
985 if (key2->length > pos) {
986 return -1;
987 } else {
988 return 0;
989 }
990 }
991 pos++;
992 }
993 }
995 // constants for type detection of ambiguous tables:
996 #define JSON_TABLETYPE_UNKNOWN 0
997 #define JSON_TABLETYPE_OBJECT 1
998 #define JSON_TABLETYPE_ARRAY 2
1000 typedef struct {
1001 int type;
1002 int pos;
1003 int count;
1004 json_key_t keys[1]; // or more
1005 } json_container_t;
1007 // special Lua stack indicies for json_export function:
1008 #define json_export_value_idx 1
1009 #define json_export_indentstring_idx 2
1010 #define json_export_objectmt_idx 3
1011 #define json_export_arraymt_idx 4
1012 #define json_export_shadowtbl_idx 5
1013 #define json_export_stackswap_idx 6
1014 #define json_export_luacontainer_idx 7
1015 #define json_export_ccontainer_idx 8
1016 #define json_export_buffer_idx 9
1018 // encodes a JSON document (passed as first argument)
1019 // optionally using indentation (indentation string or true passed as second argument)
1020 static int json_export(lua_State *L) {
1021 int pretty; // pretty printing on? (i.e. printing with indentation)
1022 luaL_Buffer buf; // Lua buffer containing result string
1023 lua_Number num; // number to encode
1024 const char *str; // string to encode
1025 size_t strlen; // length of string to encode
1026 size_t strpos ; // position in string or position of current key
1027 unsigned char c; // character to encode (unsigned!)
1028 char hexcode[7]; // store for unicode hex escape sequence
1029 // NOTE: 7 bytes due to backslash, character 'u', 4 hex digits, and terminating NULL byte
1030 int tabletype; // table type: unknown, JSON object, or JSON array
1031 size_t keycount = 0; // number of string keys in object
1032 json_key_t *key; // pointer to C structure containing a string key
1033 int level = 0; // current depth level
1034 int i; // iteration variable for level dependent repetitions
1035 int stackswapidx = 0; // elements in stack swap table
1036 int containerkey = 0; // temporarily set to 1, if a container key is being encoded
1037 json_container_t *container = NULL; // pointer to current C struct for container information
1038 // stack shall contain two function arguments:
1039 lua_settop(L, 2);
1040 // check if pretty printing (with indentation) is desired:
1041 if (lua_toboolean(L, json_export_indentstring_idx)) {
1042 // if yes,
1043 // set pretty variable to 1:
1044 pretty = 1;
1045 // check if second argument is a boolean (true):
1046 if (lua_isboolean(L, json_export_indentstring_idx)) {
1047 // if yes,
1048 // use default indentation if indentation argument is boolean true:
1049 lua_pushliteral(L, " ");
1050 lua_replace(L, json_export_indentstring_idx);
1051 } else {
1052 // if no,
1053 // require second argument to be a string:
1054 luaL_checktype(L, json_export_indentstring_idx, LUA_TSTRING);
1056 } else {
1057 // if no,
1058 // set pretty variable to 0:
1059 pretty = 0;
1061 // push objectmt onto stack position 3:
1062 json_regfetch(L, objectmt);
1063 // push arraymt onto stack position 4:
1064 json_regfetch(L, arraymt);
1065 // push shadowtbl onto stack position 5:
1066 json_regfetch(L, shadowtbl);
1067 // push table for stack swapping onto stack position 6:
1068 lua_newtable(L);
1069 // create placeholders on stack positions 7 through 8:
1070 lua_settop(L, json_export_buffer_idx);
1071 // create Lua string buffer:
1072 luaL_buffinit(L, &buf);
1073 // loop:
1074 while (1) {
1075 // if value to encode is the null-marker, then treat it the same as nil:
1076 if (json_isnullmark(L, json_export_value_idx)) {
1077 lua_pushnil(L);
1078 lua_replace(L, json_export_value_idx);
1080 // distinguish between different Lua types:
1081 switch (lua_type(L, json_export_value_idx)) {
1082 // value to encode is nil:
1083 case LUA_TNIL:
1084 // add string "null" to output buffer:
1085 luaL_addstring(&buf, "null");
1086 break;
1087 // value to encode is of type number:
1088 case LUA_TNUMBER:
1089 // convert value to double precision number:
1090 num = lua_tonumber(L, json_export_value_idx);
1091 // throw error if number is not-a-number:
1092 if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value");
1093 // throw error if number is positive or negative infinity:
1094 if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers");
1095 // add Lua's string encoding of the number to the output buffer:
1096 lua_pushvalue(L, json_export_value_idx);
1097 lua_tostring(L, -1);
1098 luaL_addvalue(&buf);
1099 break;
1100 // value to encode is of type boolean:
1101 case LUA_TBOOLEAN:
1102 // add string "true" or "false" according to boolean value:
1103 luaL_addstring(&buf, lua_toboolean(L, json_export_value_idx) ? "true" : "false");
1104 break;
1105 // value to encode is of type string:
1106 case LUA_TSTRING:
1107 // add quoted and escaped string to output buffer:
1108 str = lua_tolstring(L, json_export_value_idx, &strlen);
1109 luaL_addchar(&buf, '"');
1110 strpos = 0;
1111 while (strpos < strlen) {
1112 c = str[strpos++];
1113 if (c == '"') luaL_addstring(&buf, "\\\"");
1114 else if (c == '\\') luaL_addstring(&buf, "\\\\");
1115 else if (c == 127) luaL_addstring(&buf, "\\u007F");
1116 else if (c >= 32) luaL_addchar(&buf, c);
1117 else if (c == '\b') luaL_addstring(&buf, "\\b");
1118 else if (c == '\f') luaL_addstring(&buf, "\\f");
1119 else if (c == '\n') luaL_addstring(&buf, "\\n");
1120 else if (c == '\r') luaL_addstring(&buf, "\\r");
1121 else if (c == '\t') luaL_addstring(&buf, "\\t");
1122 else if (c == '\v') luaL_addstring(&buf, "\\v");
1123 else {
1124 sprintf(hexcode, "\\u%04X", c);
1125 luaL_addstring(&buf, hexcode);
1128 luaL_addchar(&buf, '"');
1129 break;
1130 // value to encode is of type table (this includes JSON objects and JSON arrays):
1131 case LUA_TTABLE:
1132 // use table's metatable to try to determine type of table:
1133 tabletype = JSON_TABLETYPE_UNKNOWN;
1134 if (lua_getmetatable(L, json_export_value_idx)) {
1135 if (lua_rawequal(L, -1, json_export_objectmt_idx)) {
1136 tabletype = JSON_TABLETYPE_OBJECT;
1137 } else {
1138 if (lua_rawequal(L, -1, json_export_arraymt_idx)) {
1139 tabletype = JSON_TABLETYPE_ARRAY;
1140 } else {
1141 return luaL_error(L, "JSON export not possible for tables with nonsupported metatable");
1144 // reset stack (pop metatable from stack):
1145 lua_pop(L, 1);
1147 // replace table with its shadow table if existent, and reset stack:
1148 lua_pushvalue(L, json_export_value_idx);
1149 lua_rawget(L, json_export_shadowtbl_idx);
1150 if (lua_isnil(L, -1)) lua_pop(L, 1);
1151 else lua_replace(L, json_export_value_idx);
1152 // check if type of table is still undetermined
1153 // and optionally calculate number of string keys (keycount)
1154 // or set keycount to zero:
1155 keycount = 0;
1156 if (tabletype == JSON_TABLETYPE_UNKNOWN) {
1157 // if type of table is undetermined,
1158 // iterate over all keys:
1159 for (lua_pushnil(L); lua_next(L, json_export_value_idx); lua_pop(L, 1)) {
1160 switch (lua_type(L, -2)) {
1161 case LUA_TSTRING:
1162 // for string keys,
1163 // increase keycount (may avoid another iteration):
1164 keycount++;
1165 // if type of table was unknown, then type of table is a JSON object now:
1166 if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT;
1167 // if type of table was a JSON array, then the type of table is ambiguous now
1168 // and an error is thrown:
1169 else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error;
1170 break;
1171 case LUA_TNUMBER:
1172 // for numeric keys,
1173 // if type of table was unknown, then type of table is a JSON array now:
1174 if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY;
1175 // if type of table was a JSON object, then the type of table is ambiguous now
1176 // and an error is thrown:
1177 else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error;
1178 break;
1182 // raise error if too many nested levels:
1183 if (level >= JSON_MAXDEPTH) {
1184 return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH);
1186 // store previous container information (if existent) on stack swap
1187 // and increase level variable:
1188 if (level++) {
1189 lua_pushvalue(L, json_export_luacontainer_idx);
1190 lua_rawseti(L, json_export_stackswap_idx, ++stackswapidx);
1191 lua_pushvalue(L, json_export_ccontainer_idx);
1192 lua_rawseti(L, json_export_stackswap_idx, ++stackswapidx);
1194 // use value as current container:
1195 lua_pushvalue(L, json_export_value_idx);
1196 lua_replace(L, json_export_luacontainer_idx);
1197 // distinguish between JSON objects and JSON arrays:
1198 switch (tabletype) {
1199 // JSON object:
1200 case JSON_TABLETYPE_OBJECT:
1201 // calculate count of string keys unless it has been calculated before:
1202 if (!keycount) {
1203 for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) {
1204 if (lua_type(L, -2) == LUA_TSTRING) keycount++;
1207 // allocate memory for C structure containing string keys and container iteration state:
1208 container = lua_newuserdata(L, sizeof(json_container_t) + (keycount-1) * sizeof(json_key_t));
1209 // store reference to C structure on designated stack position:
1210 lua_replace(L, json_export_ccontainer_idx);
1211 // initialize C structure for container state:
1212 container->type = JSON_TABLETYPE_OBJECT;
1213 container->count = keycount;
1214 container->pos = 0;
1215 // check if object contains any keys:
1216 if (keycount) {
1217 // if yes,
1218 // copy all string keys to the C structure (and reset container->pos again):
1219 for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) {
1220 if (lua_type(L, -2) == LUA_TSTRING) {
1221 json_key_t *key = &container->keys[container->pos++];
1222 key->data = lua_tolstring(L, -2, &key->length);
1225 container->pos = 0;
1226 // sort C array using quicksort:
1227 qsort(container->keys, keycount, sizeof(json_key_t), (void *)json_key_cmp);
1229 // add opening bracket to output buffer:
1230 luaL_addchar(&buf, '{');
1231 break;
1232 // JSON array:
1233 case JSON_TABLETYPE_ARRAY:
1234 // allocate memory for C structure for container iteration state:
1235 container = lua_newuserdata(L, sizeof(json_container_t) - sizeof(json_key_t));
1236 // store reference to C structure on designated stack position:
1237 lua_replace(L, json_export_ccontainer_idx);
1238 // initialize C structure for container state:
1239 container->type = JSON_TABLETYPE_ARRAY;
1240 container->pos = 0;
1241 // add opening bracket to output buffer:
1242 luaL_addchar(&buf, '[');
1243 break;
1244 default:
1245 // throw error if table type is unknown:
1246 json_export_tabletype_error:
1247 return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)");
1249 break;
1250 default:
1251 // all other datatypes are considered an error:
1252 return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, json_export_value_idx)));
1254 // check if a container is being processed:
1255 if (container) {
1256 // if yes,
1257 // execute code for container iteration:
1258 json_export_container:
1259 // distinguish between JSON objects and JSON arrays:
1260 switch (container->type) {
1261 // JSON object:
1262 case JSON_TABLETYPE_OBJECT:
1263 // finish iteration if all string keys have been processed:
1264 if (container->pos == container->count) goto json_export_close;
1265 // push current string key on top of stack:
1266 key = &container->keys[container->pos];
1267 lua_pushlstring(L, key->data, key->length);
1268 // check if the key has already been exported:
1269 if (!containerkey) {
1270 // if no,
1271 // add a comma to the output buffer if necessary:
1272 if (container->pos) luaL_addchar(&buf, ',');
1273 // set containerkey variable to true:
1274 containerkey = 1;
1275 } else {
1276 // if a key has already been exported,
1277 // add a colon to the output buffer:
1278 luaL_addchar(&buf, ':');
1279 // add a space to the output buffer for pretty results:
1280 if (pretty) luaL_addchar(&buf, ' ');
1281 // replace string key on top of stack with corresponding value:
1282 lua_rawget(L, json_export_luacontainer_idx);
1283 // reset containerkey variable
1284 containerkey = 0;
1285 // increase number of processed key value pairs:
1286 container->pos++;
1288 // store key or value on top of stack in designated stack position:
1289 lua_replace(L, json_export_value_idx);
1290 break;
1291 // JSON array:
1292 case JSON_TABLETYPE_ARRAY:
1293 // store next value in designated stack position:
1294 lua_rawgeti(L, json_export_luacontainer_idx, container->pos+1);
1295 lua_replace(L, json_export_value_idx);
1296 // finish iteration if value is nil:
1297 if (lua_isnil(L, json_export_value_idx)) goto json_export_close;
1298 // add a comma to the output buffer if necessary:
1299 if (container->pos) luaL_addchar(&buf, ',');
1300 // increase number of processed values:
1301 container->pos++;
1302 break;
1303 // common code for closing JSON objects or JSON arrays:
1304 json_export_close:
1305 // decrement level variable:
1306 level--;
1307 // handle indentation for pretty results:
1308 if (pretty && container->pos) {
1309 luaL_addchar(&buf, '\n');
1310 for (i=0; i<level; i++) {
1311 lua_pushvalue(L, json_export_indentstring_idx);
1312 luaL_addvalue(&buf);
1315 // add closing bracket to output buffer:
1316 luaL_addchar(&buf, container->type == JSON_TABLETYPE_OBJECT ? '}' : ']');
1317 // finish export if last level has been closed:
1318 if (!level) goto json_export_finish;
1319 // otherwise,
1320 // recall previous container information from stack swap
1321 // and set C pointer to corresponding C struct:
1322 lua_rawgeti(L, json_export_stackswap_idx, stackswapidx--);
1323 lua_replace(L, json_export_ccontainer_idx);
1324 container = lua_touserdata(L, json_export_ccontainer_idx);
1325 lua_rawgeti(L, json_export_stackswap_idx, stackswapidx--);
1326 lua_replace(L, json_export_luacontainer_idx);
1327 // repeat code for container iteration:
1328 goto json_export_container;
1330 // handle indentation for pretty results:
1331 if (pretty && (containerkey || container->type == JSON_TABLETYPE_ARRAY)) {
1332 luaL_addchar(&buf, '\n');
1333 for (i=0; i<level; i++) {
1334 lua_pushvalue(L, json_export_indentstring_idx);
1335 luaL_addvalue(&buf);
1338 } else {
1339 // if no container is being processed,
1340 // finish export:
1341 json_export_finish:
1342 // for pretty results, add final newline character if outermost container is processed:
1343 if (pretty) luaL_addchar(&buf, '\n');
1344 // create and return Lua string from buffer contents
1345 luaL_pushresult(&buf);
1346 return 1;
1351 // functions in library module:
1352 static const struct luaL_Reg json_module_functions[] = {
1353 {"object", json_object},
1354 {"array", json_array},
1355 {"import", json_import},
1356 {"export", json_export},
1357 {"get", json_get},
1358 {"type", json_type},
1359 {"set", json_set},
1360 {NULL, NULL}
1361 };
1363 // metamethods for JSON objects, JSON arrays, and unknown JSON collections (object or array):
1364 static const struct luaL_Reg json_metatable_functions[] = {
1365 {"__len", json_len},
1366 {"__index", json_index},
1367 {"__newindex", json_newindex},
1368 {"__pairs", json_pairs},
1369 {"__ipairs", json_ipairs},
1370 {"__tostring", json_export},
1371 {NULL, NULL}
1372 };
1374 // metamethods for JSON null marker:
1375 static const struct luaL_Reg json_nullmark_metamethods[] = {
1376 {"__tostring", json_nullmark_tostring},
1377 {NULL, NULL}
1378 };
1380 // initializes json library:
1381 int luaopen_json(lua_State *L) {
1382 // empty stack:
1383 lua_settop(L, 0);
1384 // push library module onto stack position 1:
1385 lua_newtable(L);
1386 // register library functions:
1387 luaL_setfuncs(L, json_module_functions, 0);
1388 // create and store objectmt:
1389 lua_newtable(L);
1390 luaL_setfuncs(L, json_metatable_functions, 0);
1391 json_regstore(L, objectmt);
1392 // create and store arraymt:
1393 lua_newtable(L);
1394 luaL_setfuncs(L, json_metatable_functions, 0);
1395 json_regstore(L, arraymt);
1396 // create and store ephemeron table to store shadow tables for each JSON object/array
1397 // to allow NULL values returned as nil
1398 lua_newtable(L);
1399 lua_newtable(L); // metatable for ephemeron table
1400 lua_pushliteral(L, "__mode");
1401 lua_pushliteral(L, "k");
1402 lua_rawset(L, -3);
1403 lua_setmetatable(L, -2);
1404 json_regstore(L, shadowtbl);
1405 // set metatable of null marker and make it available through library module:
1406 json_pushnullmark(L);
1407 lua_newtable(L);
1408 luaL_setfuncs(L, json_nullmark_metamethods, 0);
1409 lua_setmetatable(L, -2);
1410 lua_setfield(L, 1, "null");
1411 // return library module (that's expected on top of stack):
1412 return 1;

Impressum / About Us