Add Packer/Unpacker, add serialization to LuaSnap
This commit is contained in:
@@ -26,39 +26,8 @@ LuaSnap::~LuaSnap() {
|
||||
std::cerr << "LuaSnap destructor not implemented yet" << std::endl;
|
||||
}
|
||||
|
||||
bool LuaSnap::have_snapshot() const {
|
||||
return !snapshot_.empty();
|
||||
}
|
||||
|
||||
static int oss_writer(lua_State *L, const void *p, size_t sz, void *ud) {
|
||||
std::ostringstream *oss = (std::ostringstream *)ud;
|
||||
oss->write((const char *)p, sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert a C++ string into a lua_Reader.
|
||||
class StringReader {
|
||||
public:
|
||||
const std::string &str_;
|
||||
bool done_;
|
||||
StringReader(const std::string &s) : str_(s), done_(false) {}
|
||||
|
||||
static const char *fn(lua_State *L, void *data, size_t *size) {
|
||||
StringReader *sr = (StringReader*)data;
|
||||
if (sr->done_) {
|
||||
*size = 0;
|
||||
return nullptr;
|
||||
} else {
|
||||
sr->done_ = true;
|
||||
*size = sr->str_.size();
|
||||
return sr->str_.c_str();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void LuaSnap::snapshot() {
|
||||
// Snapshot and the lua stack should both be empty.
|
||||
assert(snapshot_.empty());
|
||||
void LuaSnap::serialize(Packer *pk) {
|
||||
// Lua stack should be empty.
|
||||
assert(lua_gettop(state_) == 0);
|
||||
|
||||
// lua variables that we'll need.
|
||||
@@ -93,24 +62,26 @@ void LuaSnap::snapshot() {
|
||||
LS.result();
|
||||
assert(lua_gettop(state_) == 2);
|
||||
|
||||
// Call eris to dump the state to an ostringstream,
|
||||
// then save the result.
|
||||
std::ostringstream oss;
|
||||
eris_dump(state_, oss_writer, (void *)&oss);
|
||||
snapshot_ = oss.str();
|
||||
std::cerr << "Eris dump is " << snapshot_.size() << " bytes." << std::endl;
|
||||
// Write dummy length, use eris to write data, then overwrite length.
|
||||
pk->write_int64(0);
|
||||
size_t tell = pk->tellp();
|
||||
eris_dump(state_, pk->lua_writer, pk->lua_writer_ud());
|
||||
pk->overwrite_int64(tell, pk->tellp() - tell);
|
||||
lua_settop(state_, 0);
|
||||
std::cerr << "Eris dump is " << (pk->tellp() - tell) << " bytes." << std::endl;
|
||||
}
|
||||
|
||||
void LuaSnap::rollback() {
|
||||
// Snapshot should have data, lua stack should be empty.
|
||||
assert(!snapshot_.empty());
|
||||
void LuaSnap::deserialize(Unpacker *unpk) {
|
||||
// Lua stack should be empty.
|
||||
assert(lua_gettop(state_) == 0);
|
||||
|
||||
// Get a reader subsection containing the eris data.
|
||||
size_t len = unpk->read_int64();
|
||||
Unpacker subsec = unpk->read_section(len);
|
||||
|
||||
// Call eris with the permanents table and passing the snapshot as a lua_Reader.
|
||||
lua_getfield(state_, LUA_REGISTRYINDEX, "unpersist");
|
||||
StringReader sr(snapshot_);
|
||||
eris_undump(state_, StringReader::fn, &sr);
|
||||
eris_undump(state_, subsec.lua_reader, subsec.lua_reader_ud());
|
||||
assert(lua_gettop(state_) == 2);
|
||||
|
||||
// Set up a stack frame.
|
||||
@@ -127,6 +98,47 @@ void LuaSnap::rollback() {
|
||||
}
|
||||
LS.result();
|
||||
assert(lua_gettop(state_) == 0);
|
||||
}
|
||||
|
||||
// Snapshot and rollback can trivially be implemented on top of serialize and
|
||||
// deserialize. However, it's also possible to implement snapshot and rollback
|
||||
// using an alternative technique:
|
||||
//
|
||||
// 1. When constructing the lua interpreter, use a custom memory allocator that
|
||||
// keeps track of all the memory blocks used by lua.
|
||||
//
|
||||
// 2. Snapshot simply copies all the memory blocks used by lua into a buffer.
|
||||
//
|
||||
// 3. Rollback restores lua's memory blocks back to their previous state. This
|
||||
// has the effect of restoring lua's state.
|
||||
//
|
||||
// A proof-of-concept implementation of the memory-snapshotting design was
|
||||
// created, and it worked. It is probably faster than using serialize and
|
||||
// deserialize.
|
||||
//
|
||||
// Note: even if we implement this alternative design, we still need to keep
|
||||
// serialize and deserialize around in order to implement the save-game
|
||||
// functionality. So for now, we're sticking with this design, which doesn't
|
||||
// require us to maintain any additional code.
|
||||
|
||||
bool LuaSnap::have_snapshot() const {
|
||||
return !snapshot_.empty();
|
||||
}
|
||||
|
||||
void LuaSnap::snapshot() {
|
||||
assert(snapshot_.empty());
|
||||
std::ostringstream oss;
|
||||
Packer pk(&oss);
|
||||
serialize(&pk);
|
||||
snapshot_ = oss.str();
|
||||
}
|
||||
|
||||
void LuaSnap::rollback() {
|
||||
assert(!snapshot_.empty());
|
||||
Unpacker unpk(snapshot_.c_str(), snapshot_.size());
|
||||
deserialize(&unpk);
|
||||
snapshot_.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user