Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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. }
  696.