1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4
5 #define CAF_SUITE binary_serializer
6
7 #include "caf/binary_serializer.hpp"
8
9 #include "core-test.hpp"
10 #include "nasty.hpp"
11
12 #include <cstring>
13 #include <vector>
14
15 #include "caf/actor_system.hpp"
16 #include "caf/actor_system_config.hpp"
17 #include "caf/byte.hpp"
18 #include "caf/byte_buffer.hpp"
19 #include "caf/timestamp.hpp"
20
21 using namespace caf;
22
23 namespace {
24
operator ""_b(unsigned long long int x)25 byte operator"" _b(unsigned long long int x) {
26 return static_cast<byte>(x);
27 }
28
operator ""_b(char x)29 byte operator"" _b(char x) {
30 return static_cast<byte>(x);
31 }
32
33 struct arr {
34 int8_t xs[3];
35 };
36
37 template <class Inspector>
inspect(Inspector & f,arr & x)38 bool inspect(Inspector& f, arr& x) {
39 return f.object(x).fields(f.field("xs", x.xs));
40 }
41
42 struct fixture {
43 template <class... Ts>
save__anona65dee720111::fixture44 auto save(const Ts&... xs) {
45 byte_buffer result;
46 binary_serializer sink{nullptr, result};
47 if (!(sink.apply(xs) && ...))
48 CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
49 return result;
50 }
51
52 template <class... Ts>
save_to_buf__anona65dee720111::fixture53 void save_to_buf(byte_buffer& data, const Ts&... xs) {
54 binary_serializer sink{nullptr, data};
55 if (!(sink.apply(xs) && ...))
56 CAF_FAIL("binary_serializer failed to save: " << sink.get_error());
57 }
58 };
59
60 } // namespace
61
CAF_TEST_FIXTURE_SCOPE(binary_serializer_tests,fixture)62 CAF_TEST_FIXTURE_SCOPE(binary_serializer_tests, fixture)
63
64 #define SUBTEST(msg) \
65 CAF_MESSAGE(msg); \
66 for (int subtest_dummy = 0; subtest_dummy < 1; ++subtest_dummy)
67
68 #define CHECK_SAVE(type, value, ...) \
69 CAF_CHECK_EQUAL(save(type{value}), byte_buffer({__VA_ARGS__}))
70
71 CAF_TEST(primitive types) {
72 SUBTEST("8-bit integers") {
73 CHECK_SAVE(int8_t, 60, 0b00111100_b);
74 CHECK_SAVE(int8_t, -61, 0b11000011_b);
75 CHECK_SAVE(uint8_t, 60u, 0b00111100_b);
76 CHECK_SAVE(uint8_t, 195u, 0b11000011_b);
77 }
78 SUBTEST("16-bit integers") {
79 CHECK_SAVE(int16_t, 85, 0b00000000_b, 0b01010101_b);
80 CHECK_SAVE(int16_t, -32683, 0b10000000_b, 0b01010101_b);
81 CHECK_SAVE(uint16_t, 85u, 0b00000000_b, 0b01010101_b);
82 CHECK_SAVE(uint16_t, 32853u, 0b10000000_b, 0b01010101_b);
83 }
84 SUBTEST("32-bit integers") {
85 CHECK_SAVE(int32_t, -345, 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b);
86 CHECK_SAVE(uint32_t, 4294966951u, 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b);
87 }
88 SUBTEST("64-bit integers") {
89 CHECK_SAVE(int64_t, -1234567890123456789ll, //
90 0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b);
91 CHECK_SAVE(uint64_t, 17212176183586094827llu, //
92 0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b);
93 }
94 SUBTEST("floating points use IEEE-754 conversion") {
95 CHECK_SAVE(float, 3.45f, 0x40_b, 0x5C_b, 0xCC_b, 0xCD_b);
96 }
97 SUBTEST("strings use a varbyte-encoded size prefix") {
98 CHECK_SAVE(std::string, "hello", 5_b, 'h'_b, 'e'_b, 'l'_b, 'l'_b, 'o'_b);
99 }
100 SUBTEST("enum types") {
101 CHECK_SAVE(test_enum, test_enum::a, 0_b, 0_b, 0_b, 0_b);
102 CHECK_SAVE(test_enum, test_enum::b, 0_b, 0_b, 0_b, 1_b);
103 CHECK_SAVE(test_enum, test_enum::c, 0_b, 0_b, 0_b, 2_b);
104 }
105 }
106
CAF_TEST(concatenation)107 CAF_TEST(concatenation) {
108 SUBTEST("calling f(a, b) writes a and b into the buffer in order") {
109 CAF_CHECK_EQUAL(save(int8_t{7}, int16_t{-32683}),
110 byte_buffer({7_b, 0x80_b, 0x55_b}));
111 CAF_CHECK_EQUAL(save(int16_t{-32683}, int8_t{7}),
112 byte_buffer({0x80_b, 0x55_b, 7_b}));
113 }
114 SUBTEST("calling f(a) and then f(b) is equal to calling f(a, b)") {
115 byte_buffer data;
116 binary_serializer sink{nullptr, data};
117 save_to_buf(data, int8_t{7});
118 save_to_buf(data, int16_t{-32683});
119 CAF_CHECK_EQUAL(data, byte_buffer({7_b, 0x80_b, 0x55_b}));
120 }
121 SUBTEST("calling f(make_pair(a, b)) is equal to calling f(a, b)") {
122 using i8i16_pair = std::pair<int8_t, int16_t>;
123 using i16i8_pair = std::pair<int16_t, int8_t>;
124 CHECK_SAVE(i8i16_pair, std::make_pair(int8_t{7}, int16_t{-32683}), //
125 7_b, 0x80_b, 0x55_b);
126 CHECK_SAVE(i16i8_pair, std::make_pair(int16_t{-32683}, int8_t{7}), //
127 0x80_b, 0x55_b, 7_b);
128 }
129 SUBTEST("calling f(make_tuple(a, b)) is equivalent to f(make_pair(a, b))") {
130 using i8i16_tuple = std::tuple<int8_t, int16_t>;
131 using i16i8_tuple = std::tuple<int16_t, int8_t>;
132 CHECK_SAVE(i8i16_tuple, std::make_tuple(int8_t{7}, int16_t{-32683}), //
133 7_b, 0x80_b, 0x55_b);
134 CHECK_SAVE(i16i8_tuple, std::make_tuple(int16_t{-32683}, int8_t{7}), //
135 0x80_b, 0x55_b, 7_b);
136 }
137 SUBTEST("arrays behave like tuples") {
138 arr xs{{1, 2, 3}};
139 CAF_CHECK_EQUAL(save(xs), byte_buffer({1_b, 2_b, 3_b}));
140 }
141 }
142
CAF_TEST(container types)143 CAF_TEST(container types) {
144 SUBTEST("STL vectors") {
145 CHECK_SAVE(std::vector<int8_t>, std::vector<int8_t>({1, 2, 4, 8}), //
146 4_b, 1_b, 2_b, 4_b, 8_b);
147 }
148 SUBTEST("STL sets") {
149 CHECK_SAVE(std::set<int8_t>, std::set<int8_t>({1, 2, 4, 8}), //
150 4_b, 1_b, 2_b, 4_b, 8_b);
151 }
152 }
153
CAF_TEST(binary serializer picks up inspect functions)154 CAF_TEST(binary serializer picks up inspect functions) {
155 SUBTEST("node ID") {
156 auto nid = make_node_id(123, "000102030405060708090A0B0C0D0E0F10111213");
157 CHECK_SAVE(node_id, unbox(nid),
158 // content index for hashed_node_id (1)
159 1_b,
160 // Process ID.
161 0_b, 0_b, 0_b, 123_b,
162 // Host ID.
163 0_b, 1_b, 2_b, 3_b, 4_b, 5_b, 6_b, 7_b, 8_b, 9_b, //
164 10_b, 11_b, 12_b, 13_b, 14_b, 15_b, 16_b, 17_b, 18_b, 19_b);
165 }
166 SUBTEST("custom struct") {
167 caf::timestamp ts{caf::timestamp::duration{1478715821 * 1000000000ll}};
168 test_data value{-345,
169 -1234567890123456789ll,
170 3.45,
171 54.3,
172 ts,
173 test_enum::b,
174 "Lorem ipsum dolor sit amet."};
175 CHECK_SAVE(test_data, value,
176 // 32-bit i32_ member: -345
177 0xFF_b, 0xFF_b, 0xFE_b, 0xA7_b,
178 // 64-bit i64_ member: -1234567890123456789ll
179 0xEE_b, 0xDD_b, 0xEF_b, 0x0B_b, 0x82_b, 0x16_b, 0x7E_b, 0xEB_b,
180 // 32-bit f32_ member: 3.45f
181 0x40_b, 0x5C_b, 0xCC_b, 0xCD_b,
182 // 64-bit f64_ member: 54.3
183 0x40_b, 0x4B_b, 0x26_b, 0x66_b, 0x66_b, 0x66_b, 0x66_b, 0x66_b,
184 // 64-bit ts_ member.
185 0x14_b, 0x85_b, 0x74_b, 0x34_b, 0x62_b, 0x74_b, 0x82_b, 0x00_b,
186 // 32-bit te_ member: test_enum::b
187 0x00_b, 0x00_b, 0x00_b, 0x01_b,
188 // str_ member:
189 0x1B_b, //
190 'L'_b, 'o'_b, 'r'_b, 'e'_b, 'm'_b, ' '_b, 'i'_b, 'p'_b, 's'_b,
191 'u'_b, 'm'_b, ' '_b, 'd'_b, 'o'_b, 'l'_b, 'o'_b, 'r'_b, ' '_b,
192 's'_b, 'i'_b, 't'_b, ' '_b, 'a'_b, 'm'_b, 'e'_b, 't'_b, '.'_b);
193 }
194 SUBTEST("enum class with non-default overload") {
195 auto day = weekday::friday;
196 CHECK_SAVE(weekday, day, 0x04_b);
197 }
198 }
199
200 CAF_TEST_FIXTURE_SCOPE_END()
201