HomeServer/lualib-src/lua-datasheet.c
2024-11-20 15:41:37 +08:00

373 lines
8.1 KiB
C

#include <lua.h>
#include <lauxlib.h>
#include <stdint.h>
#define NODECACHE "_ctable"
#define PROXYCACHE "_proxy"
#define TABLES "_ctables"
#define VALUE_NIL 0
#define VALUE_INTEGER 1
#define VALUE_REAL 2
#define VALUE_BOOLEAN 3
#define VALUE_TABLE 4
#define VALUE_STRING 5
#define VALUE_INVALID 6
#define INVALID_OFFSET 0xffffffff
struct proxy {
const char * data;
int index;
};
struct document {
uint32_t strtbl;
uint32_t n;
uint32_t index[1];
// table[n]
// strings
};
struct table {
uint32_t array;
uint32_t dict;
uint8_t type[1];
// value[array]
// kvpair[dict]
};
static inline const struct table *
gettable(const struct document *doc, int index) {
if (doc->index[index] == INVALID_OFFSET) {
return NULL;
}
return (const struct table *)((const char *)doc + sizeof(uint32_t) + sizeof(uint32_t) + doc->n * sizeof(uint32_t) + doc->index[index]);
}
static void
create_proxy(lua_State *L, const void *data, int index) {
const struct table * t = gettable(data, index);
if (t == NULL) {
luaL_error(L, "Invalid index %d", index);
}
lua_getfield(L, LUA_REGISTRYINDEX, NODECACHE);
if (lua_rawgetp(L, -1, t) == LUA_TTABLE) {
lua_replace(L, -2);
return;
}
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, lua_upvalueindex(1));
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
// NODECACHE, table, table
lua_rawsetp(L, -3, t);
// NODECACHE, table
lua_getfield(L, LUA_REGISTRYINDEX, PROXYCACHE);
// NODECACHE, table, PROXYCACHE
lua_pushvalue(L, -2);
// NODECACHE, table, PROXYCACHE, table
struct proxy * p = lua_newuserdatauv(L, sizeof(struct proxy), 0);
// NODECACHE, table, PROXYCACHE, table, proxy
p->data = data;
p->index = index;
lua_rawset(L, -3);
// NODECACHE, table, PROXYCACHE
lua_pop(L, 1);
// NODECACHE, table
lua_replace(L, -2);
// table
}
static void
clear_table(lua_State *L) {
int t = lua_gettop(L); // clear top table
if (lua_type(L, t) != LUA_TTABLE) {
luaL_error(L, "Invalid cache");
}
lua_pushnil(L);
while (lua_next(L, t) != 0) {
// key value
lua_pop(L, 1);
lua_pushvalue(L, -1);
lua_pushnil(L);
// key key nil
lua_rawset(L, t);
// key
}
}
static void
update_cache(lua_State *L, const void *data, const void * newdata) {
lua_getfield(L, LUA_REGISTRYINDEX, NODECACHE);
int t = lua_gettop(L);
lua_getfield(L, LUA_REGISTRYINDEX, PROXYCACHE);
int pt = t + 1;
lua_newtable(L); // temp table
int nt = pt + 1;
lua_pushnil(L);
while (lua_next(L, t) != 0) {
// pointer (-2) -> table (-1)
lua_pushvalue(L, -1);
if (lua_rawget(L, pt) == LUA_TUSERDATA) {
// pointer, table, proxy
struct proxy * p = lua_touserdata(L, -1);
if (p->data == data) {
// update to newdata
p->data = newdata;
const struct table * newt = gettable(newdata, p->index);
lua_pop(L, 1);
// pointer, table
clear_table(L);
lua_pushvalue(L, lua_upvalueindex(1));
// pointer, table, meta
lua_setmetatable(L, -2);
// pointer, table
if (newt) {
lua_rawsetp(L, nt, newt);
} else {
lua_pop(L, 1);
}
// pointer
lua_pushvalue(L, -1);
lua_pushnil(L);
lua_rawset(L, t);
} else {
lua_pop(L, 2);
}
} else {
lua_pop(L, 2);
// pointer
}
}
// copy nt to t
lua_pushnil(L);
while (lua_next(L, nt) != 0) {
lua_pushvalue(L, -2);
lua_insert(L, -2);
// key key value
lua_rawset(L, t);
}
// NODECACHE PROXYCACHE TEMP
lua_pop(L, 3);
}
static int
lupdate(lua_State *L) {
lua_getfield(L, LUA_REGISTRYINDEX, PROXYCACHE);
lua_pushvalue(L, 1);
// PROXYCACHE, table
if (lua_rawget(L, -2) != LUA_TUSERDATA) {
luaL_error(L, "Invalid proxy table %p", lua_topointer(L, 1));
}
struct proxy * p = lua_touserdata(L, -1);
luaL_checktype(L, 2, LUA_TLIGHTUSERDATA);
const char * newdata = lua_touserdata(L, 2);
update_cache(L, p->data, newdata);
return 1;
}
static inline uint32_t
getuint32(const void *v) {
union {
uint32_t d;
uint8_t t[4];
} test = { 1 };
if (test.t[0] == 0) {
// big endian
test.d = *(const uint32_t *)v;
return test.t[0] | test.t[1] << 4 | test.t[2] << 8 | test.t[3] << 12;
} else {
return *(const uint32_t *)v;
}
}
static inline float
getfloat(const void *v) {
union {
uint32_t d;
float f;
uint8_t t[4];
} test = { 1 };
if (test.t[0] == 0) {
// big endian
test.d = *(const uint32_t *)v;
test.d = test.t[0] | test.t[1] << 4 | test.t[2] << 8 | test.t[3] << 12;
return test.f;
} else {
return *(const float *)v;
}
}
static void
pushvalue(lua_State *L, const void *v, int type, const struct document * doc) {
switch (type) {
case VALUE_NIL:
lua_pushnil(L);
break;
case VALUE_INTEGER:
lua_pushinteger(L, (int32_t)getuint32(v));
break;
case VALUE_REAL:
lua_pushnumber(L, getfloat(v));
break;
case VALUE_BOOLEAN:
lua_pushboolean(L, getuint32(v));
break;
case VALUE_TABLE:
create_proxy(L, doc, getuint32(v));
break;
case VALUE_STRING:
lua_pushstring(L, (const char *)doc + doc->strtbl + getuint32(v));
break;
default:
luaL_error(L, "Invalid type %d at %p", type, v);
}
}
static void
copytable(lua_State *L, int tbl, struct proxy *p) {
const struct document * doc = (const struct document *)p->data;
if (p->index < 0 || p->index >= doc->n) {
luaL_error(L, "Invalid proxy (index = %d, total = %d)", p->index, (int)doc->n);
}
const struct table * t = gettable(doc, p->index);
if (t == NULL) {
luaL_error(L, "Invalid proxy (index = %d)", p->index);
}
const uint32_t * v = (const uint32_t *)((const char *)t + sizeof(uint32_t) + sizeof(uint32_t) + ((t->array + t->dict + 3) & ~3));
int i;
for (i=0;i<t->array;i++) {
pushvalue(L, v++, t->type[i], doc);
lua_rawseti(L, tbl, i+1);
}
for (i=0;i<t->dict;i++) {
pushvalue(L, v++, VALUE_STRING, doc);
pushvalue(L, v++, t->type[t->array+i], doc);
lua_rawset(L, tbl);
}
}
static int
lnew(lua_State *L) {
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
const char * data = lua_touserdata(L, 1);
// hold ref to data
lua_getfield(L, LUA_REGISTRYINDEX, TABLES);
lua_pushvalue(L, 1);
lua_rawsetp(L, -2, data);
create_proxy(L, data, 0);
return 1;
}
static void
copyfromdata(lua_State *L) {
lua_getfield(L, LUA_REGISTRYINDEX, PROXYCACHE);
lua_pushvalue(L, 1);
// PROXYCACHE, table
if (lua_rawget(L, -2) != LUA_TUSERDATA) {
luaL_error(L, "Invalid proxy table %p", lua_topointer(L, 1));
}
struct proxy * p = lua_touserdata(L, -1);
lua_pop(L, 2);
copytable(L, 1, p);
lua_pushnil(L);
lua_setmetatable(L, 1); // remove metatable
}
static int
lindex(lua_State *L) {
copyfromdata(L);
lua_rawget(L, 1);
return 1;
}
static int
lnext(lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, 1))
return 2;
else {
lua_pushnil(L);
return 1;
}
}
static int
lpairs(lua_State *L) {
copyfromdata(L);
lua_pushcfunction(L, lnext);
lua_pushvalue(L, 1);
lua_pushnil(L);
return 3;
}
static int
llen(lua_State *L) {
copyfromdata(L);
lua_pushinteger(L, lua_rawlen(L, 1));
return 1;
}
static void
new_weak_table(lua_State *L, const char *mode) {
lua_newtable(L); // NODECACHE { pointer:table }
lua_createtable(L, 0, 1); // weak meta table
lua_pushstring(L, mode);
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2); // make NODECACHE weak
}
static void
gen_metatable(lua_State *L) {
new_weak_table(L, "kv"); // NODECACHE { pointer:table }
lua_setfield(L, LUA_REGISTRYINDEX, NODECACHE);
new_weak_table(L, "k"); // PROXYCACHE { table:userdata }
lua_setfield(L, LUA_REGISTRYINDEX, PROXYCACHE);
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, TABLES);
lua_createtable(L, 0, 1); // mod table
lua_createtable(L, 0, 2); // metatable
luaL_Reg l[] = {
{ "__index", lindex },
{ "__pairs", lpairs },
{ "__len", llen },
{ NULL, NULL },
};
lua_pushvalue(L, -1);
luaL_setfuncs(L, l, 1);
}
static int
lstringpointer(lua_State *L) {
const char * str = luaL_checkstring(L, 1);
lua_pushlightuserdata(L, (void *)str);
return 1;
}
LUAMOD_API int
luaopen_skynet_datasheet_core(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{ "new", lnew },
{ "update", lupdate },
{ NULL, NULL },
};
luaL_newlibtable(L,l);
gen_metatable(L);
luaL_setfuncs(L, l, 1);
lua_pushcfunction(L, lstringpointer);
lua_setfield(L, -2, "stringpointer");
return 1;
}