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);