webmcp
changeset 130:209f8ce39c5a
Refactored JSON library to use shadow tables with null markers
author | jbe |
---|---|
date | Sun Jul 27 21:25:26 2014 +0200 (2014-07-27) |
parents | 453d8f8fbace |
children | dbe5881e4ecd |
files | libraries/json/json.c |
line diff
1.1 --- a/libraries/json/json.c Sun Jul 27 15:03:21 2014 +0200 1.2 +++ b/libraries/json/json.c Sun Jul 27 21:25:26 2014 +0200 1.3 @@ -3,9 +3,10 @@ 1.4 #include <stdlib.h> 1.5 #include <string.h> 1.6 1.7 -#define JSON_UPVAL_TYPES lua_upvalueindex(1) 1.8 -#define JSON_UPVAL_NULLS lua_upvalueindex(2) 1.9 -#define JSON_UPVAL_METATABLE lua_upvalueindex(3) 1.10 +#define JSON_UPVAL_NULLMARK lua_upvalueindex(1) 1.11 +#define JSON_UPVAL_SHADOWTBL lua_upvalueindex(2) 1.12 +#define JSON_UPVAL_TYPES lua_upvalueindex(3) 1.13 +#define JSON_UPVAL_METATABLE lua_upvalueindex(4) 1.14 1.15 #define JSON_STATE_VALUE 0 1.16 #define JSON_STATE_OBJECT_KEY 1 1.17 @@ -26,7 +27,6 @@ 1.18 luaL_Buffer luabuf; 1.19 char *cbuf; 1.20 size_t writepos; 1.21 - int aryidx; 1.22 lua_settop(L, 1); 1.23 str = lua_tostring(L, 1); 1.24 total = strlen(str); 1.25 @@ -44,16 +44,16 @@ 1.26 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) 1.27 goto json_import_syntax_error; 1.28 pos++; 1.29 - lua_newtable(L); // the actual JSON object 1.30 + lua_newtable(L); // the external JSON object representation 1.31 lua_pushvalue(L, JSON_UPVAL_METATABLE); 1.32 lua_setmetatable(L, -2); 1.33 lua_pushvalue(L, -1); 1.34 lua_pushliteral(L, "object"); 1.35 lua_rawset(L, JSON_UPVAL_TYPES); 1.36 - lua_newtable(L); // stores set of NULL values 1.37 + lua_newtable(L); // the internal shadow table 1.38 lua_pushvalue(L, -2); 1.39 lua_pushvalue(L, -2); 1.40 - lua_rawset(L, JSON_UPVAL_NULLS); 1.41 + lua_rawset(L, JSON_UPVAL_SHADOWTBL); 1.42 mode = JSON_STATE_OBJECT_KEY; 1.43 level++; 1.44 goto json_import_loop; 1.45 @@ -61,17 +61,17 @@ 1.46 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE) 1.47 goto json_import_syntax_error; 1.48 pos++; 1.49 - lua_newtable(L); // the actual JSON array 1.50 + lua_newtable(L); // the external JSON array representation 1.51 lua_pushvalue(L, JSON_UPVAL_METATABLE); 1.52 lua_setmetatable(L, -2); 1.53 lua_pushvalue(L, -1); 1.54 lua_pushliteral(L, "array"); 1.55 lua_rawset(L, JSON_UPVAL_TYPES); 1.56 - lua_newtable(L); // stores set of NULL values 1.57 + lua_newtable(L); // the internal shadow table 1.58 lua_pushvalue(L, -2); 1.59 lua_pushvalue(L, -2); 1.60 - lua_rawset(L, JSON_UPVAL_NULLS); 1.61 - lua_pushinteger(L, 0); // length of array (since it may contain nil's) 1.62 + lua_rawset(L, JSON_UPVAL_SHADOWTBL); 1.63 + lua_pushinteger(L, 0); // magic integer to indicate an array 1.64 mode = JSON_STATE_ARRAY_VALUE; 1.65 level++; 1.66 goto json_import_loop; 1.67 @@ -82,10 +82,10 @@ 1.68 case ']': 1.69 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR) 1.70 goto json_import_syntax_error; 1.71 - lua_pop(L, 1); // pop length information 1.72 + lua_pop(L, 1); // pop magic integer 1.73 json_import_close: 1.74 pos++; 1.75 - lua_pop(L, 1); // pop table that stores set of NULL values 1.76 + lua_pop(L, 1); // pop shadow table 1.77 if (--level) { 1.78 if (lua_type(L, -2) == LUA_TNUMBER) { 1.79 mode = JSON_STATE_ARRAY_VALUE; 1.80 @@ -180,7 +180,7 @@ 1.81 lua_pushboolean(L, 0); 1.82 pos += 5; 1.83 } else if (!strncmp(str+pos, "null", 4)) { 1.84 - lua_pushnil(L); 1.85 + lua_pushvalue(L, JSON_UPVAL_NULLMARK); 1.86 pos += 4; 1.87 } else { 1.88 goto json_import_syntax_error; 1.89 @@ -192,24 +192,11 @@ 1.90 mode = JSON_STATE_OBJECT_KEY_TERMINATOR; 1.91 goto json_import_loop; 1.92 case JSON_STATE_OBJECT_VALUE: 1.93 - if (lua_isnil(L, -1)) { 1.94 - lua_pushvalue(L, -2); 1.95 - lua_pushboolean(L, 1); 1.96 - lua_rawset(L, -5); 1.97 - } 1.98 - lua_rawset(L, -4); 1.99 + lua_rawset(L, -3); 1.100 mode = JSON_STATE_OBJECT_SEPARATOR; 1.101 goto json_import_loop; 1.102 case JSON_STATE_ARRAY_VALUE: 1.103 - aryidx = lua_tointeger(L, -2) + 1; 1.104 - if (lua_isnil(L, -1)) { 1.105 - lua_pushinteger(L, aryidx); 1.106 - lua_pushboolean(L, 1); 1.107 - lua_rawset(L, -5); 1.108 - } 1.109 - lua_rawseti(L, -4, aryidx); 1.110 - lua_pop(L, 1); 1.111 - lua_pushinteger(L, aryidx); 1.112 + lua_rawseti(L, -3, lua_rawlen(L, -3) + 1); 1.113 mode = JSON_STATE_ARRAY_SEPARATOR; 1.114 goto json_import_loop; 1.115 case JSON_STATE_VALUE: 1.116 @@ -222,119 +209,135 @@ 1.117 return 2; 1.118 } 1.119 1.120 -static int json_arylen(lua_State *L) { 1.121 - int lower, middle; 1.122 - int upper = 1; 1.123 - lua_settop(L, 1); 1.124 +#define JSON_PATH_GET 1 1.125 +#define JSON_PATH_TYPE 2 1.126 +#define JSON_PATH_ISNULL 3 1.127 + 1.128 +static int json_path(lua_State *L, int mode) { 1.129 + int argc; 1.130 + int idx = 2; 1.131 + argc = lua_gettop(L); 1.132 lua_pushvalue(L, 1); 1.133 - lua_rawget(L, JSON_UPVAL_NULLS); 1.134 - if (lua_isnil(L, 2)) goto json_arylen_default; 1.135 - lua_pushnil(L); 1.136 - if (!lua_next(L, 2)) goto json_arylen_default; 1.137 - lua_settop(L, 2); 1.138 - while (1) { 1.139 - lua_rawgeti(L, 1, upper); 1.140 + while (idx <= argc) { 1.141 + lua_pushvalue(L, -1); 1.142 + lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.143 if (lua_isnil(L, -1)) { 1.144 lua_pop(L, 1); 1.145 - lua_pushinteger(L, upper); 1.146 - lua_rawget(L, 2); 1.147 + if (lua_type(L, -1) == LUA_TTABLE) { 1.148 + lua_pushvalue(L, idx++); 1.149 + lua_gettable(L, -2); 1.150 + } else { 1.151 + lua_pushnil(L); 1.152 + } 1.153 + } else { 1.154 + lua_replace(L, -2); 1.155 + lua_pushvalue(L, idx++); 1.156 + lua_rawget(L, -2); 1.157 } 1.158 - if (lua_isnil(L, -1)) break; 1.159 - lua_pop(L, 1); 1.160 - upper *= 2; 1.161 - // TODO: avoid integer overflow! 1.162 + if (lua_isnil(L, -1)) { 1.163 + if (mode == JSON_PATH_ISNULL) lua_pushboolean(L, 0); 1.164 + return 1; 1.165 + } 1.166 + lua_replace(L, -2); 1.167 } 1.168 - lua_pop(L, 1); 1.169 - lower = upper / 2; 1.170 - while (upper - lower > 1) { 1.171 - middle = (lower + upper) / 2; 1.172 - lua_rawgeti(L, 1, middle); 1.173 - if (lua_isnil(L, -1)) { 1.174 - lua_pop(L, 1); 1.175 - lua_pushinteger(L, middle); 1.176 - lua_rawget(L, 2); 1.177 + switch (mode) { 1.178 + case JSON_PATH_GET: 1.179 + if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); 1.180 + return 1; 1.181 + case JSON_PATH_TYPE: 1.182 + if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { 1.183 + lua_pushliteral(L, "null"); 1.184 + return 1; 1.185 } 1.186 - if (lua_isnil(L, -1)) upper = middle; 1.187 - else lower = middle; 1.188 - lua_pop(L, 1); 1.189 + lua_pushvalue(L, -1); 1.190 + lua_rawget(L, JSON_UPVAL_TYPES); 1.191 + if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2))); 1.192 + return 1; 1.193 + case JSON_PATH_ISNULL: 1.194 + lua_pushboolean(L, lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)); 1.195 + return 1; 1.196 } 1.197 - lua_pushinteger(L, lower); 1.198 - return 1; 1.199 -json_arylen_default: 1.200 - lua_pushinteger(L, lua_rawlen(L, 1)); 1.201 + return 0; 1.202 +} 1.203 + 1.204 +static int json_get(lua_State *L) { 1.205 + return json_path(L, JSON_PATH_GET); 1.206 +} 1.207 + 1.208 +static int json_type(lua_State *L) { 1.209 + return json_path(L, JSON_PATH_TYPE); 1.210 +} 1.211 + 1.212 +static int json_isnull(lua_State *L) { 1.213 + return json_path(L, JSON_PATH_ISNULL); 1.214 +} 1.215 + 1.216 +static int json_len(lua_State *L) { 1.217 + lua_settop(L, 1); 1.218 + lua_pushvalue(L, 1); 1.219 + lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.220 + if (lua_isnil(L, -1)) lua_pop(L, 1); 1.221 + lua_pushinteger(L, lua_rawlen(L, -1)); 1.222 return 1; 1.223 } 1.224 1.225 -static int json_type(lua_State *L) { 1.226 - if (lua_gettop(L) >= 2) { 1.227 - lua_pushvalue(L, 1); 1.228 - lua_rawget(L, JSON_UPVAL_NULLS); 1.229 - lua_pushvalue(L, 2); 1.230 - lua_rawget(L, -2); 1.231 - if (lua_toboolean(L, -1)) { 1.232 - lua_getmetatable(L, 1); 1.233 - if (lua_rawequal(L, -1, JSON_UPVAL_METATABLE)) { 1.234 - lua_pushliteral(L, "null"); 1.235 - return 1; 1.236 - } 1.237 - } 1.238 - lua_settop(L, 2); 1.239 - lua_rawget(L, 1); 1.240 - } else { 1.241 - lua_settop(L, 1); 1.242 - } 1.243 - lua_pushvalue(L, -1); 1.244 - lua_rawget(L, JSON_UPVAL_TYPES); 1.245 - if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2))); 1.246 +static int json_index(lua_State *L) { 1.247 + lua_settop(L, 2); 1.248 + lua_pushvalue(L, 1); 1.249 + lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.250 + if (lua_isnil(L, -1)) return 1; 1.251 + lua_pushvalue(L, 2); 1.252 + lua_rawget(L, -2); 1.253 + if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); 1.254 return 1; 1.255 } 1.256 1.257 -static int json_isnull(lua_State *L) { 1.258 - lua_settop(L, 2); 1.259 - lua_getmetatable(L, 1); 1.260 - if (!lua_rawequal(L, -1, JSON_UPVAL_METATABLE)) goto json_isnull_false; 1.261 +static int json_newindex(lua_State *L) { 1.262 + lua_settop(L, 3); 1.263 lua_pushvalue(L, 1); 1.264 - lua_rawget(L, JSON_UPVAL_NULLS); 1.265 - if (lua_isnil(L, -1)) goto json_isnull_false; 1.266 - lua_pushvalue(L, 2); 1.267 - lua_rawget(L, -2); 1.268 - if (!lua_isnil(L, -1)) return 1; 1.269 -json_isnull_false: 1.270 - lua_pushboolean(L, 0); 1.271 + lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.272 + if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); 1.273 + lua_replace(L, 1); 1.274 + lua_rawset(L, 1); 1.275 return 1; 1.276 } 1.277 1.278 static const struct luaL_Reg json_module_functions[] = { 1.279 {"import", json_import}, 1.280 + {"get", json_get}, 1.281 {"type", json_type}, 1.282 {"isnull", json_isnull}, 1.283 {NULL, NULL} 1.284 }; 1.285 1.286 static const struct luaL_Reg json_metatable_functions[] = { 1.287 - {"__len", json_arylen}, 1.288 + {"__len", json_len}, 1.289 + {"__index", json_index}, 1.290 + {"__newindex", json_newindex}, 1.291 {NULL, NULL} 1.292 }; 1.293 1.294 int luaopen_json(lua_State *L) { 1.295 lua_settop(L, 0); 1.296 lua_newtable(L); // 1: library table on stack position 1.297 - lua_newtable(L); // 2: ephemeron table to store the type of the JSON object/array 1.298 - lua_newtable(L); // 3: ephemeron table to store a set of keys associated with JSON-null values 1.299 - lua_newtable(L); // 4: metatable for ephemeron tables 1.300 + lua_newtable(L); // 2: table used as JSON NULL value in internal shadow tables 1.301 + lua_newtable(L); // 3: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil 1.302 + lua_newtable(L); // 4: ephemeron table to store the type of the JSON object/array 1.303 + lua_newtable(L); // 5: metatable for ephemeron tables 1.304 lua_pushliteral(L, "__mode"); 1.305 lua_pushliteral(L, "k"); 1.306 - lua_rawset(L, 4); 1.307 - lua_pushvalue(L, 4); // 5: cloned metatable reference 1.308 - lua_setmetatable(L, 2); 1.309 + lua_rawset(L, 5); 1.310 + lua_pushvalue(L, 5); // 6: cloned metatable reference 1.311 lua_setmetatable(L, 3); 1.312 - lua_newtable(L); // 4: metatable for JSON objects and JSON arrays 1.313 - lua_pushvalue(L, 4); 1.314 + lua_setmetatable(L, 4); 1.315 + lua_newtable(L); // 5: metatable for JSON objects and JSON arrays 1.316 + lua_pushvalue(L, 5); 1.317 lua_setfield(L, 1, "metatable"); 1.318 lua_pushvalue(L, 2); 1.319 lua_pushvalue(L, 3); 1.320 lua_pushvalue(L, 4); 1.321 - luaL_setfuncs(L, json_metatable_functions, 3); 1.322 - luaL_setfuncs(L, json_module_functions, 3); 1.323 + lua_pushvalue(L, 5); 1.324 + luaL_setfuncs(L, json_metatable_functions, 4); 1.325 + luaL_setfuncs(L, json_module_functions, 4); 1.326 return 1; 1.327 }