webmcp

annotate libraries/json/json.c @ 428:52ebde158c92

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

Impressum / About Us