-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserializer.h
More file actions
170 lines (135 loc) · 4.71 KB
/
serializer.h
File metadata and controls
170 lines (135 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#ifndef LIB_SERIALIZER_H
#define LIB_SERIALIZER_H
#include <cstddef> // for size_t
#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>
namespace core {
template<typename T>
class AbstractSerializer {
public:
virtual ~AbstractSerializer() = default;
// create a buffer and serialize object to it however you want
virtual char* serialize(const T& obj) const = 0;
// deserialize from some T that was AbstractSerializer::serialize into that buffer
virtual T deserialize(char* buffer, size_t size) const = 0;
// how many bytes we need to serialize object T
virtual size_t bytesRequired() const = 0;
};
}; // namespace core
class OBufferArchive {
public:
static constexpr bool isLoading = false;
OBufferArchive() = default;
// Return a const reference to the underlying buffer
const std::vector<char>& buffer() const { return buffer_; }
// Alternatively, a pointer + size if needed
const char* data() const { return buffer_.data(); }
std::size_t size() const { return buffer_.size(); }
// Clears the internal buffer
void clear() { buffer_.clear(); }
template<typename T>
OBufferArchive& operator&(const T& value) {
static_assert(std::is_arithmetic<T>::value,
"OBufferArchive only supports arithmetic by default here. "
"Use custom serialize(...) for complex types.");
writeBytes(&value, sizeof(T));
return *this;
}
// In OBufferArchive:
OBufferArchive& operator&(const char* cstr) {
// 1) Determine length (0 if cstr == nullptr)
std::size_t length = 0;
if (cstr) {
length = std::strlen(cstr);
}
// 2) Write length as an arithmetic type
*this& length; // calls the arithmetic overload
// 3) Write the raw characters if length > 0
if (length > 0) {
writeBytes(cstr, length);
}
return *this;
}
OBufferArchive& operator&(const std::string& str) {
// 1) write length
std::size_t length = str.size();
*this& length; // calls the arithmetic overload
// 2) write the raw characters
if (length > 0) {
writeBytes(str.data(), length);
}
return *this;
}
private:
std::vector<char> buffer_;
void writeBytes(const void* ptr, std::size_t size) {
const char* cptr = static_cast<const char*>(ptr);
buffer_.insert(buffer_.end(), cptr, cptr + size);
}
};
class IBufferArchive {
public:
static constexpr bool isLoading = true;
// The constructor takes a pointer to an existing buffer and its length.
IBufferArchive(const char* data, std::size_t size) : data_(data), size_(size), pos_(0) {}
// No copying to avoid confusion, but you can allow it if you like
IBufferArchive(const IBufferArchive&) = delete;
IBufferArchive& operator=(const IBufferArchive&) = delete;
// -----------------------------
// operator& for arithmetic T
// -----------------------------
template<typename T>
IBufferArchive& operator&(T& value) {
static_assert(std::is_arithmetic<T>::value,
"IBufferArchive only supports arithmetic by default. "
"Use custom serialize(...) for non-trivial types.");
readBytes(&value, sizeof(T));
return *this;
}
// -----------------------------
// operator& for std::string
// -----------------------------
IBufferArchive& operator&(std::string& str) {
// 1) read length
std::size_t length = 0;
*this& length;
// 2) read the characters
str.clear();
if (length > 0) {
str.resize(length);
readBytes(&str[0], length);
}
return *this;
}
IBufferArchive& operator&(const char*& str) {
// 1) read the length
std::size_t length = 0;
*this& length; // calls the arithmetic overload
// 2) if there's data, allocate + read
if (length > 0) {
// allocate a buffer, read the bytes
char* temp = new char[length + 1];
readBytes(temp, length);
temp[length] = '\0'; // null-terminate
str = temp; // store the pointer in 'str'
} else {
// no data means make str null
str = nullptr;
}
return *this;
}
private:
const char* data_{nullptr};
std::size_t size_{0};
std::size_t pos_{0};
void readBytes(void* dst, std::size_t len) {
if (pos_ + len > size_) {
throw std::runtime_error("IBufferArchive: out of range read");
}
std::memcpy(dst, data_ + pos_, len);
pos_ += len;
}
};
#endif