webmcp

changeset 193:0014a7c22013

Improved performance of JSON library by storing shadow tables directly via a lightuserdata key instead of using ephemeron tables
author jbe
date Mon Aug 11 13:18:49 2014 +0200 (2014-08-11)
parents 33c8f7029cfa
children 654ddbcc49d0
files libraries/json/json.c
line diff
     1.1 --- a/libraries/json/json.c	Sun Aug 10 20:18:57 2014 +0200
     1.2 +++ b/libraries/json/json.c	Mon Aug 11 13:18:49 2014 +0200
     1.3 @@ -12,27 +12,36 @@
     1.4  //       levels for JSON documents <= 2 GiB.
     1.5  #define JSON_MAXDEPTH (1024*1024*1024)
     1.6  
     1.7 -// generate dummy memory addresses that represents null values:
     1.8 -char json_nullmark;
     1.9 -#define json_isnullmark(L, i) (lua_touserdata((L), (i)) == &json_nullmark)
    1.10 -#define json_pushnullmark(L) lua_pushlightuserdata((L), &json_nullmark)
    1.11 +// define type JSON_LIGHTUSERDATA and
    1.12 +// generate dummy memory addresses for lightuserdata values:
    1.13 +#define JSON_LIGHTUSERDATA char
    1.14 +static struct {
    1.15 +  JSON_LIGHTUSERDATA nullmark;   // lightuserdata value represents a NULL value
    1.16 +  JSON_LIGHTUSERDATA shadowtbl;  // lightuserdata key for shadow table
    1.17 +} json_lightuserdata;
    1.18 +
    1.19 +// macros for special nullmark value:
    1.20 +#define json_isnullmark(L, i) (lua_touserdata((L), (i)) == &json_lightuserdata.nullmark)
    1.21 +#define json_pushnullmark(L) lua_pushlightuserdata((L), &json_lightuserdata.nullmark)
    1.22 +
    1.23 +// macros for getting and setting shadow tables
    1.24 +#define json_setshadow(L, i) lua_rawsetp((L), (i), &json_lightuserdata.shadowtbl)
    1.25 +#define json_getshadow(L, i) lua_rawgetp((L), (i), &json_lightuserdata.shadowtbl)
    1.26 +#define json_createproxy(L) lua_createtable((L), 0, 1)
    1.27 +
    1.28 +// generate additional dummy memory addresses that represent Lua objects
    1.29 +// via lightuserdata keys and LUA_REGISTRYINDEX:
    1.30 +static struct {
    1.31 +  JSON_LIGHTUSERDATA objectmt;  // metatable for JSON objects
    1.32 +  JSON_LIGHTUSERDATA arraymt;   // metatable for JSON arrays
    1.33 +} json_registry;
    1.34  
    1.35  // macros for usage of Lua registry:
    1.36 -#define JSON_REGENT char
    1.37 -#define JSON_REGPOINTER void *
    1.38  #define json_regpointer(x) (&json_registry.x)
    1.39  #define json_regfetchpointer(L, x) lua_rawgetp((L), LUA_REGISTRYINDEX, (x))
    1.40  #define json_regfetch(L, x) json_regfetchpointer(L, json_regpointer(x))
    1.41  #define json_regstore(L, x) lua_rawsetp(L, LUA_REGISTRYINDEX, json_regpointer(x))
    1.42  
    1.43 -// generate dummy memory addresses that represent Lua objects
    1.44 -// via lightuserdata keys and LUA_REGISTRYINDEX:
    1.45 -static struct {
    1.46 -  JSON_REGENT shadowtbl;  // ephemeron table that maps tables to their corresponding shadow table
    1.47 -  JSON_REGENT objectmt;   // metatable for JSON objects
    1.48 -  JSON_REGENT arraymt;    // metatable for JSON arrays
    1.49 -} json_registry;
    1.50 -
    1.51  // returns the string "<JSON null marker>":
    1.52  static int json_nullmark_tostring(lua_State *L) {
    1.53    lua_pushliteral(L, "<JSON null marker>");
    1.54 @@ -53,35 +62,33 @@
    1.55    // determine is argument is given:
    1.56    if (lua_isnoneornil(L, json_convert_source_idx)) {
    1.57      // if no argument is given (or if argument is nil),
    1.58 -    // create table with shadow table, and leave first table on top of stack:
    1.59 -    json_regfetch(L, shadowtbl);
    1.60 +    // create proxy table with shadow table, and leave proxy table on top of stack:
    1.61 +    json_createproxy(L);
    1.62      lua_newtable(L);
    1.63 -    lua_pushvalue(L, -1);
    1.64 -    lua_newtable(L);
    1.65 -    lua_rawset(L, -4);
    1.66 +    json_setshadow(L, -2);
    1.67    } else {
    1.68      // if an argument was given,
    1.69 -    // push its iterator function on stack position 2 if existent,
    1.70 -    // else push null for normal tables:
    1.71 +    // stack shall contain only one function argument:
    1.72      lua_settop(L, 1);
    1.73 -    if (lua_getmetatable(L, json_convert_source_idx)) {
    1.74 -      lua_getfield(L, -1, array ? "__ipairs" : "__pairs");
    1.75 -      if (lua_isnil(L, -1)) luaL_checktype(L, 1, LUA_TTABLE);
    1.76 -      else if (lua_type(L, -1) != LUA_TFUNCTION)
    1.77 +    // check if there is an iterator function in its metatable:
    1.78 +    if (luaL_getmetafield(L, json_convert_source_idx, array ? "__ipairs" : "__pairs")) {
    1.79 +      // if there is an iterator function,
    1.80 +      // leave it on stack position 2 and verify its type:
    1.81 +      if (lua_type(L, json_convert_iterator_idx) != LUA_TFUNCTION)
    1.82          return luaL_error(L, "%s metamethod is not a function", array ? "__ipairs" : "__pairs");
    1.83 -      lua_replace(L, -2);
    1.84      } else {
    1.85 +      // if there is no iterator function,
    1.86 +      // verify the type of the argument itself:
    1.87 +      luaL_checktype(L, json_convert_source_idx, LUA_TTABLE);
    1.88 +      // push nil onto stack position 2:
    1.89        lua_pushnil(L);
    1.90      }
    1.91      // create result table on stack position 3:
    1.92 -    lua_newtable(L);
    1.93 +    json_createproxy(L);
    1.94      // create shadow table on stack position 4:
    1.95 -    json_regfetch(L, shadowtbl);
    1.96      lua_newtable(L);
    1.97 -    lua_pushvalue(L, json_convert_output_idx);
    1.98 -    lua_pushvalue(L, -2);
    1.99 -    lua_rawset(L, -4);
   1.100 -    lua_replace(L, -2);
   1.101 +    lua_pushvalue(L, -1);
   1.102 +    json_setshadow(L, -3);
   1.103      // check if iterator function exists:
   1.104      if (lua_isnil(L, json_convert_iterator_idx)) {
   1.105        // if there is no iterator function,
   1.106 @@ -185,8 +192,7 @@
   1.107  // special Lua stack indicies for json_import function:
   1.108  #define json_import_objectmt_idx 2
   1.109  #define json_import_arraymt_idx 3
   1.110 -#define json_import_shadowtbl_idx 4
   1.111 -#define json_import_stackswap_idx 5
   1.112 +#define json_import_stackswap_idx 4
   1.113  
   1.114  // macros for hex decoding:
   1.115  #define json_utf16_surrogate(x) ((x) >= 0xD800 && (x) <= 0xDFFF)
   1.116 @@ -232,8 +238,6 @@
   1.117    json_regfetch(L, objectmt);
   1.118    // push arraymt onto stack position 3:
   1.119    json_regfetch(L, arraymt);
   1.120 -  // push shadowtbl onto stack position 4:
   1.121 -  json_regfetch(L, shadowtbl);
   1.122    // push table for stack swapping onto stack position 5:
   1.123    // (needed to avoid Lua stack overflows)
   1.124    lua_newtable(L);
   1.125 @@ -307,9 +311,8 @@
   1.126      // create internal shadow table on stack:
   1.127      lua_newtable(L);
   1.128      // register internal shadow table:
   1.129 -    lua_pushvalue(L, -2);
   1.130 -    lua_pushvalue(L, -2);
   1.131 -    lua_rawset(L, json_import_shadowtbl_idx);
   1.132 +    lua_pushvalue(L, -1);
   1.133 +    json_setshadow(L, -3);
   1.134      // distinguish between JSON objects and JSON arrays:
   1.135      if (c == '{') {
   1.136        // if JSON object,
   1.137 @@ -610,38 +613,28 @@
   1.138    return 2;
   1.139  }
   1.140  
   1.141 -// special Lua stack indicies for json_path function:
   1.142 -#define json_path_shadowtbl_idx 1
   1.143 -
   1.144 -// stack offset of arguments to json_path function:
   1.145 -#define json_path_idxshift 1
   1.146 -
   1.147  // gets a value or its type from a JSON document (passed as first argument)
   1.148  // using a path (passed as variable number of keys after the first argument):
   1.149  static int json_path(lua_State *L, int type_mode) {
   1.150 -  int stacktop;                      // stack index of top of stack (after shifting)
   1.151 -  int idx = 2 + json_path_idxshift;  // stack index of current argument to process
   1.152 +  int stacktop;  // number of arguments
   1.153 +  int idx = 2;   // stack index of current argument to process
   1.154    // require at least one argument:
   1.155    luaL_checkany(L, 1);
   1.156 -  // insert shadowtbl into stack at position 1 (shifting the arguments):
   1.157 -  json_regfetch(L, shadowtbl);
   1.158 -  lua_insert(L, 1);
   1.159 -  // store stack index of top of stack:
   1.160 +  // store stack index of top of stack (number of arguments):
   1.161    stacktop = lua_gettop(L);
   1.162    // use first argument as "current value" (stored on top of stack):
   1.163 -  lua_pushvalue(L, 1 + json_path_idxshift);
   1.164 +  lua_pushvalue(L, 1);
   1.165    // process each "path key" (2nd argument and following arguments):
   1.166    while (idx <= stacktop) {
   1.167      // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned:
   1.168      if (lua_isnil(L, -1)) return 1;
   1.169      // try to get shadow table of "current value":
   1.170 -    lua_pushvalue(L, -1);
   1.171 -    lua_rawget(L, json_path_shadowtbl_idx);
   1.172 +    json_getshadow(L, -1);
   1.173      if (lua_isnil(L, -1)) {
   1.174        // if no shadow table is found,
   1.175 -      if (lua_type(L, -1) == LUA_TTABLE) {
   1.176 +      if (lua_type(L, -2) == LUA_TTABLE) {
   1.177          // and if "current value" is a table,
   1.178 -        // drop nil from stack:
   1.179 +        // pop nil from stack:
   1.180          lua_pop(L, 1);
   1.181          // get "next value" using the "path key":
   1.182          lua_pushvalue(L, idx++);
   1.183 @@ -714,30 +707,26 @@
   1.184  }
   1.185  
   1.186  // special Lua stack indicies for json_set function:
   1.187 -#define json_set_shadowtbl_idx 1
   1.188 -#define json_set_objectmt_idx 2
   1.189 -#define json_set_arraymt_idx 3
   1.190 +#define json_set_objectmt_idx 1
   1.191 +#define json_set_arraymt_idx 2
   1.192  
   1.193  // stack offset of arguments to json_set function:
   1.194 -#define json_set_idxshift 3
   1.195 +#define json_set_idxshift 2
   1.196  
   1.197  // sets a value (passed as second argument) in a JSON document (passed as first argument)
   1.198  // using a path (passed as variable number of keys starting at third argument):
   1.199  static int json_set(lua_State *L) {
   1.200 -  int stacktop;   // stack index of top of stack (after shifting)
   1.201 -  int idx = 3;    // stack index of current argument to process
   1.202 +  int stacktop;  // stack index of top of stack (after shifting)
   1.203 +  int idx;       // stack index of current argument to process
   1.204    // require at least two arguments:
   1.205    luaL_checkany(L, 1);
   1.206    luaL_checkany(L, 2);
   1.207 -  // insert shadowtbl into stack at position 1 (shifting the arguments):
   1.208 -  json_regfetch(L, shadowtbl);
   1.209 -  lua_insert(L, 1);
   1.210 -  // insert objectmt into stack at position 2 (shifting the arguments):
   1.211 +  // insert objectmt into stack at position 1 (shifting the arguments):
   1.212    json_regfetch(L, objectmt);
   1.213 +  lua_insert(L, 1);
   1.214 +  // insert arraymt into stack at position 2 (shifting the arguments):
   1.215 +  json_regfetch(L, arraymt);
   1.216    lua_insert(L, 2);
   1.217 -  // insert arraymt into stack at position 3 (shifting the arguments):
   1.218 -  json_regfetch(L, arraymt);
   1.219 -  lua_insert(L, 3);
   1.220    // store stack index of top of stack:
   1.221    stacktop = lua_gettop(L);
   1.222    // use nil as initial "parent value":
   1.223 @@ -767,11 +756,10 @@
   1.224          // throw error if parent element does not exist:
   1.225          if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON object");
   1.226          // push new JSON object as "current value" onto stack:
   1.227 -        lua_newtable(L);
   1.228 +        json_createproxy(L);
   1.229          // create and register shadow table:
   1.230 -        lua_pushvalue(L, -1);
   1.231          lua_newtable(L);
   1.232 -        lua_rawset(L, json_set_shadowtbl_idx);
   1.233 +        json_setshadow(L, -2);
   1.234          // set metatable of JSON object: 
   1.235          lua_pushvalue(L, json_set_objectmt_idx);
   1.236          lua_setmetatable(L, -2);
   1.237 @@ -798,11 +786,10 @@
   1.238          // throw error if parent element does not exist:
   1.239          if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON array");
   1.240          // push new JSON array as "current value" onto stack:
   1.241 -        lua_newtable(L);
   1.242 +        json_createproxy(L);
   1.243          // create and register shadow table:
   1.244 -        lua_pushvalue(L, -1);
   1.245          lua_newtable(L);
   1.246 -        lua_rawset(L, json_set_shadowtbl_idx);
   1.247 +        json_setshadow(L, -2);
   1.248          // set metatable of JSON array: 
   1.249          lua_pushvalue(L, json_set_arraymt_idx);
   1.250          lua_setmetatable(L, -2);
   1.251 @@ -840,12 +827,12 @@
   1.252  static int json_len(lua_State *L) {
   1.253    // stack shall contain one function argument:
   1.254    lua_settop(L, 1);
   1.255 -  // try to get corresponding shadow table for first argument:
   1.256 -  json_regfetch(L, shadowtbl);
   1.257 -  lua_pushvalue(L, 1);
   1.258 -  lua_rawget(L, -2);
   1.259 -  // if shadow table does not exist, return length of argument, else length of shadow table:
   1.260 -  lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
   1.261 +  // push shadow table or nil onto stack:
   1.262 +  json_getshadow(L, 1);
   1.263 +  // pop nil from stack if no shadow table has been found:
   1.264 +  if (lua_isnil(L, -1)) lua_pop(L, 1);
   1.265 +  // return length of argument or shadow table:
   1.266 +  lua_pushnumber(L, lua_rawlen(L, -1));
   1.267    return 1;
   1.268  }
   1.269  
   1.270 @@ -853,17 +840,15 @@
   1.271  static int json_index(lua_State *L) {
   1.272    // stack shall contain two function arguments:
   1.273    lua_settop(L, 2);
   1.274 -  // get corresponding shadow table for first argument:
   1.275 -  json_regfetch(L, shadowtbl);
   1.276 -  lua_pushvalue(L, 1);
   1.277 -  lua_rawget(L, -2);
   1.278 -  // throw error if no shadow table was found:
   1.279 +  // replace first argument with its shadow table
   1.280 +  // or throw error if no shadow table is found:
   1.281 +  json_getshadow(L, 1);
   1.282    if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   1.283 +  lua_replace(L, 1);
   1.284    // use key passed as second argument to lookup value in shadow table:
   1.285 -  lua_pushvalue(L, 2);
   1.286 -  lua_rawget(L, -2);
   1.287 +  lua_rawget(L, 1);
   1.288    // if value is null-marker, then push nil onto stack:
   1.289 -  if (json_isnullmark(L, -1)) lua_pushnil(L);
   1.290 +  if (json_isnullmark(L, 2)) lua_pushnil(L);
   1.291    // return either looked up value, or nil
   1.292    return 1;
   1.293  }
   1.294 @@ -872,16 +857,12 @@
   1.295  static int json_newindex(lua_State *L) {
   1.296    // stack shall contain three function arguments:
   1.297    lua_settop(L, 3);
   1.298 -  // get corresponding shadow table for first argument:
   1.299 -  json_regfetch(L, shadowtbl);
   1.300 -  lua_pushvalue(L, 1);
   1.301 -  lua_rawget(L, -2);
   1.302 -  // throw error if no shadow table was found:
   1.303 +  // replace first argument with its shadow table
   1.304 +  // or throw error if no shadow table is found:
   1.305 +  json_getshadow(L, 1);
   1.306    if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   1.307 -  // replace first argument with shadow table:
   1.308    lua_replace(L, 1);
   1.309 -  // reset stack and use second and third argument to write to shadow table:
   1.310 -  lua_settop(L, 3);
   1.311 +  // second and third argument to write to shadow table:
   1.312    lua_rawset(L, 1);
   1.313    // return nothing:
   1.314    return 0;
   1.315 @@ -891,16 +872,14 @@
   1.316  static int json_pairs_iterfunc(lua_State *L) {
   1.317    // stack shall contain two function arguments:
   1.318    lua_settop(L, 2);
   1.319 -  // get corresponding shadow table for first argument:
   1.320 -  json_regfetch(L, shadowtbl);
   1.321 -  lua_pushvalue(L, 1);
   1.322 -  lua_rawget(L, -2);
   1.323 -  // throw error if no shadow table was found:
   1.324 +  // replace first argument with its shadow table
   1.325 +  // or throw error if no shadow table is found:
   1.326 +  json_getshadow(L, 1);
   1.327    if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   1.328 +  lua_replace(L, 1);
   1.329    // get next key value pair from shadow table (using previous key from argument 2)
   1.330    // and return nothing if there is no next pair:
   1.331 -  lua_pushvalue(L, 2);
   1.332 -  if (!lua_next(L, -2)) return 0;
   1.333 +  if (!lua_next(L, 1)) return 0;
   1.334    // replace null-marker with nil:
   1.335    if (json_isnullmark(L, -1)) {
   1.336      lua_pop(L, 1);
   1.337 @@ -929,22 +908,20 @@
   1.338    lua_settop(L, 2);
   1.339    // calculate new index by incrementing second argument:
   1.340    idx = lua_tointeger(L, 2) + 1;
   1.341 -  // get corresponding shadow table for first argument:
   1.342 -  json_regfetch(L, shadowtbl);
   1.343 -  lua_pushvalue(L, 1);
   1.344 -  lua_rawget(L, -2);
   1.345 -  // throw error if no shadow table was found:
   1.346 +  // push shadow table onto stack position 3
   1.347 +  // or throw error if no shadow table is found:
   1.348 +  json_getshadow(L, 1);
   1.349    if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   1.350 -  // do integer lookup in shadow table:
   1.351 -  lua_rawgeti(L, -1, idx);
   1.352 +  // do integer lookup in shadow table and store result on stack position 4:
   1.353 +  lua_rawgeti(L, 3, idx);
   1.354    // return nothing if there was no value:
   1.355 -  if (lua_isnil(L, -1)) return 0;
   1.356 +  if (lua_isnil(L, 4)) return 0;
   1.357    // return new index and
   1.358    // either the looked up value if it is not equal to the null-marker
   1.359    // or nil instead of null-marker:
   1.360    lua_pushinteger(L, idx);
   1.361 -  if (json_isnullmark(L, -2)) lua_pushnil(L);
   1.362 -  else lua_pushvalue(L, -2);
   1.363 +  if (json_isnullmark(L, 4)) lua_pushnil(L);
   1.364 +  else lua_pushvalue(L, 4);
   1.365    return 2;
   1.366  }
   1.367  
   1.368 @@ -1009,11 +986,10 @@
   1.369  #define json_export_indentstring_idx 2
   1.370  #define json_export_objectmt_idx 3
   1.371  #define json_export_arraymt_idx 4
   1.372 -#define json_export_shadowtbl_idx 5
   1.373 -#define json_export_stackswap_idx 6
   1.374 -#define json_export_luacontainer_idx 7
   1.375 -#define json_export_ccontainer_idx 8
   1.376 -#define json_export_buffer_idx 9
   1.377 +#define json_export_stackswap_idx 5
   1.378 +#define json_export_luacontainer_idx 6
   1.379 +#define json_export_ccontainer_idx 7
   1.380 +#define json_export_buffer_idx 8
   1.381  
   1.382  // encodes a JSON document (passed as first argument)
   1.383  // optionally using indentation (indentation string or true passed as second argument)
   1.384 @@ -1063,11 +1039,9 @@
   1.385    json_regfetch(L, objectmt);
   1.386    // push arraymt onto stack position 4:
   1.387    json_regfetch(L, arraymt);
   1.388 -  // push shadowtbl onto stack position 5:
   1.389 -  json_regfetch(L, shadowtbl);
   1.390 -  // push table for stack swapping onto stack position 6:
   1.391 +  // push table for stack swapping onto stack position 5:
   1.392    lua_newtable(L);
   1.393 -  // create placeholders on stack positions 7 through 8:
   1.394 +  // create placeholders on stack positions 6 through 7:
   1.395    lua_settop(L, json_export_buffer_idx);
   1.396    // create Lua string buffer:
   1.397    luaL_buffinit(L, &buf);
   1.398 @@ -1146,11 +1120,10 @@
   1.399          // reset stack (pop metatable from stack):
   1.400          lua_pop(L, 1);
   1.401        }
   1.402 -      // replace table with its shadow table if existent, and reset stack:
   1.403 -      lua_pushvalue(L, json_export_value_idx);
   1.404 -      lua_rawget(L, json_export_shadowtbl_idx);
   1.405 +      // replace table with its shadow table if existent:
   1.406 +      json_getshadow(L, json_export_value_idx);
   1.407        if (lua_isnil(L, -1)) lua_pop(L, 1);
   1.408 -      else lua_replace(L, json_export_value_idx);
   1.409 +      else lua_replace(L, json_export_value_idx); 
   1.410        // check if type of table is still undetermined
   1.411        // and optionally calculate number of string keys (keycount)
   1.412        // or set keycount to zero:
   1.413 @@ -1395,15 +1368,6 @@
   1.414    lua_newtable(L);
   1.415    luaL_setfuncs(L, json_metatable_functions, 0);
   1.416    json_regstore(L, arraymt);
   1.417 -  // create and store ephemeron table to store shadow tables for each JSON object/array
   1.418 -  // to allow NULL values returned as nil
   1.419 -  lua_newtable(L);
   1.420 -  lua_newtable(L);  // metatable for ephemeron table
   1.421 -  lua_pushliteral(L, "__mode");
   1.422 -  lua_pushliteral(L, "k");
   1.423 -  lua_rawset(L, -3);
   1.424 -  lua_setmetatable(L, -2);
   1.425 -  json_regstore(L, shadowtbl);
   1.426    // set metatable of null marker and make it available through library module:
   1.427    json_pushnullmark(L);
   1.428    lua_newtable(L);

Impressum / About Us