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