Text-Adventure/Serializable.h
2023-11-16 00:44:12 +08:00

229 lines
6.1 KiB
C++

//
// Created by tqcq on 2023/11/14.
//
#ifndef TEXTADV_SERIALIZABLE_H
#define TEXTADV_SERIALIZABLE_H
#include <map>
#include <vector>
#include <string>
#include <sstream>
class Serializable {
public:
virtual ~Serializable() = default;
virtual std::string ToString() const = 0;
virtual void FromString(const std::string &str) = 0;
virtual void serialize(std::ostream &os) const;
virtual void deserialize(std::istream &is);
friend std::ostream &operator<<(std::ostream &os, const Serializable &obj) {
obj.serialize(os);
return os;
}
friend std::istream &operator>>(std::istream &is, Serializable &obj) {
obj.deserialize(is);
return is;
}
};
class Packer {
public:
enum Type {
CHAR = 0,
INT,
LONG,
SIZE_T,
DOUBLE,
FLOAT,
STRING,
VECTOR,
MAP,
};
struct PacketHeader {
Type type;
size_t size;
};
struct Packet {
union {
PacketHeader header;
struct {
Type type;
size_t size;
};
};
std::string data;
};
Packer& pack(char c) {
data.push_back({CHAR, sizeof(char), std::string(sizeof(char), c)});
return *this;
}
Packer& pack(int i) {
data.push_back({INT, sizeof(int), std::string((char*)&i, sizeof(int))});
return *this;
}
Packer& pack(long l) {
data.push_back({LONG, sizeof(long), std::string((char*)&l, sizeof(long))});
return *this;
}
Packer& pack(size_t s) {
data.push_back({SIZE_T, sizeof(size_t), std::string((char*)&s, sizeof(size_t))});
return *this;
}
Packer& pack(double d) {
data.push_back({DOUBLE, sizeof(double), std::string((char*)&d, sizeof(double))});
return *this;
}
Packer& pack(float f) {
data.push_back({FLOAT, sizeof(float), std::string((char*)&f, sizeof(float))});
return *this;
}
Packer& pack(const std::string &str) {
data.push_back({STRING, str.size(), str});
return *this;
}
Packer& pack(const Serializable &obj) {
pack(obj.ToString());
return *this;
}
Packer& pack(const Serializable* obj) {
pack(obj->ToString());
return *this;
}
Packer& unpack(char &c) {
Packet &d = data.back();
if (d.type != CHAR) throw std::runtime_error("Type mismatch");
c = d.data[0];
data.pop_back();
return *this;
}
Packer& unpack(int &i) {
Packet &d = data.back();
if (d.type != INT) throw std::runtime_error("Type mismatch");
i = *(int*)d.data.data();
data.pop_back();
return *this;
}
Packer& unpack(long &l) {
Packet &d = data.back();
if (d.type != LONG) throw std::runtime_error("Type mismatch");
l = *(long*)d.data.data();
data.pop_back();
return *this;
}
Packer& unpack(size_t& s) {
Packet &d = data.back();
if (d.type != SIZE_T) throw std::runtime_error("Type mismatch");
s = *(size_t*)d.data.data();
data.pop_back();
return *this;
}
Packer& unpack(double &d) {
Packet &packet = this->data.back();
if (packet.type != DOUBLE) throw std::runtime_error("Type mismatch");
d = *(double*)packet.data.data();
this->data.pop_back();
return *this;
}
Packer& unpack(float &f) {
Packet &packet = this->data.back();
if (packet.type != FLOAT) throw std::runtime_error("Type mismatch");
f = *(float*)packet.data.data();
this->data.pop_back();
return *this;
}
Packer& unpack(std::string &str) {
Packet &packet= this->data.back();
if (packet.type != STRING) throw std::runtime_error("Type mismatch");
str = packet.data;
this->data.pop_back();
return *this;
}
Packer& unpack(Serializable &obj) {
std::string str;
unpack(str);
obj.FromString(str);
return *this;
}
Packer& unpack(Serializable* obj) {
std::string str;
unpack(str);
obj->FromString(str);
return *this;
}
std::string ToHexStr() const {
static const std::string hex_str = "0123456789ABCDEF";
std::string str;
for (const auto& d : data) {
// convert header to hex
auto* ptr = (unsigned char*)&d.header;
auto* end = ptr + sizeof(d.header);
for (; ptr != end; ++ptr) {
str.push_back(hex_str[*ptr >> 4]);
str.push_back(hex_str[*ptr & 0x0F]);
}
// convert data to hex
ptr = (unsigned char*)d.data.data();
end = ptr + d.data.size();
for (; ptr != end; ++ptr) {
str.push_back(hex_str[*ptr >> 4]);
str.push_back(hex_str[*ptr & 0x0F]);
}
}
return str;
}
void FromHexStr(const std::string& str) {
static const std::string hex_str = "0123456789ABCDEF";
data.clear();
int idx = 0;
for (;idx < str.size();) {
Packet packet;
// convert header from hex
auto* ptr = (unsigned char*)&packet.header;
auto* end = ptr + sizeof(packet.header);
for (; ptr != end; ++ptr) {
auto hi = hex_str.find(str[idx++]);
auto lo = hex_str.find(str[idx++]);
if (hi == std::string::npos || lo == std::string::npos) throw std::runtime_error("Invalid hex string");
*ptr = (hi << 4) | lo;
}
packet.data.resize(packet.header.size);
for (size_t i = 0; i < packet.header.size; ++i) {
auto hi = hex_str.find(str[idx++]);
auto lo = hex_str.find(str[idx++]);
if (hi == std::string::npos || lo == std::string::npos) throw std::runtime_error("Invalid hex string");
packet.data[i] = (hi << 4) | lo;
}
data.emplace_back(packet);
}
}
private:
std::vector<Packet> data;
};
#endif //TEXTADV_SERIALIZABLE_H