From 82f1f69c25485723e5c1851016219e22246b8d0b Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Tue, 24 Aug 2021 10:59:19 -0400 Subject: [PATCH] Start breaking world.cpp into multiple files --- luprex/core/Makefile | 3 +- luprex/core/cpp/tablecmp.hpp | 29 -- .../cpp/{tablecmp.cpp => world-difftab.cpp} | 204 +++++++---- luprex/core/cpp/world-pairtab.cpp | 271 +++++++++++++++ luprex/core/cpp/world.cpp | 328 ------------------ luprex/core/cpp/world.hpp | 25 +- 6 files changed, 426 insertions(+), 434 deletions(-) delete mode 100644 luprex/core/cpp/tablecmp.hpp rename luprex/core/cpp/{tablecmp.cpp => world-difftab.cpp} (64%) create mode 100644 luprex/core/cpp/world-pairtab.cpp diff --git a/luprex/core/Makefile b/luprex/core/Makefile index 13856a0f..87b2dd4a 100644 --- a/luprex/core/Makefile +++ b/luprex/core/Makefile @@ -13,13 +13,14 @@ CPP_FILES=\ cpp/globaldb.cpp\ cpp/sched.cpp\ cpp/table.cpp\ - cpp/tablecmp.cpp\ cpp/gui.cpp\ cpp/luasnap.cpp\ cpp/animqueue.cpp\ cpp/streambuffer.cpp\ cpp/source.cpp\ cpp/world.cpp\ + cpp/world-difftab.cpp\ + cpp/world-pairtab.cpp\ cpp/textgame.cpp\ cpp/main.cpp diff --git a/luprex/core/cpp/tablecmp.hpp b/luprex/core/cpp/tablecmp.hpp deleted file mode 100644 index 4c8f3352..00000000 --- a/luprex/core/cpp/tablecmp.hpp +++ /dev/null @@ -1,29 +0,0 @@ -////////////////////////////////////////////////////////////// -// -// tablecmp -- compare two tables nonrecursively. -// -////////////////////////////////////////////////////////////// - -#ifndef TABLECMP_HPP -#define TABLECMP_HPP - -#include "luastack.hpp" -#include "util.hpp" -#include "streambuffer.hpp" - - -// Compare two tables, generating a diff in the specified stream buffer. -// -// The synch stack must have stnmap, stab on top. -// The master stack must have mtnmap, mtab on top. -// If cmeta is true, the metatables of the two tables are compared. -// Returns true if there were any diffs. -// -bool tablecmp_diff(lua_State *synch, lua_State *master, bool cmeta, StreamBuffer *sb); - -// Given a tablecmp_diff output, convert it to a debug string. -// -std::string tablecmp_debug_string(StreamBuffer *sb); - -#endif // TABLECMP_HPP - diff --git a/luprex/core/cpp/tablecmp.cpp b/luprex/core/cpp/world-difftab.cpp similarity index 64% rename from luprex/core/cpp/tablecmp.cpp rename to luprex/core/cpp/world-difftab.cpp index e770f2dc..7cf60ab9 100644 --- a/luprex/core/cpp/tablecmp.cpp +++ b/luprex/core/cpp/world-difftab.cpp @@ -1,6 +1,19 @@ +//////////////////////////////////////////////////////////////////// +// +// This file contains the code to compare the contents of tables. +// The top level functions in this file are: +// +// World::diff_lua_tables +// World::diff_tangible_databases +// +// This file also contains all the support code needed to implement +// this stuff. +// +//////////////////////////////////////////////////////////////////// + #include "luastack.hpp" #include "streambuffer.hpp" -#include "tablecmp.hpp" +#include "world.hpp" // Given a table and an tnmap, return the table number of the table. // Returns zero if the table doesn't have a table number. @@ -17,23 +30,6 @@ static int get_table_number(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap) { return result; } -// Get the tangible ID of a tangible. -static int64_t get_tangible_id(const LuaStack &LS, LuaSlot tab) { - int64_t id = 0; - if (LS.istable(tab) && LS.gettabletype(tab) == LUA_TT_TANGIBLE) { - lua_State *L = LS.state(); - if (lua_getmetatable(L, tab.index())) { - lua_pushstring(L, "id"); - lua_rawget(L, -2); - if (lua_type(L, -1) == LUA_TNUMBER) { - id = lua_tointeger(L, -1); - } - lua_pop(L, 2); - } - } - return id; -} - static bool equivalent_values(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap, LuaStack &SLS, LuaSlot sval, LuaSlot stnmap) { @@ -69,7 +65,7 @@ static bool equivalent_values(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap, } case LUA_TT_TANGIBLE: { if (SLS.xtype(sval) != LUA_TT_TANGIBLE) return false; - return get_tangible_id(MLS, mval) == get_tangible_id(SLS, sval); + return World::tangible_id(MLS, mval) == World::tangible_id(SLS, sval); } case LUA_TT_GLOBALENV: { return (SLS.xtype(sval) == LUA_TT_GLOBALENV); @@ -110,7 +106,7 @@ static void transmit_value(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap, StreamBu } case LUA_TT_TANGIBLE: { sb->write_uint8(LUA_TT_TANGIBLE); - sb->write_int64(get_tangible_id(MLS, mval)); + sb->write_int64(World::tangible_id(MLS, mval)); return; } case LUA_TT_GLOBALENV: { @@ -123,7 +119,48 @@ static void transmit_value(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap, StreamBu } } -bool tablecmp_diff(lua_State *synch, lua_State *master, bool cmeta, StreamBuffer *sb) { +static void transmit_value_debug_string(StreamBuffer *sb, std::ostringstream &oss) { + int kind = sb->read_uint8(); + switch (kind) { + case LUA_TBOOLEAN: { + bool b = sb->read_bool(); + oss << (b ? "true":"false"); + return; + } + case LUA_TNUMBER: { + oss << sb->read_double(); + return; + } + case LUA_TSTRING: { + oss << sb->read_string(); + return; + } + case LUA_TT_GENERAL: { + oss << "table " << sb->read_int32(); + return; + } + case LUA_TT_CLASS: { + oss << "class " << sb->read_string(); + return; + } + case LUA_TT_TANGIBLE: { + oss << "tan " << sb->read_int64(); + return; + } + case LUA_TT_GLOBALENV: { + oss << "globals"; + return; + } + case LUA_TNIL: { + oss << "nil"; + return; + } + default: + assert(false); // Should not get here. + } +} + +static bool compare_tables(lua_State *synch, lua_State *master, bool cmeta, StreamBuffer *sb) { LuaArg mtnmap, mtab, stnmap, stab; LuaVar skey, mkey, sval, mval, mnil; LuaStack SLS(synch, stnmap, stab, skey, sval); @@ -175,55 +212,14 @@ bool tablecmp_diff(lua_State *synch, lua_State *master, bool cmeta, StreamBuffer return (nupdates > 0); } -static void tablecmp_value_debug_string(StreamBuffer *sb, std::ostringstream &oss) { - int kind = sb->read_uint8(); - switch (kind) { - case LUA_TBOOLEAN: { - bool b = sb->read_bool(); - oss << (b ? "true":"false"); - return; - } - case LUA_TNUMBER: { - oss << sb->read_double(); - return; - } - case LUA_TSTRING: { - oss << sb->read_string(); - return; - } - case LUA_TT_GENERAL: { - oss << "table " << sb->read_int32(); - return; - } - case LUA_TT_CLASS: { - oss << "class " << sb->read_string(); - return; - } - case LUA_TT_TANGIBLE: { - oss << "tan " << sb->read_int64(); - return; - } - case LUA_TT_GLOBALENV: { - oss << "globals"; - return; - } - case LUA_TNIL: { - oss << "nil"; - return; - } - default: - assert(false); // Should not get here. - } -} - -std::string tablecmp_debug_string(StreamBuffer *sb) { +static std::string compare_tables_debug_string(StreamBuffer *sb) { std::vector sorted; std::ostringstream oss; int ndiffs = sb->read_int32(); for (int i = 0; i < ndiffs; i++) { - tablecmp_value_debug_string(sb, oss); + transmit_value_debug_string(sb, oss); oss << "="; - tablecmp_value_debug_string(sb, oss); + transmit_value_debug_string(sb, oss); sorted.push_back(oss.str()); oss.str(""); } @@ -234,6 +230,80 @@ std::string tablecmp_debug_string(StreamBuffer *sb) { return oss.str(); } +void World::diff_lua_tables(lua_State *master, StreamBuffer *sb) { + lua_State *synch = state(); + LuaVar sntmap, mntmap, stnmap, mtnmap; + LuaStack SLS(synch, sntmap, stnmap); + LuaStack MLS(master, mntmap, mtnmap); + SLS.rawget(sntmap, LuaRegistry, "ntmap"); + MLS.rawget(mntmap, LuaRegistry, "ntmap"); + SLS.rawget(stnmap, LuaRegistry, "tnmap"); + MLS.rawget(mtnmap, LuaRegistry, "tnmap"); + int m_ntables = MLS.rawlen(mntmap); + int s_ntables = SLS.rawlen(sntmap); + assert(m_ntables == s_ntables); + + sb->write_int32(0); + int write_count_after = sb->total_writes(); + int nmodified = 0; + int s_top = lua_gettop(synch); + int m_top = lua_gettop(master); + for (int id = 1; id <= m_ntables; id++) { + lua_pushvalue(master, mtnmap.index()); + lua_rawgeti(master, mtnmap.index(), id); + if (lua_type(master, -1) == LUA_TTABLE) { + lua_pushvalue(synch, stnmap.index()); + lua_rawgeti(synch, sntmap.index(), id); + int tw = sb->total_writes(); + sb->write_int32(id); + nmodified += 1; + if (!compare_tables(synch, master, true, sb)) { + sb->unwrite_to(tw); + nmodified -= 1; + } + assert(lua_gettop(synch) == s_top); + assert(lua_gettop(master) == m_top); + } else { + lua_pop(master, 2); + } + } + sb->overwrite_int32(write_count_after, nmodified); +} + + +void World::diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb) { + lua_State *synch = state(); + LuaVar stnmap, mtnmap, stangibles, mtangibles; + LuaStack SLS(synch, stnmap, stangibles); + LuaStack MLS(master, mtnmap, mtangibles); + SLS.rawget(stnmap, LuaRegistry, "tnmap"); + MLS.rawget(mtnmap, LuaRegistry, "tnmap"); + SLS.rawget(stangibles, LuaRegistry, "tangibles"); + MLS.rawget(mtangibles, LuaRegistry, "tangibles"); + + sb->write_int32(0); + int write_count_after = sb->total_writes(); + int nmodified = 0; + int s_top = lua_gettop(synch); + int m_top = lua_gettop(master); + for (int64_t id : basis) { + lua_pushvalue(master, mtnmap.index()); + lua_rawgeti(master, mtangibles.index(), id); + lua_pushvalue(synch, stnmap.index()); + lua_rawgeti(synch, stangibles.index(), id); + int tw = sb->total_writes(); + sb->write_int64(id); + nmodified += 1; + if (!compare_tables(synch, master, false, sb)) { + sb->unwrite_to(tw); + nmodified -= 1; + } + assert(lua_gettop(synch) == s_top); + assert(lua_gettop(master) == m_top); + } + sb->overwrite_int32(write_count_after, nmodified); +} + LuaDefine(table_diffcompare, "c") { LuaArg mtnmap, mtab, stnmap, stab; LuaRet dbgstring; @@ -250,9 +320,9 @@ LuaDefine(table_diffcompare, "c") { lua_xmove(L, synch, 2); lua_pushvalue(L, mtnmap.index()); lua_pushvalue(L, mtab.index()); - tablecmp_diff(synch, L, true, &sb); + compare_tables(synch, L, true, &sb); // Convert the output to a debug string. - LS.set(dbgstring, tablecmp_debug_string(&sb)); + LS.set(dbgstring, compare_tables_debug_string(&sb)); return LS.result(); } diff --git a/luprex/core/cpp/world-pairtab.cpp b/luprex/core/cpp/world-pairtab.cpp new file mode 100644 index 00000000..d87a6724 --- /dev/null +++ b/luprex/core/cpp/world-pairtab.cpp @@ -0,0 +1,271 @@ +//////////////////////////////////////////////////////////////////// +// +// This file contains the code to pair up tables in the synchronous +// model with tables in the master model. Here are the top-level +// routines in this file: +// +// World::number_lua_tables +// World::unnumber_lua_tables +// World::pair_lua_tables +// World::pair_new_tables +// World::create_new_tables +// +//////////////////////////////////////////////////////////////////// + +#include "luastack.hpp" +#include "streambuffer.hpp" +#include "world.hpp" + +int World::number_lua_tables(const IdVector &basis) { + // This is conceptually recursive, but we're going to use an + // explicit stack (the lua stack). + lua_State *L = state(); + LuaVar tnmap, ntmap, tangibles, tab, key, val, xid; + LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid); + LS.set(tnmap, LuaNewTable); + LS.set(ntmap, LuaNewTable); + LS.rawset(LuaRegistry, "tnmap", tnmap); + LS.rawset(LuaRegistry, "ntmap", ntmap); + LS.rawget(tangibles, LuaRegistry, "tangibles"); + int nextid = 1; + int top = lua_gettop(L); + + // Push all subtables onto the stack. Note that we may push + // the same table twice, that's OK. + for (int64_t id : basis) { + LS.rawget(tab, tangibles, id); + assert(LS.istable(tab)); + // Traverse subtables. + LS.set(key, LuaNil); + while (LS.next(tab, key, val)) { + if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { + lua_checkstack(L, 10); + lua_pushvalue(L, val.index()); + } + } + } + + // Pop tables from the stack one by one. If the table is not + // already numbered, number it and push subtables onto the stack. + while (lua_gettop(L) > top) { + lua_replace(L, tab.index()); + LS.rawget(xid, tnmap, tab); + if (LS.isnil(xid)) { + int id = nextid++; + LS.rawset(tnmap, tab, id); + LS.rawset(ntmap, id, tab); + // Traverse the metatable. + LS.getmetatable(val, tab); + if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { + lua_checkstack(L, 10); + lua_pushvalue(L, val.index()); + } + // Traverse the subtables. + LS.set(key, LuaNil); + while (LS.next(tab, key, val)) { + if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { + lua_checkstack(L, 10); + lua_pushvalue(L, val.index()); + } + } + } + } + + LS.result(); + assert(stack_is_clear()); + return nextid - 1; +} + +void World::unnumber_lua_tables() { + // All we have to do is remove these tables from the registry. + LuaStack LS(state()); + LS.rawset(LuaRegistry, "tnmap", LuaNil); + LS.rawset(LuaRegistry, "ntmap", LuaNil); +} + +void World::pair_lua_tables(const IdVector &basis, lua_State *master) { + lua_State *synch = state(); + LuaVar stangibles, mtangibles, sntmap, mntmap, stnmap, mtnmap, stab, mtab, skey, mkey, sval, mval, sidx, midx; + LuaStack SLS(synch, stangibles, stab, skey, sval, sntmap, stnmap, sidx); + LuaStack MLS(master, mtangibles, mtab, mkey, mval, mntmap, mtnmap, midx); + + // Fetch the tangible databases + SLS.rawget(stangibles, LuaRegistry, "tangibles"); + MLS.rawget(mtangibles, LuaRegistry, "tangibles"); + + // Fetch the synchronous model tnmap and ntmap + SLS.rawget(stnmap, LuaRegistry, "tnmap"); + SLS.rawget(sntmap, LuaRegistry, "ntmap"); + assert(SLS.istable(stnmap)); + assert(SLS.istable(sntmap)); + + // Initialize the master model tnmap and ntmap + MLS.set(mtnmap, LuaNewTable); + MLS.set(mntmap, LuaNewTable); + MLS.rawset(LuaRegistry, "tnmap", mtnmap); + MLS.rawset(LuaRegistry, "ntmap", mntmap); + int s_ntables = SLS.rawlen(sntmap); + for (int i = 1; i <= s_ntables; i++) { + MLS.rawset(mntmap, i, 0); + } + + // Keep track of which tables are already paired + std::vector paired; + paired.assign(s_ntables + 1, false); + + // This records the top of the stack. + int mtop = lua_gettop(master); + + for (int64_t id : basis) { + MLS.rawget(mtab, mtangibles, id); + SLS.rawget(stab, stangibles, id); + assert(MLS.istable(mtab)); + assert(SLS.istable(stab)); + MLS.set(mkey, LuaNil); + while (MLS.next(mtab, mkey, mval)) { + if (!MLS.issortablekey(mkey)) continue; + if (!MLS.istable(mval)) continue; + MLS.movesortablekey(mkey, SLS, skey); + SLS.rawget(sval, stab, skey); + if (!SLS.istable(sval)) continue; + lua_checkstack(master, 20); + lua_checkstack(synch, 20); + lua_pushvalue(master, mval.index()); + lua_pushvalue(synch, sval.index()); + } + } + + while (lua_gettop(master) > mtop) { + lua_replace(master, mtab.index()); + lua_replace(synch, stab.index()); + // If the master table is not a general table, skip. + if (MLS.gettabletype(mtab) != LUA_TT_GENERAL) continue; + // If the master table is already paired, skip. + MLS.rawget(midx, mtnmap, mtab); + if (MLS.isnumber(midx)) continue; + // If the synch table is not a table, skip. + if (!SLS.istable(stab)) continue; + // If the synch table doesn't have a number, skip. + SLS.rawget(sidx, stnmap, stab); + if (!SLS.isnumber(sidx)) continue; + int idx = SLS.ckinteger(sidx); + assert((idx >= 1) && (idx <= s_ntables)); + // Pair the tables. + MLS.rawset(mtnmap, mtab, idx); + MLS.rawset(mntmap, idx, mtab); + paired[idx] = true; + // Potentially pair the metatables. + MLS.getmetatable(mval, mtab); + if (MLS.istable(mval)) { + SLS.getmetatable(sval, stab); + if (SLS.istable(sval)) { + lua_pushvalue(master, mval.index()); + lua_pushvalue(synch, sval.index()); + } + } + // Pair the subtables. + MLS.set(mkey, LuaNil); + while (MLS.next(mtab, mkey, mval)) { + if (!MLS.issortablekey(mkey)) continue; + if (!MLS.istable(mval)) continue; + MLS.movesortablekey(mkey, SLS, skey); + SLS.rawget(sval, stab, skey); + if (!SLS.istable(sval)) continue; + lua_checkstack(master, 20); + lua_checkstack(synch, 20); + lua_pushvalue(master, mval.index()); + lua_pushvalue(synch, sval.index()); + } + } + + MLS.result(); + SLS.result(); +} + +int World::pair_new_tables(const IdVector &basis, lua_State *master) { + // This is conceptually recursive, but we're going to use an + // explicit stack (the lua stack). + lua_State *L = master; + LuaVar tnmap, ntmap, tangibles, tab, key, val, xid; + LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid); + LS.rawget(tnmap, LuaRegistry, "tnmap"); + LS.rawget(ntmap, LuaRegistry, "ntmap"); + LS.rawget(tangibles, LuaRegistry, "tangibles"); + int ntables = LS.rawlen(ntmap); + std::vector visited; + visited.assign(ntables + 1, false); + int top = lua_gettop(L); + + // Push all subtables onto the stack. Note that we may push + // the same table twice, that's OK. + for (int64_t id : basis) { + LS.rawget(tab, tangibles, id); + assert(LS.istable(tab)); + LS.set(key, LuaNil); + while (LS.next(tab, key, val)) { + if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { + lua_checkstack(L, 10); + lua_pushvalue(L, val.index()); + } + } + } + + // Pop tables from the stack one by one. If the table is not + // numbered, number it. If it is not visited, visit it. + while (lua_gettop(L) > top) { + lua_replace(L, tab.index()); + int id = 0; + LS.rawget(xid, tnmap, tab); + if (!LS.isnumber(xid)) { + id = visited.size(); + LS.rawset(tnmap, tab, id); + LS.rawset(ntmap, id, tab); + visited.push_back(false); + } else { + id = LS.cknumber(xid); + assert((id >= 0) && (id < int(visited.size()))); + } + if (!visited[id]) { + visited[id] = true; + // Traverse the metatable. + LS.getmetatable(val, tab); + if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { + lua_checkstack(L, 10); + lua_pushvalue(L, val.index()); + } + // Traverse the subtables. + LS.set(key, LuaNil); + while (LS.next(tab, key, val)) { + if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { + lua_checkstack(L, 10); + lua_pushvalue(L, val.index()); + } + } + } + } + + LS.result(); + assert(stack_is_clear()); + return visited.size() - 1 - ntables; +} + +void World::create_new_tables(int n) { + LuaVar tnmap, ntmap, tab; + LuaStack LS(state(), tnmap, ntmap, tab); + LS.rawget(tnmap, LuaRegistry, "tnmap"); + LS.rawget(ntmap, LuaRegistry, "ntmap"); + assert(LS.istable(tnmap)); + assert(LS.istable(ntmap)); + int ntables = LS.rawlen(ntmap); + int nextid = ntables + 1; + for (int i = 0; i < n; i++) { + int id = nextid++; + LS.newtable(tab); + LS.rawset(ntmap, id, tab); + LS.rawset(tnmap, tab, id); + } + LS.result(); + assert(stack_is_clear()); +} + + diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index bbba6544..cd06608c 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -4,7 +4,6 @@ #include "animqueue.hpp" #include "gui.hpp" #include "traceback.hpp" -#include "tablecmp.hpp" #include void World::store_global_pointer(lua_State *L, World *v) { @@ -879,333 +878,6 @@ void World::patch_visible_animations(StreamBuffer *sb) { } } -int World::number_lua_tables(const IdVector &basis) { - // This is conceptually recursive, but we're going to use an - // explicit stack (the lua stack). - lua_State *L = state(); - LuaVar tnmap, ntmap, tangibles, tab, key, val, xid; - LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid); - LS.set(tnmap, LuaNewTable); - LS.set(ntmap, LuaNewTable); - LS.rawset(LuaRegistry, "tnmap", tnmap); - LS.rawset(LuaRegistry, "ntmap", ntmap); - LS.rawget(tangibles, LuaRegistry, "tangibles"); - int nextid = 1; - int top = lua_gettop(L); - - // Push all subtables onto the stack. Note that we may push - // the same table twice, that's OK. - for (int64_t id : basis) { - LS.rawget(tab, tangibles, id); - assert(LS.istable(tab)); - // Traverse subtables. - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { - lua_checkstack(L, 10); - lua_pushvalue(L, val.index()); - } - } - } - - // Pop tables from the stack one by one. If the table is not - // already numbered, number it and push subtables onto the stack. - while (lua_gettop(L) > top) { - lua_replace(L, tab.index()); - LS.rawget(xid, tnmap, tab); - if (LS.isnil(xid)) { - int id = nextid++; - LS.rawset(tnmap, tab, id); - LS.rawset(ntmap, id, tab); - // Traverse the metatable. - LS.getmetatable(val, tab); - if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { - lua_checkstack(L, 10); - lua_pushvalue(L, val.index()); - } - // Traverse the subtables. - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { - lua_checkstack(L, 10); - lua_pushvalue(L, val.index()); - } - } - } - } - - LS.result(); - assert(stack_is_clear()); - return nextid - 1; -} - -void World::unnumber_lua_tables() { - // All we have to do is remove these tables from the registry. - LuaStack LS(state()); - LS.rawset(LuaRegistry, "tnmap", LuaNil); - LS.rawset(LuaRegistry, "ntmap", LuaNil); -} - -void World::pair_lua_tables(const IdVector &basis, lua_State *master) { - lua_State *synch = state(); - LuaVar stangibles, mtangibles, sntmap, mntmap, stnmap, mtnmap, stab, mtab, skey, mkey, sval, mval, sidx, midx; - LuaStack SLS(synch, stangibles, stab, skey, sval, sntmap, stnmap, sidx); - LuaStack MLS(master, mtangibles, mtab, mkey, mval, mntmap, mtnmap, midx); - - // Fetch the tangible databases - SLS.rawget(stangibles, LuaRegistry, "tangibles"); - MLS.rawget(mtangibles, LuaRegistry, "tangibles"); - - // Fetch the synchronous model tnmap and ntmap - SLS.rawget(stnmap, LuaRegistry, "tnmap"); - SLS.rawget(sntmap, LuaRegistry, "ntmap"); - assert(SLS.istable(stnmap)); - assert(SLS.istable(sntmap)); - - // Initialize the master model tnmap and ntmap - MLS.set(mtnmap, LuaNewTable); - MLS.set(mntmap, LuaNewTable); - MLS.rawset(LuaRegistry, "tnmap", mtnmap); - MLS.rawset(LuaRegistry, "ntmap", mntmap); - int s_ntables = SLS.rawlen(sntmap); - for (int i = 1; i <= s_ntables; i++) { - MLS.rawset(mntmap, i, 0); - } - - // Keep track of which tables are already paired - std::vector paired; - paired.assign(s_ntables + 1, false); - - // This records the top of the stack. - int mtop = lua_gettop(master); - - for (int64_t id : basis) { - MLS.rawget(mtab, mtangibles, id); - SLS.rawget(stab, stangibles, id); - assert(MLS.istable(mtab)); - assert(SLS.istable(stab)); - MLS.set(mkey, LuaNil); - while (MLS.next(mtab, mkey, mval)) { - if (!MLS.issortablekey(mkey)) continue; - if (!MLS.istable(mval)) continue; - MLS.movesortablekey(mkey, SLS, skey); - SLS.rawget(sval, stab, skey); - if (!SLS.istable(sval)) continue; - lua_checkstack(master, 20); - lua_checkstack(synch, 20); - lua_pushvalue(master, mval.index()); - lua_pushvalue(synch, sval.index()); - } - } - - while (lua_gettop(master) > mtop) { - lua_replace(master, mtab.index()); - lua_replace(synch, stab.index()); - // If the master table is not a general table, skip. - if (MLS.gettabletype(mtab) != LUA_TT_GENERAL) continue; - // If the master table is already paired, skip. - MLS.rawget(midx, mtnmap, mtab); - if (MLS.isnumber(midx)) continue; - // If the synch table is not a table, skip. - if (!SLS.istable(stab)) continue; - // If the synch table doesn't have a number, skip. - SLS.rawget(sidx, stnmap, stab); - if (!SLS.isnumber(sidx)) continue; - int idx = SLS.ckinteger(sidx); - assert((idx >= 1) && (idx <= s_ntables)); - // Pair the tables. - MLS.rawset(mtnmap, mtab, idx); - MLS.rawset(mntmap, idx, mtab); - paired[idx] = true; - // Potentially pair the metatables. - MLS.getmetatable(mval, mtab); - if (MLS.istable(mval)) { - SLS.getmetatable(sval, stab); - if (SLS.istable(sval)) { - lua_pushvalue(master, mval.index()); - lua_pushvalue(synch, sval.index()); - } - } - // Pair the subtables. - MLS.set(mkey, LuaNil); - while (MLS.next(mtab, mkey, mval)) { - if (!MLS.issortablekey(mkey)) continue; - if (!MLS.istable(mval)) continue; - MLS.movesortablekey(mkey, SLS, skey); - SLS.rawget(sval, stab, skey); - if (!SLS.istable(sval)) continue; - lua_checkstack(master, 20); - lua_checkstack(synch, 20); - lua_pushvalue(master, mval.index()); - lua_pushvalue(synch, sval.index()); - } - } - - MLS.result(); - SLS.result(); -} - -int World::pair_new_tables(const IdVector &basis, lua_State *master) { - // This is conceptually recursive, but we're going to use an - // explicit stack (the lua stack). - lua_State *L = master; - LuaVar tnmap, ntmap, tangibles, tab, key, val, xid; - LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid); - LS.rawget(tnmap, LuaRegistry, "tnmap"); - LS.rawget(ntmap, LuaRegistry, "ntmap"); - LS.rawget(tangibles, LuaRegistry, "tangibles"); - int ntables = LS.rawlen(ntmap); - std::vector visited; - visited.assign(ntables + 1, false); - int top = lua_gettop(L); - - // Push all subtables onto the stack. Note that we may push - // the same table twice, that's OK. - for (int64_t id : basis) { - LS.rawget(tab, tangibles, id); - assert(LS.istable(tab)); - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { - lua_checkstack(L, 10); - lua_pushvalue(L, val.index()); - } - } - } - - // Pop tables from the stack one by one. If the table is not - // numbered, number it. If it is not visited, visit it. - while (lua_gettop(L) > top) { - lua_replace(L, tab.index()); - int id = 0; - LS.rawget(xid, tnmap, tab); - if (!LS.isnumber(xid)) { - id = visited.size(); - LS.rawset(tnmap, tab, id); - LS.rawset(ntmap, id, tab); - visited.push_back(false); - } else { - id = LS.cknumber(xid); - assert((id >= 0) && (id < int(visited.size()))); - } - if (!visited[id]) { - visited[id] = true; - // Traverse the metatable. - LS.getmetatable(val, tab); - if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { - lua_checkstack(L, 10); - lua_pushvalue(L, val.index()); - } - // Traverse the subtables. - LS.set(key, LuaNil); - while (LS.next(tab, key, val)) { - if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) { - lua_checkstack(L, 10); - lua_pushvalue(L, val.index()); - } - } - } - } - - LS.result(); - assert(stack_is_clear()); - return visited.size() - 1 - ntables; -} - -void World::create_new_tables(int n) { - LuaVar tnmap, ntmap, tab; - LuaStack LS(state(), tnmap, ntmap, tab); - LS.rawget(tnmap, LuaRegistry, "tnmap"); - LS.rawget(ntmap, LuaRegistry, "ntmap"); - assert(LS.istable(tnmap)); - assert(LS.istable(ntmap)); - int ntables = LS.rawlen(ntmap); - int nextid = ntables + 1; - for (int i = 0; i < n; i++) { - int id = nextid++; - LS.newtable(tab); - LS.rawset(ntmap, id, tab); - LS.rawset(tnmap, tab, id); - } - LS.result(); - assert(stack_is_clear()); -} - -void World::diff_lua_tables(lua_State *master, StreamBuffer *sb) { - lua_State *synch = state(); - LuaVar sntmap, mntmap, stnmap, mtnmap; - LuaStack SLS(synch, sntmap, stnmap); - LuaStack MLS(master, mntmap, mtnmap); - SLS.rawget(sntmap, LuaRegistry, "ntmap"); - MLS.rawget(mntmap, LuaRegistry, "ntmap"); - SLS.rawget(stnmap, LuaRegistry, "tnmap"); - MLS.rawget(mtnmap, LuaRegistry, "tnmap"); - int m_ntables = MLS.rawlen(mntmap); - int s_ntables = SLS.rawlen(sntmap); - assert(m_ntables == s_ntables); - - sb->write_int32(0); - int write_count_after = sb->total_writes(); - int nmodified = 0; - int s_top = lua_gettop(synch); - int m_top = lua_gettop(master); - for (int id = 1; id <= m_ntables; id++) { - lua_pushvalue(master, mtnmap.index()); - lua_rawgeti(master, mtnmap.index(), id); - if (lua_type(master, -1) == LUA_TTABLE) { - lua_pushvalue(synch, stnmap.index()); - lua_rawgeti(synch, sntmap.index(), id); - int tw = sb->total_writes(); - sb->write_int32(id); - nmodified += 1; - if (!tablecmp_diff(synch, master, true, sb)) { - sb->unwrite_to(tw); - nmodified -= 1; - } - assert(lua_gettop(synch) == s_top); - assert(lua_gettop(master) == m_top); - } else { - lua_pop(master, 2); - } - } - sb->overwrite_int32(write_count_after, nmodified); -} - - -void World::diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb) { - lua_State *synch = state(); - LuaVar stnmap, mtnmap, stangibles, mtangibles; - LuaStack SLS(synch, stnmap, stangibles); - LuaStack MLS(master, mtnmap, mtangibles); - SLS.rawget(stnmap, LuaRegistry, "tnmap"); - MLS.rawget(mtnmap, LuaRegistry, "tnmap"); - SLS.rawget(stangibles, LuaRegistry, "tangibles"); - MLS.rawget(mtangibles, LuaRegistry, "tangibles"); - - sb->write_int32(0); - int write_count_after = sb->total_writes(); - int nmodified = 0; - int s_top = lua_gettop(synch); - int m_top = lua_gettop(master); - for (int64_t id : basis) { - lua_pushvalue(master, mtnmap.index()); - lua_rawgeti(master, mtangibles.index(), id); - lua_pushvalue(synch, stnmap.index()); - lua_rawgeti(synch, stangibles.index(), id); - int tw = sb->total_writes(); - sb->write_int64(id); - nmodified += 1; - if (!tablecmp_diff(synch, master, false, sb)) { - sb->unwrite_to(tw); - nmodified -= 1; - } - assert(lua_gettop(synch) == s_top); - assert(lua_gettop(master) == m_top); - } - sb->overwrite_int32(write_count_after, nmodified); -} - - LuaDefine(tangible_animstate, "c") { LuaArg tanobj; LuaRet graphic, plane, x, y, z, facing; diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 14e5fc8d..dd7fe82b 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -285,11 +285,26 @@ private: static void diff_visible_animations(const TanVector &mvis, const TanVector &svis, StreamBuffer *sb); void patch_visible_animations(StreamBuffer *sb); + // Compare the general tables. + // + void diff_lua_tables(lua_State *master, StreamBuffer *sb); + + // Compare the tangible databases. + // + void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb); + public: - // Pass 3 of difference transmission: numbering of tables. + /////////////////////////////////////////////////////////// + // + // Numbering and pairing of lua tables. // // Table-to-number mapping is stored in registry.tnmap // Number-to-table mapping is stored in registry.ntmap + // + /////////////////////////////////////////////////////////// + + // numbering of tables. + // // Returns the total number of tables numbered. // int number_lua_tables(const IdVector &basis); @@ -313,14 +328,6 @@ public: // void create_new_tables(int n); - // Compare the general tables. - // - void diff_lua_tables(lua_State *master, StreamBuffer *sb); - - // Compare the tangible databases. - // - void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb); - private: // Type of model util::WorldType world_type_;