Files
integration/luprex/cpp/core/serializelua.cpp

190 lines
5.3 KiB
C++

#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;
}