webmcp

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

Impressum / About Us