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},

Impressum / About Us