summaryrefslogtreecommitdiff
path: root/library/LuaTools.cpp
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-04-01 16:43:40 +0400
committerAlexander Gavrilov2012-04-01 16:43:40 +0400
commitafe4eba957dadf219ff4b4d2a3aa89b773f44fdd (patch)
tree62a5df09f82e5cf8dc9843525710a2399bb8e3f5 /library/LuaTools.cpp
parenta9a6fbd8b597203083eb30e408d3982b01e611b8 (diff)
downloaddfhack-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.cpp212
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