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