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