229 lines
6.1 KiB
C++
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
|