webmcp

annotate libraries/json/json.c @ 173:f417c4b607ed

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

Impressum / About Us