2014-06-01 17:55:23 +00:00
/*
2018-09-02 00:57:29 +00:00
* This file is part of the DXX - Rebirth project < https : //www.dxx-rebirth.com/>.
2014-06-01 17:55:23 +00:00
* It is copyright by its individual contributors , as recorded in the
* project ' s Git history . See COPYING . txt at the top level for license
* terms and a link to the Git history .
*/
2014-01-12 23:00:43 +00:00
# pragma once
# include <algorithm>
2022-09-24 17:47:52 +00:00
# include <bit>
2020-05-17 23:35:25 +00:00
# include <cassert>
2014-01-12 23:00:43 +00:00
# include <cstddef>
# include <cstdint>
# include <functional>
2014-07-02 03:08:44 +00:00
# include <initializer_list>
2014-01-12 23:00:43 +00:00
# include <tuple>
2017-06-25 20:46:03 +00:00
# include <type_traits>
2014-01-12 23:00:43 +00:00
# include "dxxsconf.h"
# include "compiler-range_for.h"
2014-04-27 03:07:24 +00:00
# include "compiler-static_assert.h"
2020-05-02 21:18:42 +00:00
# include <array>
2020-05-02 21:18:43 +00:00
# include <memory>
2020-04-26 17:26:23 +00:00
# include <utility>
2014-01-12 23:00:43 +00:00
namespace serial {
2020-05-17 23:35:25 +00:00
template < typename . . . Args >
2014-01-12 23:00:43 +00:00
class message ;
2017-07-26 03:15:59 +00:00
template < typename >
class message_type ;
2014-01-12 23:00:43 +00:00
/* Classifiers to identify whether a type is a message<...> */
template < typename >
2017-06-25 20:46:03 +00:00
class is_message : public std : : false_type
2014-01-12 23:00:43 +00:00
{
} ;
2020-05-17 23:35:25 +00:00
template < typename . . . Args >
class is_message < message < Args . . . > > : public std : : true_type
2014-01-12 23:00:43 +00:00
{
} ;
2017-06-25 20:46:03 +00:00
namespace detail {
2017-07-26 03:15:59 +00:00
template < std : : size_t maximum , std : : size_t minimum = maximum >
struct size_base
2017-06-25 20:46:03 +00:00
{
2017-08-11 23:43:54 +00:00
static constexpr std : : integral_constant < std : : size_t , maximum > maximum_size = { } ;
static constexpr std : : integral_constant < std : : size_t , minimum > minimum_size = { } ;
2017-06-25 20:46:03 +00:00
} ;
2017-07-26 03:15:59 +00:00
template < std : : size_t maximum , std : : size_t minimum >
2017-08-11 23:43:54 +00:00
constexpr std : : integral_constant < std : : size_t , maximum > size_base < maximum , minimum > : : maximum_size ;
2017-06-25 20:46:03 +00:00
2017-07-26 03:15:59 +00:00
template < std : : size_t maximum , std : : size_t minimum >
2017-08-11 23:43:54 +00:00
constexpr std : : integral_constant < std : : size_t , minimum > size_base < maximum , minimum > : : minimum_size ;
2017-06-25 20:46:03 +00:00
2017-07-26 03:15:59 +00:00
}
2014-01-12 23:00:43 +00:00
template < typename >
2017-06-25 20:46:03 +00:00
class is_cxx_array : public std : : false_type
2014-01-12 23:00:43 +00:00
{
} ;
template < typename T , std : : size_t N >
2020-05-02 21:18:42 +00:00
class is_cxx_array < std : : array < T , N > > : public std : : true_type
2014-01-12 23:00:43 +00:00
{
2021-09-19 10:53:48 +00:00
public :
using array_type = std : : array < T , N > ;
2014-01-12 23:00:43 +00:00
} ;
template < typename T >
class is_cxx_array < const T > : public is_cxx_array < T >
{
} ;
template < typename T >
2017-06-25 20:46:03 +00:00
using is_generic_class = typename std : : conditional < is_cxx_array < T > : : value , std : : false_type , std : : is_class < T > > : : type ;
2014-01-12 23:00:43 +00:00
2017-06-25 20:46:03 +00:00
template < typename Accessor , typename A1 >
2022-07-30 17:42:59 +00:00
requires ( std : : is_integral < typename std : : remove_reference < A1 > : : type > : : value )
static inline void process_buffer ( Accessor & & accessor , A1 & & a1 )
2017-06-25 20:46:03 +00:00
{
process_integer ( std : : forward < Accessor & & > ( accessor ) , a1 ) ;
}
2014-01-12 23:00:43 +00:00
2017-06-25 20:46:03 +00:00
template < typename Accessor , typename A1 , typename A1rr = typename std : : remove_reference < A1 > : : type >
2022-07-30 17:42:59 +00:00
requires ( std : : is_enum < A1rr > : : value )
static inline void process_buffer ( Accessor & , A1 & & ) ;
2014-01-12 23:00:43 +00:00
2017-06-25 20:46:03 +00:00
template < typename Accessor , typename A1 , typename A1rr = typename std : : remove_reference < A1 > : : type >
2022-07-30 17:42:59 +00:00
requires ( is_generic_class < A1rr > : : value )
static inline void process_buffer ( Accessor & , A1 & & ) ;
2014-01-12 23:00:43 +00:00
template < typename Accessor , typename A1 >
2022-07-30 17:42:59 +00:00
requires ( is_cxx_array < A1 > : : value )
static void process_buffer ( Accessor & & , A1 & ) ;
2014-01-12 23:00:43 +00:00
2020-05-17 23:35:25 +00:00
template < typename Accessor , typename . . . Args >
static void process_buffer ( Accessor & , const message < Args . . . > & ) ;
2014-01-12 23:00:43 +00:00
2015-06-05 02:34:40 +00:00
class endian_access
2014-09-04 01:53:11 +00:00
{
2015-06-05 02:34:40 +00:00
public :
2014-09-04 01:53:11 +00:00
/*
* Endian access modes :
* - little_endian : assume buffered data is little endian
* Copy on little endian host , byte swap on big endian host
* - big_endian : assume buffered data is big endian
* Copy on big endian host , byte swap on little endian host
* - native_endian : assume buffered data is native endian
* Copy regardless of host byte order
*/
2022-09-24 17:47:52 +00:00
using little_endian_type = std : : integral_constant < std : : endian , std : : endian : : little > ;
using big_endian_type = std : : integral_constant < std : : endian , std : : endian : : big > ;
using native_endian_type = std : : integral_constant < std : : endian , std : : endian : : native > ;
/* If this static_assert fails, then endian_skip_byteswap may return the
* wrong result .
*/
static_assert ( std : : endian : : little = = std : : endian : : native | | std : : endian : : big = = std : : endian : : native , " host byte order must be little endian or big endian " ) ;
2014-09-04 01:53:11 +00:00
} ;
2014-01-12 23:00:43 +00:00
/* Implementation details - avoid namespace pollution */
namespace detail {
2017-06-25 20:46:03 +00:00
template < typename T , typename Trr = typename std : : remove_reference < T > : : type >
using capture_type = typename std : : conditional < std : : is_lvalue_reference < T > : : value ,
2014-09-20 22:44:46 +00:00
std : : reference_wrapper < Trr > ,
2020-05-17 23:35:25 +00:00
std : : tuple < Trr >
2016-08-06 19:55:24 +00:00
> ;
2014-09-20 22:44:46 +00:00
2017-06-25 20:46:03 +00:00
template < typename T , typename Trr = typename std : : remove_reference < T > : : type >
2022-07-30 17:42:59 +00:00
static inline auto capture_value ( Trr & t )
2014-09-20 22:44:46 +00:00
{
return std : : ref ( t ) ;
}
2017-06-25 20:46:03 +00:00
template < typename T , typename Trr = typename std : : remove_reference < T > : : type >
2022-07-30 17:42:59 +00:00
requires ( std : : is_rvalue_reference < T > : : value )
static inline auto capture_value ( Trr & & t )
2014-09-20 22:44:46 +00:00
{
return std : : tuple < Trr > { std : : forward < T > ( t ) } ;
}
2017-01-21 19:05:43 +00:00
template < typename extended_signed_type , typename wrapped_type >
class sign_extend_type : std : : reference_wrapper < wrapped_type >
{
static_assert ( sizeof ( extended_signed_type ) > sizeof ( wrapped_type ) , " cannot sign-extend into a type of equal or smaller size " ) ;
static_assert ( std : : is_signed < extended_signed_type > : : value , " cannot sign-extend into an unsigned type " ) ;
using base_type = std : : reference_wrapper < wrapped_type > ;
public :
using base_type : : base_type ;
using base_type : : get ;
} ;
template < typename extended_signed_type , typename wrapped_type >
2020-05-02 21:18:42 +00:00
message < std : : array < uint8_t , sizeof ( extended_signed_type ) > > udt_to_message ( const sign_extend_type < extended_signed_type , wrapped_type > & ) ;
2017-01-21 19:05:43 +00:00
2014-02-09 18:03:41 +00:00
template < std : : size_t amount , uint8_t value >
2014-01-12 23:00:43 +00:00
class pad_type
{
} ;
2014-02-09 18:03:41 +00:00
template < std : : size_t amount , uint8_t value >
2020-05-02 21:18:42 +00:00
message < std : : array < uint8_t , amount > > udt_to_message ( const pad_type < amount , value > & ) ;
2014-01-12 23:00:43 +00:00
/*
* This can never be instantiated , but will be requested if a UDT
* specialization is missing .
*/
template < typename T >
2015-06-05 02:34:40 +00:00
class missing_udt_specialization
2014-01-12 23:00:43 +00:00
{
2015-06-05 02:34:40 +00:00
public :
2015-05-01 02:18:33 +00:00
missing_udt_specialization ( ) = delete ;
2014-01-12 23:00:43 +00:00
} ;
template < typename T >
void udt_to_message ( T & , missing_udt_specialization < T > = missing_udt_specialization < T > ( ) ) ;
template < typename Accessor , typename UDT >
void preprocess_udt ( Accessor & , UDT & ) { }
template < typename Accessor , typename UDT >
void postprocess_udt ( Accessor & , UDT & ) { }
template < typename Accessor , typename UDT >
2016-08-28 22:41:47 +00:00
static inline void process_udt ( Accessor & & accessor , UDT & udt )
2014-01-12 23:00:43 +00:00
{
2016-08-28 22:41:47 +00:00
process_buffer ( std : : forward < Accessor & & > ( accessor ) , udt_to_message ( udt ) ) ;
2014-01-12 23:00:43 +00:00
}
template < typename Accessor , typename E >
void check_enum ( Accessor & , E ) { }
template < typename T , typename D >
2020-05-02 21:18:43 +00:00
struct base_bytebuffer_t : endian_access
2014-01-12 23:00:43 +00:00
{
public :
2020-05-02 21:18:43 +00:00
using iterator_category = std : : random_access_iterator_tag ;
using value_type = T ;
using difference_type = std : : ptrdiff_t ;
using pointer = T * ;
using reference = T & ;
2014-09-04 01:53:11 +00:00
// Default bytebuffer_t usage to little endian
2020-05-17 23:35:25 +00:00
static constexpr endian_access : : little_endian_type endian { } ;
2014-01-12 23:00:43 +00:00
base_bytebuffer_t ( pointer u ) : p ( u ) { }
operator pointer ( ) const { return p ; }
2018-05-05 22:33:55 +00:00
D & operator + + ( )
{
+ + p ;
return * static_cast < D * > ( this ) ;
}
D & operator - - ( )
{
- - p ;
return * static_cast < D * > ( this ) ;
}
D & operator + = ( const difference_type d )
2014-01-12 23:00:43 +00:00
{
p + = d ;
return * static_cast < D * > ( this ) ;
}
2014-10-12 22:11:28 +00:00
operator const void * ( ) const = delete ;
2014-01-12 23:00:43 +00:00
protected :
pointer p ;
} ;
# define SERIAL_UDT_ROUND_UP(X,M) (((X) + (M) - 1) & ~((M) - 1))
2014-09-17 02:44:45 +00:00
template < std : : size_t amount ,
std : : size_t SERIAL_UDT_ROUND_MULTIPLIER = sizeof ( void * ) ,
std : : size_t SERIAL_UDT_ROUND_UP_AMOUNT = SERIAL_UDT_ROUND_UP ( amount , SERIAL_UDT_ROUND_MULTIPLIER ) ,
std : : size_t FULL_SIZE = amount / 64 ? 64 : SERIAL_UDT_ROUND_UP_AMOUNT ,
std : : size_t REMAINDER_SIZE = amount % 64 >
union pad_storage
{
2014-01-12 23:00:43 +00:00
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 " ) ;
static_assert ( SERIAL_UDT_ROUND_UP_AMOUNT % SERIAL_UDT_ROUND_MULTIPLIER = = 0 , " round modulus error " ) ;
2014-09-17 02:44:45 +00:00
static_assert ( amount % FULL_SIZE = = REMAINDER_SIZE | | FULL_SIZE = = REMAINDER_SIZE , " padding alignment error " ) ;
2020-05-02 21:18:42 +00:00
std : : array < uint8_t , FULL_SIZE > f ;
std : : array < uint8_t , REMAINDER_SIZE > p ;
2014-09-17 02:44:45 +00:00
# undef SERIAL_UDT_ROUND_UP
2014-05-31 22:59:34 +00:00
} ;
template < typename Accessor , std : : size_t amount , uint8_t value >
2016-08-28 22:41:47 +00:00
static inline void process_udt ( Accessor & & accessor , const pad_type < amount , value > & )
2014-05-31 22:59:34 +00:00
{
2014-09-17 02:44:45 +00:00
/* If reading from accessor, accessor data is const and buffer is
* overwritten by read .
* If writing to accessor , accessor data is non - const , so initialize
* buffer to be written .
*/
2020-05-17 23:35:25 +00:00
pad_storage < amount > s ;
if constexpr ( ! std : : is_const <
2016-08-28 22:41:47 +00:00
typename std : : remove_pointer <
/* rvalue reference `Accessor &&` causes `Accessor` to be `T &`
* for some type T . Use std : : remove_reference to get T . Then
* take the type ` pointer ` from type T to use as input to
* std : : remove_pointer .
*/
typename std : : remove_reference < Accessor > : : type
: : pointer
> : : type
2020-05-17 23:35:25 +00:00
> : : value )
s . f . fill ( value ) ;
2014-05-31 22:59:34 +00:00
for ( std : : size_t count = amount ; count ; count - = s . f . size ( ) )
2014-01-12 23:00:43 +00:00
{
2014-05-31 22:59:34 +00:00
if ( count < s . f . size ( ) )
2014-01-12 23:00:43 +00:00
{
2014-05-31 22:59:34 +00:00
assert ( count = = s . p . size ( ) ) ;
process_buffer ( accessor , s . p ) ;
2014-01-12 23:00:43 +00:00
break ;
}
2014-05-31 22:59:34 +00:00
process_buffer ( accessor , s . f ) ;
2014-01-12 23:00:43 +00:00
}
}
2014-09-20 22:44:46 +00:00
template < typename T >
static inline T & extract_value ( std : : reference_wrapper < T > t )
{
return t ;
}
template < typename T >
static inline T & extract_value ( std : : tuple < T > & t )
{
return std : : get < 0 > ( t ) ;
}
template < typename T >
static inline const T & extract_value ( const std : : tuple < T > & t )
{
return std : : get < 0 > ( t ) ;
}
2017-06-25 20:46:03 +00:00
template < typename T >
struct message_dispatch_base
{
using effective_type = T ;
} ;
2014-01-12 23:00:43 +00:00
}
2014-02-09 18:03:41 +00:00
template < std : : size_t amount , uint8_t value = 0xcc >
2016-08-28 22:41:46 +00:00
using pad = detail : : pad_type < amount , value > ;
2014-01-12 23:00:43 +00:00
2017-01-21 19:05:43 +00:00
template < typename extended_signed_type , typename wrapped_type >
static inline detail : : sign_extend_type < extended_signed_type , wrapped_type > sign_extend ( wrapped_type & t )
{
return { t } ;
}
2014-01-12 23:00:43 +00:00
# define DEFINE_SERIAL_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \
DEFINE_SERIAL_CONST_UDT_TO_MESSAGE ( TYPE , NAME , MEMBERLIST ) \
DEFINE_SERIAL_MUTABLE_UDT_TO_MESSAGE ( TYPE , NAME , MEMBERLIST ) \
# define _DEFINE_SERIAL_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \
2014-09-20 23:04:16 +00:00
template < typename Accessor > \
2016-08-28 22:41:47 +00:00
static inline void process_udt ( Accessor & & accessor , TYPE & NAME ) \
2014-09-20 23:04:16 +00:00
{ \
using serial : : process_buffer ; \
2016-08-28 22:41:47 +00:00
process_buffer ( std : : forward < Accessor & & > ( accessor ) , _SERIAL_UDT_UNWRAP_LIST MEMBERLIST ) ; \
2014-09-20 23:04:16 +00:00
} \
\
2015-02-05 03:03:50 +00:00
__attribute_unused \
2020-07-05 23:34:32 +00:00
static inline auto udt_to_message ( TYPE & NAME ) { \
return serial : : message MEMBERLIST ; \
2014-01-12 23:00:43 +00:00
}
# define DEFINE_SERIAL_CONST_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \
_DEFINE_SERIAL_UDT_TO_MESSAGE ( const TYPE , NAME , MEMBERLIST )
# define DEFINE_SERIAL_MUTABLE_UDT_TO_MESSAGE(TYPE, NAME, MEMBERLIST) \
_DEFINE_SERIAL_UDT_TO_MESSAGE ( TYPE , NAME , MEMBERLIST )
# define ASSERT_SERIAL_UDT_MESSAGE_SIZE(T, SIZE) \
2014-04-27 03:07:24 +00:00
assert_equal ( serial : : class_type < T > : : maximum_size , SIZE , " sizeof( " # T " ) is not " # SIZE )
2014-01-12 23:00:43 +00:00
2020-05-17 23:35:25 +00:00
template < typename M1 , typename T1 , typename base_type = std : : is_same < typename std : : remove_cv < typename std : : remove_reference < M1 > : : type > : : type , T1 > >
struct udt_message_compatible_same_type : base_type
2014-01-12 23:00:43 +00:00
{
2020-05-17 23:35:25 +00:00
static_assert ( base_type : : value , " parameter type mismatch " ) ;
2014-01-12 23:00:43 +00:00
} ;
template < bool , typename M , typename T >
class assert_udt_message_compatible2 ;
template < typename M , typename T >
2017-06-25 20:46:03 +00:00
class assert_udt_message_compatible2 < false , M , T > : public std : : false_type
2014-01-12 23:00:43 +00:00
{
} ;
2020-05-17 23:35:25 +00:00
template < typename . . . Mn , typename . . . Tn >
class assert_udt_message_compatible2 < true , message < Mn . . . > , std : : tuple < Tn . . . > > :
public std : : integral_constant < bool , ( udt_message_compatible_same_type < Mn , Tn > : : value & & . . . ) >
2014-01-12 23:00:43 +00:00
{
} ;
template < typename M , typename T >
2020-05-17 23:35:25 +00:00
class assert_udt_message_compatible ;
2014-01-12 23:00:43 +00:00
2020-05-17 23:35:25 +00:00
template < typename . . . Mn , typename . . . Tn >
class assert_udt_message_compatible < message < Mn . . . > , std : : tuple < Tn . . . > > : public assert_udt_message_compatible2 < sizeof . . . ( Mn ) = = sizeof . . . ( Tn ) , message < Mn . . . > , std : : tuple < Tn . . . > >
2014-01-12 23:00:43 +00:00
{
static_assert ( sizeof . . . ( Mn ) < = sizeof . . . ( Tn ) , " too few types in tuple " ) ;
static_assert ( sizeof . . . ( Mn ) > = sizeof . . . ( Tn ) , " too few types in message " ) ;
} ;
2017-07-26 03:15:59 +00:00
template < typename T >
using class_type = message_type < decltype ( udt_to_message ( std : : declval < T > ( ) ) ) > ;
2014-01-12 23:00:43 +00:00
# define _SERIAL_UDT_UNWRAP_LIST(A1,...) A1, ## __VA_ARGS__
# define ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) \
ASSERT_SERIAL_UDT_MESSAGE_CONST_TYPE ( T , TYPELIST ) ; \
ASSERT_SERIAL_UDT_MESSAGE_MUTABLE_TYPE ( T , TYPELIST ) ; \
# define _ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) \
2020-05-17 23:35:25 +00:00
static_assert ( serial : : assert_udt_message_compatible < typename class_type < T > : : as_message , std : : tuple < _SERIAL_UDT_UNWRAP_LIST TYPELIST > > : : value , " udt/message mismatch " )
2014-01-12 23:00:43 +00:00
# define ASSERT_SERIAL_UDT_MESSAGE_CONST_TYPE(T, TYPELIST) \
_ASSERT_SERIAL_UDT_MESSAGE_TYPE ( const T , TYPELIST )
# define ASSERT_SERIAL_UDT_MESSAGE_MUTABLE_TYPE(T, TYPELIST) \
_ASSERT_SERIAL_UDT_MESSAGE_TYPE ( T , TYPELIST )
2022-09-24 17:47:52 +00:00
static constexpr uint8_t endian_skip_byteswap ( std : : endian E )
2014-09-04 01:53:11 +00:00
{
2022-09-24 17:47:52 +00:00
return E = = std : : endian : : native ;
2014-09-04 01:53:11 +00:00
}
2014-09-04 01:57:04 +00:00
template < typename T , std : : size_t N >
union unaligned_storage
{
T a ;
2017-06-25 20:46:03 +00:00
typename std : : conditional < N < 4 ,
typename std : : conditional < N = = 1 , uint8_t , uint16_t > ,
typename std : : conditional < N = = 4 , uint32_t , uint64_t > > : : type : : type i ;
2014-09-04 01:57:04 +00:00
uint8_t u [ N ] ;
2015-07-18 21:01:55 +00:00
assert_equal ( sizeof ( i ) , N , " sizeof(i) is not N " ) ;
2014-09-04 01:57:04 +00:00
assert_equal ( sizeof ( a ) , sizeof ( u ) , " sizeof(T) is not N " ) ;
} ;
2014-08-17 23:24:24 +00:00
template < typename T , typename = void >
class message_dispatch_type ;
template < typename T >
2017-07-26 03:15:59 +00:00
class message_dispatch_type < T , typename std : : enable_if < std : : is_integral < T > : : value or std : : is_enum < T > : : value , void > : : type > :
public detail : : message_dispatch_base < detail : : size_base < sizeof ( T ) > >
2014-08-17 23:24:24 +00:00
{
} ;
template < typename T >
2017-06-25 20:46:03 +00:00
class message_dispatch_type < T , typename std : : enable_if < is_cxx_array < T > : : value , void > : : type > :
2017-07-26 03:15:59 +00:00
public detail : : message_dispatch_base <
detail : : size_base <
2021-09-19 10:53:48 +00:00
message_type < typename T : : value_type > : : maximum_size * std : : tuple_size < typename is_cxx_array < T > : : array_type > : : value ,
message_type < typename T : : value_type > : : minimum_size * std : : tuple_size < typename is_cxx_array < T > : : array_type > : : value
2017-07-26 03:15:59 +00:00
>
>
2014-08-17 23:24:24 +00:00
{
} ;
2014-01-12 23:00:43 +00:00
template < typename T >
2017-06-25 20:46:03 +00:00
class message_dispatch_type < T , typename std : : enable_if < is_generic_class < T > : : value & & ! is_message < T > : : value , void > : : type > :
public detail : : message_dispatch_base < class_type < T > >
2014-08-17 23:24:24 +00:00
{
} ;
template < typename T >
2017-06-25 20:46:03 +00:00
class message_type :
message_dispatch_type < typename std : : remove_reference < T > : : type > : : effective_type
2014-08-17 23:24:24 +00:00
{
2017-06-25 20:46:03 +00:00
using effective_type = typename message_dispatch_type < typename std : : remove_reference < T > : : type > : : effective_type ;
2014-01-12 23:00:43 +00:00
public :
2017-06-25 20:46:03 +00:00
using effective_type : : maximum_size ;
2017-07-26 03:15:59 +00:00
using effective_type : : minimum_size ;
2014-01-12 23:00:43 +00:00
} ;
2014-08-17 23:24:24 +00:00
template < typename A1 >
2017-06-25 20:46:03 +00:00
class message_dispatch_type < message < A1 > , void > :
public detail : : message_dispatch_base < message_type < A1 > >
2014-08-17 23:24:24 +00:00
{
2014-09-20 22:44:46 +00:00
public :
typedef message < A1 > as_message ;
2014-08-17 23:24:24 +00:00
} ;
2020-05-17 23:35:25 +00:00
template < typename . . . Args >
class message_type < message < Args . . . > > :
2017-07-26 03:15:59 +00:00
public detail : : size_base <
2020-05-17 23:35:25 +00:00
( 0 + . . . + message_dispatch_type < message < Args > > : : effective_type : : maximum_size ) ,
( 0 + . . . + message_dispatch_type < message < Args > > : : effective_type : : minimum_size )
2017-07-26 03:15:59 +00:00
>
2014-01-12 23:00:43 +00:00
{
public :
2020-05-17 23:35:25 +00:00
using as_message = message < Args . . . > ;
2014-01-12 23:00:43 +00:00
} ;
2020-05-17 23:35:25 +00:00
template < typename . . . Args >
2014-01-12 23:00:43 +00:00
class message
{
2020-05-17 23:35:25 +00:00
static_assert ( sizeof . . . ( Args ) > 0 , " message must have at least one template argument " ) ;
using tuple_type = std : : tuple < typename detail : : capture_type < Args & & > : : type . . . > ;
2014-01-12 23:00:43 +00:00
template < typename T1 >
static void check_type ( )
{
static_assert ( message_type < T1 > : : maximum_size > 0 , " empty field in message " ) ;
}
tuple_type t ;
public :
2020-05-17 23:35:25 +00:00
message ( Args & & . . . args ) :
t ( detail : : capture_value < Args > ( std : : forward < Args > ( args ) ) . . . )
2014-01-12 23:00:43 +00:00
{
2020-05-17 23:35:25 +00:00
( check_type < Args > ( ) , . . . ) ;
2014-01-12 23:00:43 +00:00
}
const tuple_type & get_tuple ( ) const
{
return t ;
}
} ;
2020-05-17 23:35:25 +00:00
template < typename . . . Args >
2020-07-05 23:34:32 +00:00
message ( Args & & . . . args ) - > message < Args & & . . . > ;
2014-01-12 23:00:43 +00:00
2022-04-17 22:27:19 +00:00
template < typename T >
static constexpr T bswap ( const T u )
{
if constexpr ( std : : is_same < T , int8_t > : : value | | std : : is_same < T , uint8_t > : : value )
/* Swapping a byte-sized value is a no-op. This is permitted here so
* that callers can swap without checking the size of the value .
*/
return u ;
if constexpr ( std : : is_same < T , int16_t > : : value | | std : : is_same < T , uint16_t > : : value )
{
# ifdef DXX_HAVE_BUILTIN_BSWAP
return __builtin_bswap16 ( u ) ;
# else
return ( static_cast < T > ( static_cast < uint8_t > ( u ) ) < < 8 ) | static_cast < T > ( static_cast < uint8_t > ( u > > 8 ) ) ;
# endif
2014-09-20 22:15:32 +00:00
}
2022-04-17 22:27:19 +00:00
if constexpr ( std : : is_same < T , int32_t > : : value | | std : : is_same < T , uint32_t > : : value )
{
# ifdef DXX_HAVE_BUILTIN_BSWAP
return __builtin_bswap32 ( u ) ;
# else
return ( static_cast < T > ( bswap ( static_cast < uint16_t > ( u ) ) ) < < 16 ) | static_cast < T > ( bswap ( static_cast < uint16_t > ( u > > 16 ) ) ) ;
# endif
2014-09-20 22:15:32 +00:00
}
2022-04-17 22:27:19 +00:00
if constexpr ( std : : is_same < T , int64_t > : : value | | std : : is_same < T , uint64_t > : : value )
{
# ifdef DXX_HAVE_BUILTIN_BSWAP
return __builtin_bswap64 ( u ) ;
# else
return ( static_cast < T > ( bswap ( static_cast < uint32_t > ( u ) ) ) < < 32 ) | static_cast < T > ( bswap ( static_cast < uint32_t > ( u > > 32 ) ) ) ;
# endif
2014-09-20 22:15:32 +00:00
}
2022-04-17 22:27:19 +00:00
/* Unsupported type. Fall off the end of the function and trigger a
* compile error due to the missing ` return ` statement .
*/
2014-09-20 22:15:32 +00:00
}
2022-04-17 22:27:19 +00:00
assert_equal ( bswap ( static_cast < uint8_t > ( 1 ) ) , 1 , " " ) ;
2014-09-20 22:15:32 +00:00
2022-04-17 22:27:19 +00:00
assert_equal ( bswap ( static_cast < uint16_t > ( 0x12 ) ) , 0x1200 , " " ) ;
assert_equal ( bswap ( static_cast < uint16_t > ( 0x92 ) ) , 0x9200 , " " ) ;
assert_equal ( bswap ( static_cast < uint16_t > ( 0x9200 ) ) , 0x92 , " " ) ;
assert_equal ( bswap ( static_cast < uint16_t > ( 0x102 ) ) , 0x201 , " " ) ;
2014-09-20 22:15:32 +00:00
2022-04-17 22:27:19 +00:00
assert_equal ( bswap ( static_cast < uint32_t > ( 0x102 ) ) , 0x2010000 , " " ) ;
2014-09-20 22:15:32 +00:00
2022-04-17 22:27:19 +00:00
assert_equal ( bswap ( static_cast < uint64_t > ( 0x102 ) ) , 0x201000000000000ull , " " ) ;
2014-09-20 22:15:32 +00:00
2014-01-12 23:00:43 +00:00
namespace reader {
class bytebuffer_t : public detail : : base_bytebuffer_t < const uint8_t , bytebuffer_t >
{
public :
2020-08-24 01:31:28 +00:00
using base_bytebuffer_t : : base_bytebuffer_t ;
2014-09-02 22:11:31 +00:00
explicit bytebuffer_t ( const bytebuffer_t & ) = default ;
bytebuffer_t ( bytebuffer_t & & ) = default ;
2014-01-12 23:00:43 +00:00
} ;
2015-07-18 21:01:55 +00:00
template < typename A1 , std : : size_t BYTES >
static inline void unaligned_copy ( const uint8_t * src , unaligned_storage < A1 , BYTES > & dst )
{
2020-05-17 23:35:25 +00:00
if constexpr ( BYTES = = 1 )
dst . u [ 0 ] = * src ;
else
std : : copy_n ( src , sizeof ( dst . u ) , dst . u ) ;
2015-07-18 21:01:55 +00:00
}
2014-09-20 22:15:32 +00:00
2014-01-12 23:00:43 +00:00
template < typename Accessor , typename A1 >
static inline void process_integer ( Accessor & buffer , A1 & a1 )
{
2014-09-04 01:57:04 +00:00
using std : : advance ;
unaligned_storage < A1 , message_type < A1 > : : maximum_size > u ;
2014-09-20 22:15:32 +00:00
unaligned_copy ( buffer , u ) ;
2015-07-18 21:01:55 +00:00
if ( ! endian_skip_byteswap ( buffer . endian ( ) ) )
u . i = bswap ( u . i ) ;
a1 = u . a ;
2014-09-04 01:57:04 +00:00
advance ( buffer , sizeof ( u . u ) ) ;
2014-01-12 23:00:43 +00:00
}
2014-09-20 18:14:00 +00:00
template < typename Accessor , typename A , typename T = typename A : : value_type >
2022-07-30 17:42:59 +00:00
requires ( sizeof ( T ) = = 1 & & std : : is_integral < T > : : value )
static inline void process_array ( Accessor & accessor , A & a )
2014-09-20 18:14:00 +00:00
{
2020-05-02 21:18:43 +00:00
using std : : advance ;
2014-09-20 18:14:00 +00:00
std : : copy_n ( static_cast < typename Accessor : : pointer > ( accessor ) , a . size ( ) , & a [ 0 ] ) ;
advance ( accessor , a . size ( ) ) ;
}
2017-01-21 19:05:43 +00:00
template < typename Accessor , typename extended_signed_type , typename wrapped_type >
static inline void process_udt ( Accessor & & accessor , const detail : : sign_extend_type < extended_signed_type , wrapped_type > & v )
{
extended_signed_type est ;
process_integer < Accessor , extended_signed_type > ( static_cast < Accessor & & > ( accessor ) , est ) ;
v . get ( ) = static_cast < wrapped_type > ( est ) ;
}
2014-01-12 23:00:43 +00:00
}
namespace writer {
class bytebuffer_t : public detail : : base_bytebuffer_t < uint8_t , bytebuffer_t >
{
public :
2020-08-24 01:31:28 +00:00
using base_bytebuffer_t : : base_bytebuffer_t ;
2014-09-02 22:11:31 +00:00
explicit bytebuffer_t ( const bytebuffer_t & ) = default ;
bytebuffer_t ( bytebuffer_t & & ) = default ;
2014-01-12 23:00:43 +00:00
} ;
2020-05-17 23:35:25 +00:00
/* If unaligned_copy is manually inlined into the caller, then gcc
* inlining of copy_n creates a loop instead of a store .
2014-09-20 22:15:32 +00:00
*/
2015-07-18 21:01:55 +00:00
template < typename A1 , std : : size_t BYTES >
static inline void unaligned_copy ( const unaligned_storage < A1 , BYTES > & src , uint8_t * dst )
{
2020-05-17 23:35:25 +00:00
if constexpr ( BYTES = = 1 )
* dst = src . u [ 0 ] ;
else
std : : copy_n ( src . u , sizeof ( src . u ) , dst ) ;
2015-07-18 21:01:55 +00:00
}
2014-09-20 22:15:32 +00:00
2014-01-12 23:00:43 +00:00
template < typename Accessor , typename A1 >
static inline void process_integer ( Accessor & buffer , const A1 & a1 )
{
2014-09-04 01:57:04 +00:00
using std : : advance ;
2015-07-18 21:01:55 +00:00
unaligned_storage < A1 , message_type < A1 > : : maximum_size > u { a1 } ;
if ( ! endian_skip_byteswap ( buffer . endian ( ) ) )
u . i = bswap ( u . i ) ;
2014-09-20 22:15:32 +00:00
unaligned_copy ( u , buffer ) ;
2014-09-04 01:57:04 +00:00
advance ( buffer , sizeof ( u . u ) ) ;
2014-01-12 23:00:43 +00:00
}
2014-09-20 18:14:00 +00:00
template < typename Accessor , typename A , typename T = typename A : : value_type >
2022-07-30 17:42:59 +00:00
requires ( sizeof ( T ) = = 1 & & std : : is_integral < T > : : value )
static inline void process_array ( Accessor & accessor , const A & a )
2014-09-20 18:14:00 +00:00
{
2020-05-02 21:18:43 +00:00
using std : : advance ;
2014-09-20 18:14:00 +00:00
std : : copy_n ( & a [ 0 ] , a . size ( ) , static_cast < typename Accessor : : pointer > ( accessor ) ) ;
advance ( accessor , a . size ( ) ) ;
}
2017-01-21 19:05:43 +00:00
template < typename Accessor , typename extended_signed_type , typename wrapped_type >
static inline void process_udt ( Accessor & & accessor , const detail : : sign_extend_type < extended_signed_type , const wrapped_type > & v )
{
const typename std : : make_signed < wrapped_type > : : type swt = v . get ( ) ;
const extended_signed_type est = swt ;
process_integer < Accessor , extended_signed_type > ( static_cast < Accessor & & > ( accessor ) , est ) ;
}
2014-01-12 23:00:43 +00:00
}
2014-09-20 23:04:16 +00:00
template < typename Accessor , typename A1 , typename A1rr >
2022-07-30 17:42:59 +00:00
requires ( std : : is_enum < A1rr > : : value )
static inline void process_buffer ( Accessor & accessor , A1 & & a1 )
2014-01-12 23:00:43 +00:00
{
using detail : : check_enum ;
process_integer ( accessor , a1 ) ;
/* Hook for enum types to check that the given value is legal */
check_enum ( accessor , a1 ) ;
}
2014-09-20 23:04:16 +00:00
template < typename Accessor , typename A1 , typename A1rr >
2022-07-30 17:42:59 +00:00
requires ( is_generic_class < A1rr > : : value )
static inline void process_buffer ( Accessor & accessor , A1 & & a1 )
2014-01-12 23:00:43 +00:00
{
using detail : : preprocess_udt ;
using detail : : process_udt ;
using detail : : postprocess_udt ;
preprocess_udt ( accessor , a1 ) ;
2014-09-20 23:04:16 +00:00
process_udt ( accessor , std : : forward < A1 > ( a1 ) ) ;
2014-01-12 23:00:43 +00:00
postprocess_udt ( accessor , a1 ) ;
}
2014-09-20 18:14:00 +00:00
template < typename Accessor , typename A , typename T = typename A : : value_type >
2022-07-30 17:42:59 +00:00
requires ( ! ( sizeof ( T ) = = 1 & & std : : is_integral < T > : : value ) )
static void process_array ( Accessor & accessor , A & a )
2014-09-20 18:14:00 +00:00
{
range_for ( auto & i , a )
process_buffer ( accessor , i ) ;
}
2014-01-12 23:00:43 +00:00
template < typename Accessor , typename A1 >
2022-07-30 17:42:59 +00:00
requires ( is_cxx_array < A1 > : : value )
static void process_buffer ( Accessor & & accessor , A1 & a1 )
2014-01-12 23:00:43 +00:00
{
2016-08-28 22:41:47 +00:00
process_array ( std : : forward < Accessor & & > ( accessor ) , a1 ) ;
2014-01-12 23:00:43 +00:00
}
2014-07-02 03:08:44 +00:00
template < typename Accessor , typename . . . Args , std : : size_t . . . N >
2020-04-26 17:26:23 +00:00
static inline void process_message_tuple ( Accessor & & accessor , const std : : tuple < Args . . . > & t , std : : index_sequence < N . . . > )
2014-01-12 23:00:43 +00:00
{
2020-05-17 23:35:25 +00:00
( process_buffer ( accessor , detail : : extract_value ( std : : get < N > ( t ) ) ) , . . . ) ;
2014-01-12 23:00:43 +00:00
}
2020-05-17 23:35:25 +00:00
template < typename Accessor , typename . . . Args >
static void process_buffer ( Accessor & & accessor , const message < Args . . . > & m )
2014-01-12 23:00:43 +00:00
{
2020-05-17 23:35:25 +00:00
process_message_tuple ( std : : forward < Accessor & & > ( accessor ) , m . get_tuple ( ) , std : : make_index_sequence < sizeof . . . ( Args ) > ( ) ) ;
2014-01-12 23:00:43 +00:00
}
2014-09-20 23:04:16 +00:00
/* Require at least two arguments to prevent self-selection */
2020-05-17 23:35:25 +00:00
template < typename Accessor , typename . . . An >
2022-07-30 17:42:59 +00:00
requires ( sizeof . . . ( An ) > 1 )
static void process_buffer ( Accessor & & accessor , An & & . . . an )
2020-05-17 23:35:25 +00:00
{
( process_buffer ( accessor , std : : forward < An > ( an ) ) , . . . ) ;
2014-09-20 23:04:16 +00:00
}
2014-01-12 23:00:43 +00:00
}