webmcp
changeset 183:f7c1869b5b32
Reimplemented json.export(...) function without recursion (incomplete yet)
author | jbe |
---|---|
date | Sat Aug 09 19:39:16 2014 +0200 (2014-08-09) |
parents | b460ae08dd78 |
children | 1f925cb34299 |
files | libraries/json/json.c |
line diff
1.1 --- a/libraries/json/json.c Sat Aug 02 02:53:32 2014 +0200 1.2 +++ b/libraries/json/json.c Sat Aug 09 19:39:16 2014 +0200 1.3 @@ -6,16 +6,11 @@ 1.4 1.5 // maximum number of nested JSON values (objects and arrays): 1.6 // NOTE: json_import can store 2^32 / 3 levels on stack swap (using 1.7 -// also negative indicies after integer wraparound), so 1.8 -// 1024^3 = 1073741824 is a safe value. 1.9 +// also negative indicies after integer wraparound), and 1.10 +// json_export can store even more levels, so 1024^3 = 1.11 +// 1073741824 is a safe value and allows practically unlimited 1.12 +// levels for JSON documents <= 2 GiB. 1.13 #define JSON_MAXDEPTH (1024*1024*1024) 1.14 -// NOTE: As long as the function json_export_internal is implemented 1.15 -// recursively, JSON_MAXDEPTH needs to be low (e.g. 50) to avoid C stack 1.16 -// overflows. 1.17 -#if JSON_MAXDEPTH > 50 1.18 -#undef JSON_MAXDEPTH 1.19 -#define JSON_MAXDEPTH 50 1.20 -#endif 1.21 1.22 // generate dummy memory addresses that represents null values: 1.23 char json_nullmark; 1.24 @@ -1002,345 +997,312 @@ 1.25 #define JSON_TABLETYPE_OBJECT 1 1.26 #define JSON_TABLETYPE_ARRAY 2 1.27 1.28 -// special Lua stack indicies for json_export_internal function: 1.29 -#define json_export_internal_indentstring_idx 1 1.30 -#define json_export_internal_level_idx 2 1.31 -#define json_export_internal_value_idx 3 1.32 -#define json_export_internal_tmp_idx 4 1.33 +typedef struct { 1.34 + int type; 1.35 + int pos; 1.36 + int count; 1.37 + json_key_t keys[1]; // or more 1.38 +} json_container_t; 1.39 + 1.40 +// special Lua stack indicies for json_export function: 1.41 +#define json_export_value_idx 1 1.42 +#define json_export_indentstring_idx 2 1.43 +#define json_export_objectmt_idx 3 1.44 +#define json_export_arraymt_idx 4 1.45 +#define json_export_shadowtbl_idx 5 1.46 +#define json_export_stackswap_idx 6 1.47 +#define json_export_luacontainer_idx 7 1.48 +#define json_export_ccontainer_idx 8 1.49 1.50 -// encodes a JSON document (passed as third argument) 1.51 -// optionally using indentation (indentation string passed as first argument) 1.52 -// for a certain depth level (passed as second argument): 1.53 -static int json_export_internal(lua_State *L) { 1.54 +// encodes a JSON document (passed as first argument) 1.55 +// optionally using indentation (indentation string passed as second argument) 1.56 +static int json_export(lua_State *L) { 1.57 + int pretty; // pretty printing on? (i.e. printing with indentation) 1.58 + luaL_Buffer buf; // Lua buffer containing result string 1.59 + lua_Number num; // number to encode 1.60 + const char *str; // string to encode 1.61 + size_t strlen; // length of string to encode 1.62 + size_t strpos ; // position in string or position of current key 1.63 + unsigned char c; // character to encode (unsigned!) 1.64 + char hexcode[7]; // store for unicode hex escape sequence 1.65 + // NOTE: 7 bytes due to backslash, character 'u', 4 hex digits, and terminating NULL byte 1.66 + int tabletype; // table type: unknown, JSON object, or JSON array 1.67 + size_t keycount = 0; // number of string keys in object 1.68 + int level = 0; // current depth level 1.69 + int i; // iteration variable for level dependent repetitions 1.70 + int stackswapidx = 0; // elements in stack swap table 1.71 + int containerkey = 0; // temporarily set to 1, if a container key is being encoded 1.72 + json_container_t *container = NULL; // pointer to current C struct for container information 1.73 +/* 1.74 int level; // current depth level 1.75 - int pretty; // pretty printing on? (i.e. printing with indentation) 1.76 - int i; // iteration variable for level dependent repetitions: 1.77 - lua_Number num; // number to encode 1.78 - const char *str; // string to encode 1.79 - size_t strlen; // length of string to encode 1.80 - unsigned char c; // character to encode (unsigned!) 1.81 - size_t pos = 0; // position in string or position of current key (initialized with zero!) 1.82 - luaL_Buffer buf; // Lua buffer to create strings 1.83 - char hexcode[7]; // store for unicode hex escape sequence 1.84 - // NOTE: 7 bytes due to backslash, character 'u', 4 hex digits, and terminating NULL byte 1.85 - int tabletype = JSON_TABLETYPE_UNKNOWN; // table type: unknown, JSON object, or JSON array 1.86 int anyelement = 0; // set to 1 if at least one array element has been processed 1.87 - size_t keycount = 0; // number of string keys in object 1.88 json_key_t *keybuf = NULL; // temporary buffer to store (and sort) string keys of objects 1.89 lua_Integer arrayidx; // index to iterate through arrays 1.90 - // stack shall contain three function arguments: 1.91 - lua_settop(L, json_export_internal_value_idx); 1.92 - // if value to encode is the null-marker, then treat it the same as nil: 1.93 - if (json_isnullmark(L, json_export_internal_value_idx)) { 1.94 - lua_pop(L, 1); 1.95 - lua_pushnil(L); 1.96 +*/ 1.97 + // stack shall contain two function arguments: 1.98 + lua_settop(L, 2); 1.99 + // use default indentation if indentation argument is (boolean) true: 1.100 + if ( 1.101 + lua_isboolean(L, json_export_indentstring_idx) && 1.102 + lua_toboolean(L, json_export_indentstring_idx) 1.103 + ) { 1.104 + lua_pushliteral(L, " "); 1.105 + lua_replace(L, json_export_indentstring_idx); 1.106 } 1.107 - // distinguish between different Lua types: 1.108 - switch (lua_type(L, json_export_internal_value_idx)) { 1.109 - // value to encode is nil: 1.110 - case LUA_TNIL: 1.111 - // return string "null": 1.112 - lua_pushliteral(L, "null"); 1.113 - return 1; 1.114 - // value to encode is of type number: 1.115 - case LUA_TNUMBER: 1.116 - // convert value to double precision number: 1.117 - num = lua_tonumber(L, json_export_internal_value_idx); 1.118 - // throw error if number is not-a-number: 1.119 - if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value"); 1.120 - // throw error if number is positive or negative infinity: 1.121 - if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers"); 1.122 - // return Lua's string encoding of the number: 1.123 - lua_tostring(L, json_export_internal_value_idx); 1.124 - return 1; 1.125 - // value to encode is of type boolean: 1.126 - case LUA_TBOOLEAN: 1.127 - // return string "true" or "false" according to boolean value: 1.128 - if (lua_toboolean(L, json_export_internal_value_idx)) { 1.129 - lua_pushliteral(L, "true"); 1.130 - } else { 1.131 - lua_pushliteral(L, "false"); 1.132 + // set pretty variable to 1 if pretty printing (with indentation) is desired: 1.133 + pretty = lua_toboolean(L, json_export_indentstring_idx); 1.134 + // push objectmt onto stack position 3: 1.135 + json_regfetch(L, objectmt); 1.136 + // push arraymt onto stack position 4: 1.137 + json_regfetch(L, arraymt); 1.138 + // push shadowtbl onto stack position 5: 1.139 + json_regfetch(L, shadowtbl); 1.140 + // push table for stack swapping onto stack position 6: 1.141 + lua_newtable(L); 1.142 + // create placeholders on stack positions 7 through 8: 1.143 + lua_settop(L, 9); 1.144 + // create Lua string buffer: 1.145 + luaL_buffinit(L, &buf); 1.146 + // loop: 1.147 + while (1) { 1.148 + // if value to encode is the null-marker, then treat it the same as nil: 1.149 + if (json_isnullmark(L, json_export_value_idx)) { 1.150 + lua_pushnil(L); 1.151 + lua_replace(L, json_export_value_idx); 1.152 } 1.153 - return 1; 1.154 - // value to encode is of type string: 1.155 - case LUA_TSTRING: 1.156 - // quote, escape and return string: 1.157 - str = lua_tolstring(L, 3, &strlen); 1.158 - luaL_buffinit(L, &buf); 1.159 - luaL_addchar(&buf, '"'); 1.160 - while (pos < strlen) { 1.161 - c = str[pos++]; 1.162 - if (c == '"') luaL_addstring(&buf, "\\\""); 1.163 - else if (c == '\\') luaL_addstring(&buf, "\\\\"); 1.164 - else if (c == 127) luaL_addstring(&buf, "\\u007F"); 1.165 - else if (c >= 32) luaL_addchar(&buf, c); 1.166 - else if (c == '\b') luaL_addstring(&buf, "\\b"); 1.167 - else if (c == '\f') luaL_addstring(&buf, "\\f"); 1.168 - else if (c == '\n') luaL_addstring(&buf, "\\n"); 1.169 - else if (c == '\r') luaL_addstring(&buf, "\\r"); 1.170 - else if (c == '\t') luaL_addstring(&buf, "\\t"); 1.171 - else if (c == '\v') luaL_addstring(&buf, "\\v"); 1.172 - else { 1.173 - sprintf(hexcode, "\\u%04X", c); 1.174 - luaL_addstring(&buf, hexcode); 1.175 - } 1.176 - } 1.177 - luaL_addchar(&buf, '"'); 1.178 - luaL_pushresult(&buf); 1.179 - return 1; 1.180 - // value to encode is of type table (this includes JSON objects and JSON arrays): 1.181 - case LUA_TTABLE: 1.182 - // use table's metatable to try to determine type of table: 1.183 - if (lua_getmetatable(L, json_export_internal_value_idx)) { 1.184 - json_regfetch(L, objectmt); 1.185 - if (lua_rawequal(L, -2, -1)) { 1.186 - tabletype = JSON_TABLETYPE_OBJECT; 1.187 - } else { 1.188 - json_regfetch(L, arraymt); 1.189 - if (lua_rawequal(L, -3, -1)) { 1.190 - tabletype = JSON_TABLETYPE_ARRAY; 1.191 - } else { 1.192 - return luaL_error(L, "JSON export not possible for tables with nonsupported metatable"); 1.193 - } 1.194 - } 1.195 - } 1.196 - // replace table with its shadow table if existent, and reset stack: 1.197 - json_regfetch(L, shadowtbl); 1.198 - lua_pushvalue(L, json_export_internal_value_idx); 1.199 - lua_rawget(L, -2); 1.200 - if (!lua_isnil(L, -1)) lua_replace(L, json_export_internal_value_idx); 1.201 - lua_settop(L, json_export_internal_value_idx); 1.202 - // check if type of table is still undetermined: 1.203 - if (tabletype == JSON_TABLETYPE_UNKNOWN) { 1.204 - // if yes, iterate over all keys: 1.205 - for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { 1.206 - switch (lua_type(L, -2)) { 1.207 - case LUA_TSTRING: 1.208 - // for string keys, 1.209 - // increase keycount (may avoid another iteration): 1.210 - keycount++; 1.211 - // if type of table was unknown, then type of table is a JSON object now: 1.212 - if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT; 1.213 - // if type of table was a JSON array, then the type of table is ambiguous now 1.214 - // and an error is thrown: 1.215 - else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error; 1.216 - break; 1.217 - case LUA_TNUMBER: 1.218 - // for numeric keys, 1.219 - // if type of table was unknown, then type of table is a JSON array now: 1.220 - if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY; 1.221 - // if type of table was a JSON object, then the type of table is ambiguous now 1.222 - // and an error is thrown: 1.223 - else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error; 1.224 - break; 1.225 + // distinguish between different Lua types: 1.226 + switch (lua_type(L, json_export_value_idx)) { 1.227 + // value to encode is nil: 1.228 + case LUA_TNIL: 1.229 + // add string "null" to output buffer: 1.230 + luaL_addstring(&buf, "null"); 1.231 + break; 1.232 + // value to encode is of type number: 1.233 + case LUA_TNUMBER: 1.234 + // convert value to double precision number: 1.235 + num = lua_tonumber(L, json_export_value_idx); 1.236 + // throw error if number is not-a-number: 1.237 + if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value"); 1.238 + // throw error if number is positive or negative infinity: 1.239 + if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers"); 1.240 + // add Lua's string encoding of the number to the output buffer: 1.241 + lua_pushvalue(L, json_export_value_idx); 1.242 + lua_tostring(L, -1); 1.243 + luaL_addvalue(&buf); 1.244 + break; 1.245 + // value to encode is of type boolean: 1.246 + case LUA_TBOOLEAN: 1.247 + // add string "true" or "false" according to boolean value: 1.248 + luaL_addstring(&buf, lua_toboolean(L, json_export_value_idx) ? "true" : "false"); 1.249 + break; 1.250 + // value to encode is of type string: 1.251 + case LUA_TSTRING: 1.252 + // add quoted and escaped string to output buffer: 1.253 + str = lua_tolstring(L, json_export_value_idx, &strlen); 1.254 + luaL_addchar(&buf, '"'); 1.255 + strpos = 0; 1.256 + while (strpos < strlen) { 1.257 + c = str[strpos++]; 1.258 + if (c == '"') luaL_addstring(&buf, "\\\""); 1.259 + else if (c == '\\') luaL_addstring(&buf, "\\\\"); 1.260 + else if (c == 127) luaL_addstring(&buf, "\\u007F"); 1.261 + else if (c >= 32) luaL_addchar(&buf, c); 1.262 + else if (c == '\b') luaL_addstring(&buf, "\\b"); 1.263 + else if (c == '\f') luaL_addstring(&buf, "\\f"); 1.264 + else if (c == '\n') luaL_addstring(&buf, "\\n"); 1.265 + else if (c == '\r') luaL_addstring(&buf, "\\r"); 1.266 + else if (c == '\t') luaL_addstring(&buf, "\\t"); 1.267 + else if (c == '\v') luaL_addstring(&buf, "\\v"); 1.268 + else { 1.269 + sprintf(hexcode, "\\u%04X", c); 1.270 + luaL_addstring(&buf, hexcode); 1.271 } 1.272 } 1.273 - } 1.274 - // set pretty variable to 1 if pretty printing (with indentation) is desired: 1.275 - pretty = lua_toboolean(L, json_export_internal_indentstring_idx); 1.276 - // set level variable to corresponding function argument increased by one: 1.277 - level = lua_tointeger(L, json_export_internal_level_idx) + 1; 1.278 - // throw error if there are more levels that could be imported by this library: 1.279 - if (level > JSON_MAXDEPTH) { 1.280 - return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH); 1.281 - } 1.282 - // distinguish between JSON objects and JSON arrays: 1.283 - switch (tabletype) { 1.284 - // JSON object: 1.285 - case JSON_TABLETYPE_OBJECT: 1.286 - // calculate count of string keys unless it has been calculated before: 1.287 - if (!keycount) { 1.288 - for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { 1.289 - if (lua_type(L, -2) == LUA_TSTRING) keycount++; 1.290 + luaL_addchar(&buf, '"'); 1.291 + break; 1.292 + // value to encode is of type table (this includes JSON objects and JSON arrays): 1.293 + case LUA_TTABLE: 1.294 + // use value as container: 1.295 + lua_pushvalue(L, json_export_value_idx); 1.296 + lua_replace(L, json_export_luacontainer_idx); 1.297 + // use table's metatable to try to determine type of table: 1.298 + tabletype = JSON_TABLETYPE_UNKNOWN; 1.299 + if (lua_getmetatable(L, json_export_luacontainer_idx)) { 1.300 + if (lua_rawequal(L, -1, json_export_objectmt_idx)) { 1.301 + tabletype = JSON_TABLETYPE_OBJECT; 1.302 + } else { 1.303 + if (lua_rawequal(L, -1, json_export_arraymt_idx)) { 1.304 + tabletype = JSON_TABLETYPE_ARRAY; 1.305 + } else { 1.306 + return luaL_error(L, "JSON export not possible for tables with nonsupported metatable"); 1.307 + } 1.308 + } 1.309 + // reset stack (pop metatable from stack): 1.310 + lua_pop(L, 1); 1.311 + } 1.312 + // replace table with its shadow table if existent, and reset stack: 1.313 + lua_pushvalue(L, json_export_luacontainer_idx); 1.314 + lua_rawget(L, json_export_shadowtbl_idx); 1.315 + if (lua_isnil(L, -1)) lua_pop(L, 1); 1.316 + else lua_replace(L, json_export_luacontainer_idx); 1.317 + // check if type of table is still undetermined: 1.318 + if (tabletype == JSON_TABLETYPE_UNKNOWN) { 1.319 + // if yes, iterate over all keys: 1.320 + for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) { 1.321 + switch (lua_type(L, -2)) { 1.322 + case LUA_TSTRING: 1.323 + // for string keys, 1.324 + // increase keycount (may avoid another iteration): 1.325 + keycount++; 1.326 + // if type of table was unknown, then type of table is a JSON object now: 1.327 + if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT; 1.328 + // if type of table was a JSON array, then the type of table is ambiguous now 1.329 + // and an error is thrown: 1.330 + else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error; 1.331 + break; 1.332 + case LUA_TNUMBER: 1.333 + // for numeric keys, 1.334 + // if type of table was unknown, then type of table is a JSON array now: 1.335 + if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY; 1.336 + // if type of table was a JSON object, then the type of table is ambiguous now 1.337 + // and an error is thrown: 1.338 + else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error; 1.339 + break; 1.340 + } 1.341 } 1.342 } 1.343 - // create a sorted list of all string keys in memory: 1.344 - if (keycount) { 1.345 - // allocate memory for string keys: 1.346 - keybuf = calloc(keycount, sizeof(json_key_t)); 1.347 - // check if memory allocation was successful: 1.348 - if (!keybuf) { 1.349 - // in case of memory exhaustion, try to collect garbage: 1.350 - lua_gc(L, LUA_GCCOLLECT, 0); 1.351 - // try to allocate memory again: 1.352 - keybuf = calloc(keycount, sizeof(json_key_t)); 1.353 - // throw error if memory allocation failed again: 1.354 - if (!keybuf) { 1.355 - return luaL_error(L, "Memory allocation failed in JSON library"); 1.356 - } 1.357 - } 1.358 - // copy all string keys to the C array: 1.359 - for (lua_pushnil(L); lua_next(L, json_export_internal_value_idx); lua_pop(L, 1)) { 1.360 - if (lua_type(L, -2) == LUA_TSTRING) { 1.361 - json_key_t *key = keybuf + (pos++); 1.362 - key->data = lua_tolstring(L, -2, &key->length); 1.363 - } 1.364 - } 1.365 - // sort C array using quicksort: 1.366 - qsort(keybuf, keycount, sizeof(json_key_t), (void *)json_key_cmp); 1.367 + // raise error if too many nested levels: 1.368 + if (level >= JSON_MAXDEPTH) { 1.369 + return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH); 1.370 } 1.371 - // create Lua string buffer: 1.372 - luaL_buffinit(L, &buf); 1.373 - // add opening bracket to output buffer: 1.374 - luaL_addchar(&buf, '{'); 1.375 - // iterate through all (sorted) string keys: 1.376 - for (pos=0; pos<keycount; pos++) { 1.377 - json_key_t *key = keybuf + pos; 1.378 - // add comma to output buffer unless we process the first key: 1.379 - if (pos) luaL_addchar(&buf, ','); 1.380 - // handle indentation for pretty results: 1.381 - if (pretty) { 1.382 - luaL_addchar(&buf, '\n'); 1.383 - for (i=0; i<level; i++) { 1.384 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.385 - luaL_addvalue(&buf); 1.386 + // store previous container information (if existent) on stack swap 1.387 + // and increase level variable: 1.388 + if (level++) { 1.389 + lua_pushvalue(L, json_export_luacontainer_idx); 1.390 + lua_rawseti(L, json_export_stackswap_idx, ++stackswapidx); 1.391 + lua_pushvalue(L, json_export_ccontainer_idx); 1.392 + lua_rawseti(L, json_export_stackswap_idx, ++stackswapidx); 1.393 + } 1.394 + // distinguish between JSON objects and JSON arrays: 1.395 + switch (tabletype) { 1.396 + // JSON object: 1.397 + case JSON_TABLETYPE_OBJECT: 1.398 + // calculate count of string keys unless it has been calculated before: 1.399 + if (!keycount) { 1.400 + for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) { 1.401 + if (lua_type(L, -2) == LUA_TSTRING) keycount++; 1.402 } 1.403 } 1.404 - // recursive call to encode key: 1.405 - lua_pushcfunction(L, json_export_internal); 1.406 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.407 - lua_pushinteger(L, level); 1.408 - lua_pushlstring(L, key->data, key->length); 1.409 - if (lua_pcall(L, 3, 1, 0)) { 1.410 - // free memory of sorted string keys on error: 1.411 - if (keybuf) free(keybuf); 1.412 - // rethrow error: 1.413 - return lua_error(L); 1.414 + // create a sorted list of all string keys in memory: 1.415 + if (keycount) { 1.416 + // allocate memory for C structure containing string keys and container iteration state: 1.417 + container = lua_newuserdata(L, sizeof(json_container_t) + (keycount-1) * sizeof(json_key_t)); 1.418 + // store reference on designated stack position: 1.419 + lua_replace(L, json_export_ccontainer_idx); 1.420 + // initialize C structure for container state: 1.421 + container->type = JSON_TABLETYPE_OBJECT; 1.422 + container->count = keycount; 1.423 + container->pos = 0; 1.424 + // copy all string keys to the C structure and reset container->pos again: 1.425 + for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) { 1.426 + if (lua_type(L, -2) == LUA_TSTRING) { 1.427 + json_key_t *key = &container->keys[container->pos++]; 1.428 + key->data = lua_tolstring(L, -2, &key->length); 1.429 + } 1.430 + } 1.431 + container->pos = 0; 1.432 + // sort C array using quicksort: 1.433 + qsort(container->keys, keycount, sizeof(json_key_t), (void *)json_key_cmp); 1.434 } 1.435 - // add encoded key to output buffer: 1.436 - luaL_addvalue(&buf); 1.437 - // add colon to output buffer: 1.438 - luaL_addchar(&buf, ':'); 1.439 - // handle indentation for pretty results: 1.440 - if (pretty) luaL_addchar(&buf, ' '); 1.441 - // recursive call to encode value: 1.442 - lua_pushcfunction(L, json_export_internal); 1.443 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.444 - lua_pushinteger(L, level); 1.445 - lua_pushlstring(L, key->data, key->length); 1.446 - lua_rawget(L, json_export_internal_value_idx); 1.447 - if (lua_pcall(L, 3, 1, 0)) { 1.448 - // free memory of sorted string keys on error: 1.449 - if (keybuf) free(keybuf); 1.450 - // rethrow error: 1.451 - return lua_error(L); 1.452 + // add opening bracket to output buffer: 1.453 + luaL_addchar(&buf, '{'); 1.454 + break; 1.455 + // JSON array: 1.456 + case JSON_TABLETYPE_ARRAY: 1.457 + container = lua_newuserdata(L, sizeof(json_container_t) - sizeof(json_key_t)); 1.458 + lua_replace(L, json_export_ccontainer_idx); 1.459 + container->type = JSON_TABLETYPE_ARRAY; 1.460 + container->pos = 0; 1.461 + // add opening bracket to output buffer: 1.462 + luaL_addchar(&buf, '['); 1.463 + break; 1.464 + default: 1.465 + // throw error if table type is unknown: 1.466 + json_export_tabletype_error: 1.467 + return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)"); 1.468 + } 1.469 + break; 1.470 + default: 1.471 + // all other datatypes are considered an error: 1.472 + return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, json_export_value_idx))); 1.473 + } 1.474 + if (container) { 1.475 + // add colon where necessary: 1.476 + if (containerkey) luaL_addchar(&buf, ':'); 1.477 + switch (container->type) { 1.478 + case JSON_TABLETYPE_OBJECT: 1.479 + if (container->pos < container->count) { 1.480 + json_key_t *key; 1.481 + key = &container->keys[container->pos]; 1.482 + lua_pushlstring(L, key->data, key->length); 1.483 + if (!containerkey) { 1.484 + containerkey = 1; 1.485 + } else { 1.486 + lua_rawget(L, json_export_luacontainer_idx); 1.487 + containerkey = 0; 1.488 + container->pos++; 1.489 + } 1.490 + lua_replace(L, json_export_value_idx); 1.491 + } else { 1.492 + luaL_addchar(&buf, '}'); 1.493 + goto json_export_close; 1.494 } 1.495 - // add encoded value to output buffer: 1.496 - luaL_addvalue(&buf); 1.497 - } 1.498 - // decrement level variable for all following statements: 1.499 - level--; 1.500 - // free memory of sorted string keys: 1.501 - if (keybuf) free(keybuf); 1.502 - // handle indentation for pretty results: 1.503 - if (pretty && keycount != 0) { 1.504 - luaL_addchar(&buf, '\n'); 1.505 - for (i=0; i<level; i++) { 1.506 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.507 - luaL_addvalue(&buf); 1.508 + break; 1.509 + case JSON_TABLETYPE_ARRAY: 1.510 + lua_rawgeti(L, json_export_luacontainer_idx, ++container->pos); 1.511 + lua_replace(L, json_export_value_idx); 1.512 + if (lua_isnil(L, json_export_value_idx)) { 1.513 + luaL_addchar(&buf, ']'); 1.514 + goto json_export_close; 1.515 + } 1.516 + break; 1.517 + json_export_close: 1.518 + if (--level) { 1.519 + lua_rawgeti(L, json_export_stackswap_idx, stackswapidx--); 1.520 + lua_replace(L, json_export_ccontainer_idx); 1.521 + container = lua_touserdata(L, json_export_ccontainer_idx); 1.522 + lua_rawgeti(L, json_export_stackswap_idx, stackswapidx--); 1.523 + lua_replace(L, json_export_luacontainer_idx); 1.524 + } else { 1.525 + // for pretty results, add final newline character if outermost container is processed: 1.526 + if (pretty) luaL_addchar(&buf, '\n'); 1.527 + luaL_pushresult(&buf); 1.528 + return 1; 1.529 } 1.530 } 1.531 - // add closing bracket to output buffer: 1.532 - luaL_addchar(&buf, '}'); 1.533 - // for pretty results, add final newline character if outermost container is processed: 1.534 - if (pretty && level == 0) luaL_addchar(&buf, '\n'); 1.535 - // convert and return buffer: 1.536 - luaL_pushresult(&buf); 1.537 - return 1; 1.538 - // JSON array: 1.539 - case JSON_TABLETYPE_ARRAY: 1.540 - // reserve an extra element on the stack (needed because Lua buffer has unknown size on stack): 1.541 - lua_settop(L, json_export_internal_tmp_idx); 1.542 - // create Lua string buffer: 1.543 - luaL_buffinit(L, &buf); 1.544 - // add opening bracket to output buffer: 1.545 - luaL_addchar(&buf, '['); 1.546 - // iterate through integer keys: 1.547 - for (arrayidx = 1; ; arrayidx++) { 1.548 - // get value in array, and break if nil: 1.549 - lua_rawgeti(L, json_export_internal_value_idx, arrayidx); 1.550 - if (lua_isnil(L, -1)) { 1.551 - lua_pop(L, 1); 1.552 - break; 1.553 - } 1.554 - // store value below Lua string buffer on stack (to allow operation on string buffer): 1.555 - lua_replace(L, json_export_internal_tmp_idx); 1.556 - // 1.557 - // add comma to output buffer unless we process the first element: 1.558 - if (anyelement) luaL_addchar(&buf, ','); 1.559 - // remember that we processed an element: 1.560 - anyelement = 1; 1.561 + /* 1.562 + if (containerkey || container->type == JSON_TABLETYPE_ARRAY) { 1.563 + // add comma where necessary: 1.564 + if (container->pos > 1) luaL_addchar(&buf, ','); 1.565 // handle indentation for pretty results: 1.566 if (pretty) { 1.567 - luaL_addchar(&buf, '\n'); 1.568 - for (i=0; i<level; i++) { 1.569 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.570 - luaL_addvalue(&buf); 1.571 + if (containerkey) { 1.572 + luaL_addchar(&buf, ' '); 1.573 + } else { 1.574 + luaL_addchar(&buf, '\n'); 1.575 + for (i=0; i<level; i++) { 1.576 + lua_pushvalue(L, json_export_indentstring_idx); 1.577 + luaL_addvalue(&buf); 1.578 + } 1.579 } 1.580 } 1.581 - // recursive call to encode previously stored value: 1.582 - lua_pushcfunction(L, json_export_internal); 1.583 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.584 - lua_pushinteger(L, level); 1.585 - lua_pushvalue(L, json_export_internal_tmp_idx); 1.586 - lua_call(L, 3, 1); 1.587 - // add encoded value to output buffer: 1.588 - luaL_addvalue(&buf); 1.589 } 1.590 - // decrement level variable for all following statements: 1.591 - level--; 1.592 - // handle indentation for pretty results: 1.593 - if (pretty && anyelement) { 1.594 - luaL_addchar(&buf, '\n'); 1.595 - for (i=0; i<level; i++) { 1.596 - lua_pushvalue(L, json_export_internal_indentstring_idx); 1.597 - luaL_addvalue(&buf); 1.598 - } 1.599 - } 1.600 - // add closing bracket to output buffer: 1.601 - luaL_addchar(&buf, ']'); 1.602 - // for pretty results, add final newline character if outermost container is processed: 1.603 - if (pretty && level == 0) luaL_addchar(&buf, '\n'); 1.604 - // convert and return buffer: 1.605 + */ 1.606 + } else { 1.607 luaL_pushresult(&buf); 1.608 return 1; 1.609 } 1.610 - // throw error if table type is unknown: 1.611 - json_export_tabletype_error: 1.612 - return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)"); 1.613 } 1.614 - // all other datatypes are considered an error: 1.615 - return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, json_export_internal_value_idx))); 1.616 -} 1.617 - 1.618 -// encodes a JSON document (passed as argument): 1.619 -static int json_export(lua_State *L) { 1.620 - // stack shall contain one function argument: 1.621 - lua_settop(L, 1); 1.622 - // call json_export_internal function with proper arguments: 1.623 - lua_pushcfunction(L, json_export_internal); 1.624 - lua_pushnil(L); 1.625 - lua_pushinteger(L, 0); 1.626 - lua_pushvalue(L, 1); 1.627 - lua_call(L, 3, 1); 1.628 - // return result: 1.629 - return 1; 1.630 -} 1.631 - 1.632 -// encodes a JSON document (passed as first argument) 1.633 -// with indentation (indentation string may be passed as second argument): 1.634 -static int json_pretty(lua_State *L) { 1.635 - // stack shall contain two function arguments: 1.636 - lua_settop(L, 2); 1.637 - // call json_export_internal function with proper arguments: 1.638 - lua_pushcfunction(L, json_export_internal); 1.639 - if (lua_isnil(L, 2)) lua_pushliteral(L, " "); 1.640 - else lua_pushvalue(L, 2); 1.641 - lua_pushinteger(L, 0); 1.642 - lua_pushvalue(L, 1); 1.643 - lua_call(L, 3, 1); 1.644 - // return result: 1.645 - return 1; 1.646 } 1.647 1.648 // functions in library module: 1.649 @@ -1349,7 +1311,6 @@ 1.650 {"array", json_array}, 1.651 {"import", json_import}, 1.652 {"export", json_export}, 1.653 - {"pretty", json_pretty}, 1.654 {"get", json_get}, 1.655 {"type", json_type}, 1.656 {"set", json_set},