diff options
| author | Alexander Gavrilov | 2012-04-01 16:43:40 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-04-01 16:43:40 +0400 |
| commit | afe4eba957dadf219ff4b4d2a3aa89b773f44fdd (patch) | |
| tree | 62a5df09f82e5cf8dc9843525710a2399bb8e3f5 /library/LuaTools.cpp | |
| parent | a9a6fbd8b597203083eb30e408d3982b01e611b8 (diff) | |
| download | dfhack-afe4eba957dadf219ff4b4d2a3aa89b773f44fdd.tar.gz dfhack-afe4eba957dadf219ff4b4d2a3aa89b773f44fdd.tar.bz2 dfhack-afe4eba957dadf219ff4b4d2a3aa89b773f44fdd.tar.xz | |
Improve performance of the persistent data api, and wrap it for lua.
Use an stl table for string keys to avoid linear cost of lookup.
This uncovered a bug in the new luaL_getsubtable function.
Diffstat (limited to 'library/LuaTools.cpp')
| -rw-r--r-- | library/LuaTools.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/library/LuaTools.cpp b/library/LuaTools.cpp index 68cd8aa3..8b3fa037 100644 --- a/library/LuaTools.cpp +++ b/library/LuaTools.cpp @@ -36,6 +36,8 @@ distribution. #include "DataDefs.h" #include "DataIdentity.h" +#include "modules/World.h" + #include "LuaWrapper.h" #include "LuaTools.h" @@ -339,6 +341,213 @@ static const luaL_Reg dfhack_funcs[] = { { NULL, NULL } }; +/* + * Per-world persistent configuration storage. + */ + +static PersistentDataItem persistent_by_struct(lua_State *state, int idx) +{ + lua_getfield(state, idx, "entry_id"); + int id = lua_tointeger(state, -1); + lua_pop(state, 1); + + PersistentDataItem ref = Core::getInstance().getWorld()->GetPersistentData(id); + + if (ref.isValid()) + { + lua_getfield(state, idx, "key"); + const char *str = lua_tostring(state, -1); + if (!str || str != ref.key()) + luaL_argerror(state, idx, "inconsistent id and key"); + lua_pop(state, 1); + } + + return ref; +} + +static int read_persistent(lua_State *state, PersistentDataItem ref, bool create) +{ + if (!ref.isValid()) + { + lua_pushnil(state); + lua_pushstring(state, "entry not found"); + return 2; + } + + if (create) + lua_createtable(state, 0, 4); + + lua_pushvalue(state, lua_upvalueindex(1)); + lua_setmetatable(state, -2); + + lua_pushinteger(state, ref.entry_id()); + lua_setfield(state, -2, "entry_id"); + lua_pushstring(state, ref.key().c_str()); + lua_setfield(state, -2, "key"); + lua_pushstring(state, ref.val().c_str()); + lua_setfield(state, -2, "value"); + + lua_createtable(state, PersistentDataItem::NumInts, 0); + for (int i = 0; i < PersistentDataItem::NumInts; i++) + { + lua_pushinteger(state, ref.ival(i)); + lua_rawseti(state, -2, i+1); + } + lua_setfield(state, -2, "ints"); + + return 1; +} + +static PersistentDataItem get_persistent(lua_State *state) +{ + luaL_checkany(state, 1); + + if (lua_istable(state, 1)) + { + if (!lua_getmetatable(state, 1) || + !lua_rawequal(state, -1, lua_upvalueindex(1))) + luaL_argerror(state, 1, "invalid table type"); + + lua_settop(state, 1); + + return persistent_by_struct(state, 1); + } + else + { + const char *str = luaL_checkstring(state, 1); + + return Core::getInstance().getWorld()->GetPersistentData(str); + } +} + +static int dfhack_persistent_get(lua_State *state) +{ + auto ref = get_persistent(state); + + return read_persistent(state, ref, !lua_istable(state, 1)); +} + +static int dfhack_persistent_delete(lua_State *state) +{ + auto ref = get_persistent(state); + + bool ok = Core::getInstance().getWorld()->DeletePersistentData(ref); + + lua_pushboolean(state, ok); + return 1; +} + +static int dfhack_persistent_get_all(lua_State *state) +{ + const char *str = luaL_checkstring(state, 1); + bool prefix = (lua_gettop(state)>=2 ? lua_toboolean(state,2) : false); + + std::vector<PersistentDataItem> data; + Core::getInstance().getWorld()->GetPersistentData(&data, str, prefix); + + if (data.empty()) + { + lua_pushnil(state); + } + else + { + lua_createtable(state, data.size(), 0); + for (size_t i = 0; i < data.size(); ++i) + { + read_persistent(state, data[i], true); + lua_rawseti(state, -2, i+1); + } + } + + return 1; +} + +static int dfhack_persistent_save(lua_State *state) +{ + lua_settop(state, 2); + luaL_checktype(state, 1, LUA_TTABLE); + bool add = lua_toboolean(state, 2); + + lua_getfield(state, 1, "key"); + const char *str = lua_tostring(state, -1); + if (!str) + luaL_argerror(state, 1, "no key field"); + + lua_settop(state, 1); + + PersistentDataItem ref; + bool added = false; + + if (add) + { + ref = Core::getInstance().getWorld()->AddPersistentData(str); + added = true; + } + else if (lua_getmetatable(state, 1)) + { + if (!lua_rawequal(state, -1, lua_upvalueindex(1))) + return luaL_argerror(state, 1, "invalid table type"); + lua_pop(state, 1); + + ref = persistent_by_struct(state, 1); + } + else + { + ref = Core::getInstance().getWorld()->GetPersistentData(str); + } + + if (!ref.isValid()) + { + ref = Core::getInstance().getWorld()->AddPersistentData(str); + if (!ref.isValid()) + luaL_error(state, "cannot create persistent entry"); + added = true; + } + + lua_getfield(state, 1, "value"); + if (const char *str = lua_tostring(state, -1)) + ref.val() = str; + lua_pop(state, 1); + + lua_getfield(state, 1, "ints"); + if (lua_istable(state, -1)) + { + for (int i = 0; i < PersistentDataItem::NumInts; i++) + { + lua_rawgeti(state, -1, i+1); + if (lua_isnumber(state, -1)) + ref.ival(i) = lua_tointeger(state, -1); + lua_pop(state, 1); + } + } + lua_pop(state, 1); + + read_persistent(state, ref, false); + lua_pushboolean(state, added); + return 2; +} + +static const luaL_Reg dfhack_persistent_funcs[] = { + { "get", dfhack_persistent_get }, + { "delete", dfhack_persistent_delete }, + { "get_all", dfhack_persistent_get_all }, + { "save", dfhack_persistent_save }, + { NULL, NULL } +}; + +static void OpenPersistent(lua_State *state) +{ + luaL_getsubtable(state, lua_gettop(state), "persistent"); + + lua_dup(state); + luaL_setfuncs(state, dfhack_persistent_funcs, 1); + + lua_dup(state); + lua_setfield(state, -2, "__index"); + + lua_pop(state, 1); +} + lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) { if (!state) @@ -354,6 +563,9 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state) // Create and initialize the dfhack global lua_newtable(state); luaL_setfuncs(state, dfhack_funcs, 0); + + OpenPersistent(state); + lua_setglobal(state, "dfhack"); // load dfhack.lua |
