#include "serializelua.hpp" enum PackCodes { LUA_PK_TRUE = LUA_TT_SENTINEL, LUA_PK_FALSE, LUA_PK_REFERENCE, LUA_PK_ENDTABLE }; class Deserializer { LuaVar id_to_value_; LuaStack LS_; int next_id_; eng::string &error_; StreamBuffer *sb_; Deserializer(LuaStack &LS0, LuaSlot val, StreamBuffer *sb, eng::string &error) : LS_(LS0.state(), lookup_, value_to_id_), error_(error) { next_id_ = 1; LS_.newtable(id_to_value_); try { uint8_t b = sb_->read_uint8(); deserialize_r(b, val); } catch (StreamEof e) { error_ = "EOF reached while deserializing data"; LS_.set(val, LuaNil); } LS_.result(); } }; // If the serialize routine encounters an error, it must set the // error message string. In that case, it is assumed that the contents // of the serialization buffer is no longer valid and there is no // requirement to try to keep it valid. class Serializer { LuaVar lookup_; LuaVar value_to_id_; LuaStack LS_; int next_id_; eng::string &error_; StreamBuffer *sb_; void serialize_keyvals_r(LuaSlot tab) { LuaVar key, val; LuaStack SLS(LS_.state(), key, val); SLS.getmetatable(val, tab); if (!LS.isnil(val)) { error_ = "Cannot serialize metatables"; SLS.result(); return; } sb_->pack_uint8(LUA_TT_GENERAL); SLS.set(key, LuaNil); while (LS.next(tab, key, val)) { serialize_r(key); if (!error_.empty()) { SLS.result(); return; } serialize_r(val); if (!error_.empty()) { SLS.result(); return; } } sb_->pack_uint8(LUA_PK_ENDTABLE); SLS.result(); } void serialize_r(LuaSlot val) { int tt = LS_.xtype(val); switch (tt) { case LUA_TNIL: { sb_->pack_uint8(LUA_TNIL); return; } case LUA_TBOOLEAN: { if (LS_.ckboolean(val)) { sb_->pack_uint8(LUA_PK_TRUE); } else { sb_->pack_uint8(LUA_PK_FALSE); } return; } case LUA_TLIGHTUSERDATA: { sb_->pack_uint8(LUA_TLIGHTUSERDATA); sb_->pack_uint64(LS_.cktoken(val).value); return; } case LUA_TNUMBER: { sb_->pack_uint8(LUA_TNUMBER); sb_->pack_double(LS_.cknumber(val)); return; } case LUA_TSTRING: { LS_.rawget(lookup_, value_to_id_, val); if (!LS_.isnil(lookup_)) { sb_->pack_uint8(LUA_PK_REFERENCE); sb_->pack_uint32(LS.ckint(lookup_)); } else { LS_.rawset(value_to_id_, val, next_id_++); sb_->pack_uint8(LUA_TSTRING); sb_->pack_string(LS_.ckstring(val)); } return; } case LUA_TTABLE: { // LS.xtype should never return LUA_TTABLE. error_ = "Bad xtype in serialization"; return; } case LUA_TFUNCTION: { error_ = "Cannot serialize closures"; return; } case LUA_TUSERDATA: { error_ = "Cannot serialize userdata"; return; } case LUA_TTHREAD: { error_ = "Cannot serialize coroutines"; return; } case LUA_TT_GENERAL: { LS_.rawget(lookup_, value_to_id_, val); if (!LS_.isnil(lookup_)) { sb_->pack_uint8(LUA_PK_REFERENCE); sb_->pack_uint32(LS.ckint(lookup_)); } else { LS_.rawset(value_to_id_, val, next_id_++); serialize_table_r(val); } return; } case LUA_TT_REGISTRY: { error_ = "Pointer to registry found in serialization, shouldn't happen"; return; } case LUA_TT_GLOBALENV: { sb_->pack_uint8(LUA_TT_GLOBALENV); return; } case LUA_TT_TANGIBLE: { sb_->pack_uint8(LUA_TT_TANGIBLE); sb_->pack_int64(LS_.tanid(val)); return; } case LUA_TT_TANGIBLEMETA: { error_ = "Pointer to a tangible metatable found in serialization, shouldn't happen"; return; } case LUA_TT_CLASS: { sb_->pack_uint8(LUA_TT_CLASS); sb_->pack_string(LS_.classname(val)); return; } default: { error_ = "Unrecognized xtype in serialization"; return; } } Serializer(LuaStack &LS0, LuaSlot val, StreamBuffer *sb, eng::string &error) : LS_(LS0.state(), lookup_, value_to_id_), error_(error) { next_id_ = 1; LS_.newtable(value_to_id_); serialize_r(val); LS_.result(); } }; eng::string serialize_lua(LuaStack &LS0, LuaSlot val, StreamBuffer *sb) { eng::string error; Serializer sz(LS0, val, sb, error); return error; }; eng::string deserialize_lua(LuaStack &LS0, LuaSlot val, StreamBuffer *sb) { eng::string error; Deserializer dsz(LS0, val, sb, error); if (!error_.empty()) { LS0.set(val, LuaNil); } return error; }