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  }

Impressum / About Us