Subversion Repositories Games.Descent

Rev

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
}