Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | #pragma once |
||
8 | #include <algorithm> |
||
9 | #include <cassert> |
||
10 | #include <cstddef> |
||
11 | #include <cstdint> |
||
12 | #include <functional> |
||
13 | #include <initializer_list> |
||
14 | #include <tuple> |
||
15 | #include <type_traits> |
||
16 | |||
17 | #include "dxxsconf.h" |
||
18 | #include "compiler-range_for.h" |
||
19 | #include "compiler-static_assert.h" |
||
20 | #include <array> |
||
21 | #include <memory> |
||
22 | #include <utility> |
||
23 | |||
24 | namespace serial { |
||
25 | |||
26 | template <typename... Args> |
||
27 | class message; |
||
28 | |||
29 | template <typename> |
||
30 | class message_type; |
||
31 | |||
32 | /* Classifiers to identify whether a type is a message<...> */ |
||
33 | template <typename> |
||
34 | class is_message : public std::false_type |
||
35 | { |
||
36 | }; |
||
37 | |||
38 | template <typename... Args> |
||
39 | class is_message<message<Args...>> : public std::true_type |
||
40 | { |
||
41 | }; |
||
42 | |||
43 | namespace detail { |
||
44 | |||
45 | template <std::size_t maximum, std::size_t minimum = maximum> |
||
46 | struct size_base |
||
47 | { |
||
48 | static constexpr std::integral_constant<std::size_t, maximum> maximum_size = {}; |
||
49 | static constexpr std::integral_constant<std::size_t, minimum> minimum_size = {}; |
||
50 | }; |
||
51 | |||
52 | template <std::size_t maximum, std::size_t minimum> |
||
53 | constexpr std::integral_constant<std::size_t, maximum> size_base<maximum, minimum>::maximum_size; |
||
54 | |||
55 | template <std::size_t maximum, std::size_t minimum> |
||
56 | constexpr std::integral_constant<std::size_t, minimum> size_base<maximum, minimum>::minimum_size; |
||
57 | |||
58 | } |
||
59 | |||
60 | template <typename> |
||
61 | class is_cxx_array : public std::false_type |
||
62 | { |
||
63 | }; |
||
64 | |||
65 | template <typename T, std::size_t N> |
||
66 | class is_cxx_array<std::array<T, N>> : public std::true_type |
||
67 | { |
||
68 | }; |
||
69 | |||
70 | template <typename T> |
||
71 | class is_cxx_array<const T> : public is_cxx_array<T> |
||
72 | { |
||
73 | }; |
||
74 | |||
75 | template <typename T> |
||
76 | using is_generic_class = typename std::conditional<is_cxx_array<T>::value, std::false_type, std::is_class<T>>::type; |
||
77 | |||
78 | template <typename Accessor, typename A1> |
||
79 | static inline typename std::enable_if<std::is_integral<typename std::remove_reference<A1>::type>::value, void>::type process_buffer(Accessor &&accessor, A1 &&a1) |
||
80 | { |
||
81 | process_integer(std::forward<Accessor &&>(accessor), a1); |
||
82 | } |
||
83 | |||
84 | template <typename Accessor, typename A1, typename A1rr = typename std::remove_reference<A1>::type> |
||
85 | static inline typename std::enable_if<std::is_enum<A1rr>::value, void>::type process_buffer(Accessor &, A1 &&); |
||
86 | |||
87 | template <typename Accessor, typename A1, typename A1rr = typename std::remove_reference<A1>::type> |
||
88 | static inline typename std::enable_if<is_generic_class<A1rr>::value, void>::type process_buffer(Accessor &, A1 &&); |
||
89 | |||
90 | template <typename Accessor, typename A1> |
||
91 | static typename std::enable_if<is_cxx_array<A1>::value, void>::type process_buffer(Accessor &&, A1 &); |
||
92 | |||
93 | template <typename Accessor, typename... Args> |
||
94 | static void process_buffer(Accessor &, const message<Args...> &); |
||
95 | |||
96 | class endian_access |
||
97 | { |
||
98 | public: |
||
99 | /* |
||
100 | * Endian access modes: |
||
101 | * - foreign_endian: assume buffered data is foreign endian |
||
102 | * Byte swap regardless of host byte order |
||
103 | * - little_endian: assume buffered data is little endian |
||
104 | * Copy on little endian host, byte swap on big endian host |
||
105 | * - big_endian: assume buffered data is big endian |
||
106 | * Copy on big endian host, byte swap on little endian host |
||
107 | * - native_endian: assume buffered data is native endian |
||
108 | * Copy regardless of host byte order |
||
109 | */ |
||
110 | typedef std::integral_constant<uint16_t, 0> foreign_endian_type; |
||
111 | typedef std::integral_constant<uint16_t, 255> little_endian_type; |
||
112 | typedef std::integral_constant<uint16_t, 256> big_endian_type; |
||
113 | typedef std::integral_constant<uint16_t, 257> native_endian_type; |
||
114 | |||
115 | static constexpr auto foreign_endian = foreign_endian_type{}; |
||
116 | static constexpr auto little_endian = little_endian_type{}; |
||
117 | static constexpr auto big_endian = big_endian_type{}; |
||
118 | static constexpr auto native_endian = native_endian_type{}; |
||
119 | }; |
||
120 | |||
121 | /* Implementation details - avoid namespace pollution */ |
||
122 | namespace detail { |
||
123 | |||
124 | template <typename T, typename Trr = typename std::remove_reference<T>::type> |
||
125 | using capture_type = typename std::conditional<std::is_lvalue_reference<T>::value, |
||
126 | std::reference_wrapper<Trr>, |
||
127 | std::tuple<Trr> |
||
128 | >; |
||
129 | |||
130 | template <typename T, typename Trr = typename std::remove_reference<T>::type> |
||
131 | static inline auto capture_value(Trr &t) -> decltype(std::ref(t)) |
||
132 | { |
||
133 | return std::ref(t); |
||
134 | } |
||
135 | |||
136 | template <typename T, typename Trr = typename std::remove_reference<T>::type> |
||
137 | static inline typename std::enable_if<std::is_rvalue_reference<T>::value, std::tuple<Trr>>::type capture_value(Trr &&t) |
||
138 | { |
||
139 | return std::tuple<Trr>{std::forward<T>(t)}; |
||
140 | } |
||
141 | |||
142 | template <typename extended_signed_type, typename wrapped_type> |
||
143 | class sign_extend_type : std::reference_wrapper<wrapped_type> |
||
144 | { |
||
145 | static_assert(sizeof(extended_signed_type) > sizeof(wrapped_type), "cannot sign-extend into a type of equal or smaller size"); |
||
146 | static_assert(std::is_signed<extended_signed_type>::value, "cannot sign-extend into an unsigned type"); |
||
147 | using base_type = std::reference_wrapper<wrapped_type>; |
||
148 | public: |
||
149 | using base_type::base_type; |
||
150 | using base_type::get; |
||
151 | }; |
||
152 | |||
153 | template <typename extended_signed_type, typename wrapped_type> |
||
154 | message<std::array<uint8_t, sizeof(extended_signed_type)>> udt_to_message(const sign_extend_type<extended_signed_type, wrapped_type> &); |
||
155 | |||
156 | template <std::size_t amount, uint8_t value> |
||
157 | class pad_type |
||
158 | { |
||
159 | }; |
||
160 | |||
161 | template <std::size_t amount, uint8_t value> |
||
162 | message<std::array<uint8_t, amount>> udt_to_message(const pad_type<amount, value> &); |
||
163 | |||
164 | /* |
||
165 | * This can never be instantiated, but will be requested if a UDT |
||
166 | * specialization is missing. |
||
167 | */ |
||
168 | template <typename T> |
||
169 | class missing_udt_specialization |
||
170 | { |
||
171 | public: |
||
172 | missing_udt_specialization() = delete; |
||
173 | }; |
||
174 | |||
175 | template <typename T> |
||
176 | void udt_to_message(T &, missing_udt_specialization<T> = missing_udt_specialization<T>()); |
||
177 | |||
178 | template <typename Accessor, typename UDT> |
||
179 | void preprocess_udt(Accessor &, UDT &) {} |
||
180 | |||
181 | template <typename Accessor, typename UDT> |
||
182 | void postprocess_udt(Accessor &, UDT &) {} |
||
183 | |||
184 | template <typename Accessor, typename UDT> |
||
185 | static inline void process_udt(Accessor &&accessor, UDT &udt) |
||
186 | { |
||
187 | process_buffer(std::forward<Accessor &&>(accessor), udt_to_message(udt)); |
||
188 | } |
||
189 | |||
190 | template <typename Accessor, typename E> |
||
191 | void check_enum(Accessor &, E) {} |
||
192 | |||
193 | template <typename T, typename D> |
||
194 | struct base_bytebuffer_t : endian_access |
||
195 | { |
||
196 | public: |
||
197 | using iterator_category = std::random_access_iterator_tag; |
||
198 | using value_type = T; |
||
199 | using difference_type = std::ptrdiff_t; |
||
200 | using pointer = T *; |
||
201 | using reference = T &; |
||
202 | // Default bytebuffer_t usage to little endian |
||
203 | static constexpr endian_access::little_endian_type endian{}; |
||
204 | base_bytebuffer_t(pointer u) : p(u) {} |
||
205 | operator pointer() const { return p; } |
||
206 | D &operator++() |
||
207 | { |
||
208 | ++p; |
||
209 | return *static_cast<D *>(this); |
||
210 | } |
||
211 | D &operator--() |
||
212 | { |
||
213 | --p; |
||
214 | return *static_cast<D *>(this); |
||
215 | } |
||
216 | D &operator+=(const difference_type d) |
||
217 | { |
||
218 | p += d; |
||
219 | return *static_cast<D *>(this); |
||
220 | } |
||
221 | operator const void *() const = delete; |
||
222 | protected: |
||
223 | pointer p; |
||
224 | }; |
||
225 | |||
226 | #define SERIAL_UDT_ROUND_UP(X,M) (((X) + (M) - 1) & ~((M) - 1)) |
||
227 | template <std::size_t amount, |
||
228 | std::size_t SERIAL_UDT_ROUND_MULTIPLIER = sizeof(void *), |
||
229 | std::size_t SERIAL_UDT_ROUND_UP_AMOUNT = SERIAL_UDT_ROUND_UP(amount, SERIAL_UDT_ROUND_MULTIPLIER), |
||
230 | std::size_t FULL_SIZE = amount / 64 ? 64 : SERIAL_UDT_ROUND_UP_AMOUNT, |
||
231 | std::size_t REMAINDER_SIZE = amount % 64> |
||
232 | union pad_storage |
||
233 | { |
||
234 | static_assert(amount % SERIAL_UDT_ROUND_MULTIPLIER ? SERIAL_UDT_ROUND_UP_AMOUNT > amount && SERIAL_UDT_ROUND_UP_AMOUNT < amount + SERIAL_UDT_ROUND_MULTIPLIER : SERIAL_UDT_ROUND_UP_AMOUNT == amount, "round up error"); |
||
235 | static_assert(SERIAL_UDT_ROUND_UP_AMOUNT % SERIAL_UDT_ROUND_MULTIPLIER == 0, "round modulus error"); |
||
236 | static_assert(amount % FULL_SIZE == REMAINDER_SIZE || FULL_SIZE == REMAINDER_SIZE, "padding alignment error"); |
||
237 | std::array<uint8_t, FULL_SIZE> f; |
||
238 | std::array<uint8_t, REMAINDER_SIZE> p; |
||
239 | #undef SERIAL_UDT_ROUND_UP |
||
240 | }; |
||
241 | |||
242 | template <typename Accessor, std::size_t amount, uint8_t value> |
||
243 | static inline void process_udt(Accessor &&accessor, const pad_type<amount, value> &) |
||
244 | { |
||
245 | /* If reading from accessor, accessor data is const and buffer is |
||
246 | * overwritten by read. |
||
247 | * If writing to accessor, accessor data is non-const, so initialize |
||
248 | * buffer to be written. |
||
249 | */ |
||
250 | pad_storage<amount> s; |
||
251 | if constexpr (!std::is_const< |
||
252 | typename std::remove_pointer< |
||
253 | /* rvalue reference `Accessor &&` causes `Accessor` to be `T &` |
||
254 | * for some type T. Use std::remove_reference to get T. Then |
||
255 | * take the type `pointer` from type T to use as input to |
||
256 | * std::remove_pointer. |
||
257 | */ |
||
258 | typename std::remove_reference<Accessor>::type |
||
259 | ::pointer |
||
260 | >::type |
||
261 | >::value) |
||
262 | s.f.fill(value); |
||
263 | for (std::size_t count = amount; count; count -= s.f.size()) |
||
264 | { |
||
265 | if (count < s.f.size()) |
||
266 | { |
||
267 | assert(count == s.p.size()); |
||
268 | process_buffer(accessor, s.p); |
||
269 | break; |
||
270 | } |
||
271 | process_buffer(accessor, s.f); |
||
272 | } |
||
273 | } |
||
274 | |||
275 | template <typename T> |
||
276 | static inline T &extract_value(std::reference_wrapper<T> t) |
||
277 | { |
||
278 | return t; |
||
279 | } |
||
280 | |||
281 | template <typename T> |
||
282 | static inline T &extract_value(std::tuple<T> &t) |
||
283 | { |
||
284 | return std::get<0>(t); |
||
285 | } |
||
286 | |||
287 | template <typename T> |
||
288 | static inline const T &extract_value(const std::tuple<T> &t) |
||
289 | { |
||
290 | return std::get<0>(t); |
||
291 | } |
||
292 | |||
293 | template <typename T> |
||
294 | struct message_dispatch_base |
||
295 | { |
||
296 | using effective_type = T; |
||
297 | }; |
||
298 | |||
299 | } |
||
300 | |||
301 | template <std::size_t amount, uint8_t value = 0xcc> |
||
302 | using pad = detail::pad_type<amount, value>; |
||
303 | |||
304 | template <typename extended_signed_type, typename wrapped_type> |
||
305 | static inline detail::sign_extend_type<extended_signed_type, wrapped_type> sign_extend(wrapped_type &t) |
||
306 | { |
||
307 | return {t}; |
||
308 | } |
||
309 | |||
310 | #define DEFINE_SERIAL_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \ |
||
311 | DEFINE_SERIAL_CONST_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \ |
||
312 | DEFINE_SERIAL_MUTABLE_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \ |
||
313 | |||
314 | #define _DEFINE_SERIAL_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \ |
||
315 | template <typename Accessor> \ |
||
316 | static inline void process_udt(Accessor &&accessor, TYPE &NAME) \ |
||
317 | { \ |
||
318 | using serial::process_buffer; \ |
||
319 | process_buffer(std::forward<Accessor &&>(accessor), _SERIAL_UDT_UNWRAP_LIST MEMBERLIST); \ |
||
320 | } \ |
||
321 | \ |
||
322 | __attribute_unused \ |
||
323 | static inline auto udt_to_message(TYPE &NAME) { \ |
||
324 | return serial::message MEMBERLIST; \ |
||
325 | } |
||
326 | |||
327 | #define DEFINE_SERIAL_CONST_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \ |
||
328 | _DEFINE_SERIAL_UDT_TO_MESSAGE(const TYPE, NAME, MEMBERLIST) |
||
329 | #define DEFINE_SERIAL_MUTABLE_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \ |
||
330 | _DEFINE_SERIAL_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) |
||
331 | |||
332 | #define ASSERT_SERIAL_UDT_MESSAGE_SIZE(T, SIZE) \ |
||
333 | assert_equal(serial::class_type<T>::maximum_size, SIZE, "sizeof(" #T ") is not " #SIZE) |
||
334 | |||
335 | template <typename M1, typename T1, typename base_type = std::is_same<typename std::remove_cv<typename std::remove_reference<M1>::type>::type, T1>> |
||
336 | struct udt_message_compatible_same_type : base_type |
||
337 | { |
||
338 | static_assert(base_type::value, "parameter type mismatch"); |
||
339 | }; |
||
340 | |||
341 | template <bool, typename M, typename T> |
||
342 | class assert_udt_message_compatible2; |
||
343 | |||
344 | template <typename M, typename T> |
||
345 | class assert_udt_message_compatible2<false, M, T> : public std::false_type |
||
346 | { |
||
347 | }; |
||
348 | |||
349 | template <typename... Mn, typename... Tn> |
||
350 | class assert_udt_message_compatible2<true, message<Mn...>, std::tuple<Tn...>> : |
||
351 | public std::integral_constant<bool, (udt_message_compatible_same_type<Mn, Tn>::value && ...)> |
||
352 | { |
||
353 | }; |
||
354 | |||
355 | template <typename M, typename T> |
||
356 | class assert_udt_message_compatible; |
||
357 | |||
358 | template <typename... Mn, typename... Tn> |
||
359 | class assert_udt_message_compatible<message<Mn...>, std::tuple<Tn...>> : public assert_udt_message_compatible2<sizeof...(Mn) == sizeof...(Tn), message<Mn...>, std::tuple<Tn...>> |
||
360 | { |
||
361 | static_assert(sizeof...(Mn) <= sizeof...(Tn), "too few types in tuple"); |
||
362 | static_assert(sizeof...(Mn) >= sizeof...(Tn), "too few types in message"); |
||
363 | }; |
||
364 | |||
365 | template <typename T> |
||
366 | using class_type = message_type<decltype(udt_to_message(std::declval<T>()))>; |
||
367 | |||
368 | #define _SERIAL_UDT_UNWRAP_LIST(A1,...) A1, ## __VA_ARGS__ |
||
369 | |||
370 | #define ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) \ |
||
371 | ASSERT_SERIAL_UDT_MESSAGE_CONST_TYPE(T, TYPELIST); \ |
||
372 | ASSERT_SERIAL_UDT_MESSAGE_MUTABLE_TYPE(T, TYPELIST); \ |
||
373 | |||
374 | #define _ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) \ |
||
375 | static_assert(serial::assert_udt_message_compatible<typename class_type<T>::as_message, std::tuple<_SERIAL_UDT_UNWRAP_LIST TYPELIST>>::value, "udt/message mismatch") |
||
376 | |||
377 | #define ASSERT_SERIAL_UDT_MESSAGE_CONST_TYPE(T, TYPELIST) \ |
||
378 | _ASSERT_SERIAL_UDT_MESSAGE_TYPE(const T, TYPELIST) |
||
379 | #define ASSERT_SERIAL_UDT_MESSAGE_MUTABLE_TYPE(T, TYPELIST) \ |
||
380 | _ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) |
||
381 | |||
382 | union endian_skip_byteswap_u |
||
383 | { |
||
384 | uint8_t c[2]; |
||
385 | uint16_t s; |
||
386 | constexpr endian_skip_byteswap_u(const uint16_t &u) : s(u) |
||
387 | { |
||
388 | static_assert((offsetof(endian_skip_byteswap_u, c) == offsetof(endian_skip_byteswap_u, s)), "union layout error"); |
||
389 | } |
||
390 | }; |
||
391 | |||
392 | static inline constexpr uint8_t endian_skip_byteswap(const uint16_t &endian) |
||
393 | { |
||
394 | return endian_skip_byteswap_u{endian}.c[0]; |
||
395 | } |
||
396 | |||
397 | template <typename T, std::size_t N> |
||
398 | union unaligned_storage |
||
399 | { |
||
400 | T a; |
||
401 | typename std::conditional<N < 4, |
||
402 | typename std::conditional<N == 1, uint8_t, uint16_t>, |
||
403 | typename std::conditional<N == 4, uint32_t, uint64_t>>::type::type i; |
||
404 | uint8_t u[N]; |
||
405 | assert_equal(sizeof(i), N, "sizeof(i) is not N"); |
||
406 | assert_equal(sizeof(a), sizeof(u), "sizeof(T) is not N"); |
||
407 | }; |
||
408 | |||
409 | template <typename T, typename = void> |
||
410 | class message_dispatch_type; |
||
411 | |||
412 | template <typename T> |
||
413 | class message_dispatch_type<T, typename std::enable_if<std::is_integral<T>::value or std::is_enum<T>::value, void>::type> : |
||
414 | public detail::message_dispatch_base<detail::size_base<sizeof(T)>> |
||
415 | { |
||
416 | }; |
||
417 | |||
418 | template <typename T> |
||
419 | class message_dispatch_type<T, typename std::enable_if<is_cxx_array<T>::value, void>::type> : |
||
420 | public detail::message_dispatch_base< |
||
421 | detail::size_base< |
||
422 | message_type<typename T::value_type>::maximum_size * std::tuple_size<T>::value, |
||
423 | message_type<typename T::value_type>::minimum_size * std::tuple_size<T>::value |
||
424 | > |
||
425 | > |
||
426 | { |
||
427 | }; |
||
428 | |||
429 | template <typename T> |
||
430 | class message_dispatch_type<T, typename std::enable_if<is_generic_class<T>::value && !is_message<T>::value, void>::type> : |
||
431 | public detail::message_dispatch_base<class_type<T>> |
||
432 | { |
||
433 | }; |
||
434 | |||
435 | template <typename T> |
||
436 | class message_type : |
||
437 | message_dispatch_type<typename std::remove_reference<T>::type>::effective_type |
||
438 | { |
||
439 | using effective_type = typename message_dispatch_type<typename std::remove_reference<T>::type>::effective_type; |
||
440 | public: |
||
441 | using effective_type::maximum_size; |
||
442 | using effective_type::minimum_size; |
||
443 | }; |
||
444 | |||
445 | template <typename A1> |
||
446 | class message_dispatch_type<message<A1>, void> : |
||
447 | public detail::message_dispatch_base<message_type<A1>> |
||
448 | { |
||
449 | public: |
||
450 | typedef message<A1> as_message; |
||
451 | }; |
||
452 | |||
453 | template <typename... Args> |
||
454 | class message_type<message<Args...>> : |
||
455 | public detail::size_base< |
||
456 | (0 + ... + message_dispatch_type<message<Args>>::effective_type::maximum_size), |
||
457 | (0 + ... + message_dispatch_type<message<Args>>::effective_type::minimum_size) |
||
458 | > |
||
459 | { |
||
460 | public: |
||
461 | using as_message = message<Args...>; |
||
462 | }; |
||
463 | |||
464 | template <typename... Args> |
||
465 | class message |
||
466 | { |
||
467 | static_assert(sizeof...(Args) > 0, "message must have at least one template argument"); |
||
468 | using tuple_type = std::tuple<typename detail::capture_type<Args &&>::type...>; |
||
469 | template <typename T1> |
||
470 | static void check_type() |
||
471 | { |
||
472 | static_assert(message_type<T1>::maximum_size > 0, "empty field in message"); |
||
473 | } |
||
474 | tuple_type t; |
||
475 | public: |
||
476 | message(Args &&... args) : |
||
477 | t(detail::capture_value<Args>(std::forward<Args>(args))...) |
||
478 | { |
||
479 | (check_type<Args>(), ...); |
||
480 | } |
||
481 | const tuple_type &get_tuple() const |
||
482 | { |
||
483 | return t; |
||
484 | } |
||
485 | }; |
||
486 | |||
487 | template <typename... Args> |
||
488 | message(Args &&... args) -> message<Args && ...>; |
||
489 | |||
490 | #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_BUILTIN(HBITS,BITS) \ |
||
491 | static inline constexpr uint##BITS##_t bswap(const uint##BITS##_t &u) \ |
||
492 | { \ |
||
493 | return __builtin_bswap##BITS(u); \ |
||
494 | } |
||
495 | |||
496 | #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_EXPLICIT(HBITS,BITS) \ |
||
497 | static inline constexpr uint##BITS##_t bswap(const uint##BITS##_t &u) \ |
||
498 | { \ |
||
499 | return (static_cast<uint##BITS##_t>(bswap(static_cast<uint##HBITS##_t>(u))) << HBITS) | \ |
||
500 | static_cast<uint##BITS##_t>(bswap(static_cast<uint##HBITS##_t>(u >> HBITS))); \ |
||
501 | } |
||
502 | |||
503 | #define SERIAL_DEFINE_SIZE_SPECIFIC_BSWAP(HBITS,BITS) \ |
||
504 | SERIAL_DEFINE_SIZE_SPECIFIC_USWAP(HBITS,BITS); \ |
||
505 | static inline constexpr int##BITS##_t bswap(const int##BITS##_t &i) \ |
||
506 | { \ |
||
507 | return bswap(static_cast<uint##BITS##_t>(i)); \ |
||
508 | } |
||
509 | |||
510 | static inline constexpr uint8_t bswap(const uint8_t &u) |
||
511 | { |
||
512 | return u; |
||
513 | } |
||
514 | |||
515 | static inline constexpr int8_t bswap(const int8_t &u) |
||
516 | { |
||
517 | return u; |
||
518 | } |
||
519 | |||
520 | #ifdef DXX_HAVE_BUILTIN_BSWAP16 |
||
521 | #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_BUILTIN |
||
522 | #else |
||
523 | #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_EXPLICIT |
||
524 | #endif |
||
525 | |||
526 | SERIAL_DEFINE_SIZE_SPECIFIC_BSWAP(8, 16); |
||
527 | #undef SERIAL_DEFINE_SIZE_SPECIFIC_USWAP |
||
528 | |||
529 | #ifdef DXX_HAVE_BUILTIN_BSWAP |
||
530 | #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_BUILTIN |
||
531 | #else |
||
532 | #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_EXPLICIT |
||
533 | #endif |
||
534 | |||
535 | SERIAL_DEFINE_SIZE_SPECIFIC_BSWAP(16, 32); |
||
536 | SERIAL_DEFINE_SIZE_SPECIFIC_BSWAP(32, 64); |
||
537 | |||
538 | #undef SERIAL_DEFINE_SIZE_SPECIFIC_BSWAP |
||
539 | #undef SERIAL_DEFINE_SIZE_SPECIFIC_USWAP |
||
540 | #undef SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_BUILTIN |
||
541 | #undef SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_EXPLICIT |
||
542 | |||
543 | namespace reader { |
||
544 | |||
545 | class bytebuffer_t : public detail::base_bytebuffer_t<const uint8_t, bytebuffer_t> |
||
546 | { |
||
547 | public: |
||
548 | bytebuffer_t(pointer u) : base_bytebuffer_t(u) {} |
||
549 | explicit bytebuffer_t(const bytebuffer_t &) = default; |
||
550 | bytebuffer_t(bytebuffer_t &&) = default; |
||
551 | }; |
||
552 | |||
553 | template <typename A1, std::size_t BYTES> |
||
554 | static inline void unaligned_copy(const uint8_t *src, unaligned_storage<A1, BYTES> &dst) |
||
555 | { |
||
556 | if constexpr (BYTES == 1) |
||
557 | dst.u[0] = *src; |
||
558 | else |
||
559 | std::copy_n(src, sizeof(dst.u), dst.u); |
||
560 | } |
||
561 | |||
562 | template <typename Accessor, typename A1> |
||
563 | static inline void process_integer(Accessor &buffer, A1 &a1) |
||
564 | { |
||
565 | using std::advance; |
||
566 | unaligned_storage<A1, message_type<A1>::maximum_size> u; |
||
567 | unaligned_copy(buffer, u); |
||
568 | if (!endian_skip_byteswap(buffer.endian())) |
||
569 | u.i = bswap(u.i); |
||
570 | a1 = u.a; |
||
571 | advance(buffer, sizeof(u.u)); |
||
572 | } |
||
573 | |||
574 | template <typename Accessor, typename A, typename T = typename A::value_type> |
||
575 | static inline typename std::enable_if<sizeof(T) == 1 && std::is_integral<T>::value, void>::type process_array(Accessor &accessor, A &a) |
||
576 | { |
||
577 | using std::advance; |
||
578 | std::copy_n(static_cast<typename Accessor::pointer>(accessor), a.size(), &a[0]); |
||
579 | advance(accessor, a.size()); |
||
580 | } |
||
581 | |||
582 | template <typename Accessor, typename extended_signed_type, typename wrapped_type> |
||
583 | static inline void process_udt(Accessor &&accessor, const detail::sign_extend_type<extended_signed_type, wrapped_type> &v) |
||
584 | { |
||
585 | extended_signed_type est; |
||
586 | process_integer<Accessor, extended_signed_type>(static_cast<Accessor &&>(accessor), est); |
||
587 | v.get() = static_cast<wrapped_type>(est); |
||
588 | } |
||
589 | |||
590 | } |
||
591 | |||
592 | namespace writer { |
||
593 | |||
594 | class bytebuffer_t : public detail::base_bytebuffer_t<uint8_t, bytebuffer_t> |
||
595 | { |
||
596 | public: |
||
597 | bytebuffer_t(pointer u) : base_bytebuffer_t(u) {} |
||
598 | explicit bytebuffer_t(const bytebuffer_t &) = default; |
||
599 | bytebuffer_t(bytebuffer_t &&) = default; |
||
600 | }; |
||
601 | |||
602 | /* If unaligned_copy is manually inlined into the caller, then gcc |
||
603 | * inlining of copy_n creates a loop instead of a store. |
||
604 | */ |
||
605 | template <typename A1, std::size_t BYTES> |
||
606 | static inline void unaligned_copy(const unaligned_storage<A1, BYTES> &src, uint8_t *dst) |
||
607 | { |
||
608 | if constexpr (BYTES == 1) |
||
609 | *dst = src.u[0]; |
||
610 | else |
||
611 | std::copy_n(src.u, sizeof(src.u), dst); |
||
612 | } |
||
613 | |||
614 | template <typename Accessor, typename A1> |
||
615 | static inline void process_integer(Accessor &buffer, const A1 &a1) |
||
616 | { |
||
617 | using std::advance; |
||
618 | unaligned_storage<A1, message_type<A1>::maximum_size> u{a1}; |
||
619 | if (!endian_skip_byteswap(buffer.endian())) |
||
620 | u.i = bswap(u.i); |
||
621 | unaligned_copy(u, buffer); |
||
622 | advance(buffer, sizeof(u.u)); |
||
623 | } |
||
624 | |||
625 | template <typename Accessor, typename A, typename T = typename A::value_type> |
||
626 | static inline typename std::enable_if<sizeof(T) == 1 && std::is_integral<T>::value, void>::type process_array(Accessor &accessor, const A &a) |
||
627 | { |
||
628 | using std::advance; |
||
629 | std::copy_n(&a[0], a.size(), static_cast<typename Accessor::pointer>(accessor)); |
||
630 | advance(accessor, a.size()); |
||
631 | } |
||
632 | |||
633 | template <typename Accessor, typename extended_signed_type, typename wrapped_type> |
||
634 | static inline void process_udt(Accessor &&accessor, const detail::sign_extend_type<extended_signed_type, const wrapped_type> &v) |
||
635 | { |
||
636 | const typename std::make_signed<wrapped_type>::type swt = v.get(); |
||
637 | const extended_signed_type est = swt; |
||
638 | process_integer<Accessor, extended_signed_type>(static_cast<Accessor &&>(accessor), est); |
||
639 | } |
||
640 | |||
641 | } |
||
642 | |||
643 | template <typename Accessor, typename A1, typename A1rr> |
||
644 | static inline typename std::enable_if<std::is_enum<A1rr>::value, void>::type process_buffer(Accessor &accessor, A1 &&a1) |
||
645 | { |
||
646 | using detail::check_enum; |
||
647 | process_integer(accessor, a1); |
||
648 | /* Hook for enum types to check that the given value is legal */ |
||
649 | check_enum(accessor, a1); |
||
650 | } |
||
651 | |||
652 | template <typename Accessor, typename A1, typename A1rr> |
||
653 | static inline typename std::enable_if<is_generic_class<A1rr>::value, void>::type process_buffer(Accessor &accessor, A1 &&a1) |
||
654 | { |
||
655 | using detail::preprocess_udt; |
||
656 | using detail::process_udt; |
||
657 | using detail::postprocess_udt; |
||
658 | preprocess_udt(accessor, a1); |
||
659 | process_udt(accessor, std::forward<A1>(a1)); |
||
660 | postprocess_udt(accessor, a1); |
||
661 | } |
||
662 | |||
663 | template <typename Accessor, typename A, typename T = typename A::value_type> |
||
664 | static typename std::enable_if<!(sizeof(T) == 1 && std::is_integral<T>::value), void>::type process_array(Accessor &accessor, A &a) |
||
665 | { |
||
666 | range_for (auto &i, a) |
||
667 | process_buffer(accessor, i); |
||
668 | } |
||
669 | |||
670 | template <typename Accessor, typename A1> |
||
671 | static typename std::enable_if<is_cxx_array<A1>::value, void>::type process_buffer(Accessor &&accessor, A1 &a1) |
||
672 | { |
||
673 | process_array(std::forward<Accessor &&>(accessor), a1); |
||
674 | } |
||
675 | |||
676 | template <typename Accessor, typename... Args, std::size_t... N> |
||
677 | static inline void process_message_tuple(Accessor &&accessor, const std::tuple<Args...> &t, std::index_sequence<N...>) |
||
678 | { |
||
679 | (process_buffer(accessor, detail::extract_value(std::get<N>(t))), ...); |
||
680 | } |
||
681 | |||
682 | template <typename Accessor, typename... Args> |
||
683 | static void process_buffer(Accessor &&accessor, const message<Args...> &m) |
||
684 | { |
||
685 | process_message_tuple(std::forward<Accessor &&>(accessor), m.get_tuple(), std::make_index_sequence<sizeof...(Args)>()); |
||
686 | } |
||
687 | |||
688 | /* Require at least two arguments to prevent self-selection */ |
||
689 | template <typename Accessor, typename... An> |
||
690 | static typename std::enable_if<(sizeof...(An) > 1)>::type process_buffer(Accessor &&accessor, An &&... an) |
||
691 | { |
||
692 | (process_buffer(accessor, std::forward<An>(an)), ...); |
||
693 | } |
||
694 | |||
695 | } |