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
|
#pragma once
#include <cstdint>
#include <cstring>
#include <optional>
#include <type_traits>
#include <vector>
namespace {
// Trivial type
template <typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true>
std::vector<uint8_t> encode_helper(const T& val) {
auto begin = reinterpret_cast<const uint8_t*>(&val);
auto end = begin + sizeof(val);
return {begin, end};
}
// Container type
template <typename Container,
std::enable_if_t<!std::is_trivially_copyable_v<Container>, bool> = true>
std::vector<uint8_t> encode_helper(const Container& val) {
// Check comment in decode_helper below
static_assert(std::is_trivially_copyable_v<typename Container::value_type>,
"Can encode only a containers of trivial types currently");
constexpr auto member_size = sizeof(typename Container::value_type);
auto n_bytes = member_size * val.size();
std::vector<uint8_t> out(n_bytes);
std::memcpy(out.data(), val.data(), n_bytes);
return out;
}
// Trivial type
template <typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true>
std::optional<T> decode_helper(const std::vector<uint8_t>& bytes) {
T t;
if (sizeof(t) != bytes.size()) {
return {};
}
std::memcpy(&t, bytes.data(), bytes.size());
return t;
}
// Container type
template <typename Container,
std::enable_if_t<!std::is_trivially_copyable_v<Container>, bool> = true>
std::optional<Container> decode_helper(const std::vector<uint8_t>& bytes) {
Container t;
size_t member_size = sizeof(typename Container::value_type);
// NOTE: This can only reconstruct container of trivial types, not a
// container of non-trivial types. We can either use a standard serializer
// (like protobuf) or roll one of our own simple ones (like prepending size
// of the object), but have to be careful about securing such a serializer.
// But, do we even need that? I do not see any metadata which is either not
// trivial or a container of trivial type.
size_t to_copy = bytes.size();
if (to_copy % member_size != 0) {
return {};
}
size_t members = to_copy / member_size;
t.resize(members);
std::memcpy(t.data(), bytes.data(), to_copy);
return t;
}
} // namespace
namespace pixel::graphics::utils {
// TODO: Setup a fuzzer for encode/decode
template <typename T>
std::vector<uint8_t> encode(const T& val) {
return encode_helper(val);
}
template <typename T>
std::optional<T> decode(const std::vector<uint8_t>& bytes) {
return decode_helper<T>(bytes);
}
} // namespace pixel::graphics::utils
|