1 #ifndef _BITCPY_COMMON_H_
8 #define _BITCPY_COMMON_H_
13 #include <type_traits>
19 #ifndef CONSTEXPR_ABOVE_CPP11
20 #if defined(configBITCPY_DISABLE_CONSTEXPR) || !defined(__cplusplus) || (__cplusplus < 201402L)
21 #define BITCPY_CONSTEXPR_SUPPORTED 0
23 #define CONSTEXPR_ABOVE_CPP11
25 #define CONSTEXPR_ABOVE_CPP11_OR_INLINE inline
27 #define BITCPY_CONSTEXPR_SUPPORTED 1
29 #define CONSTEXPR_ABOVE_CPP11 constexpr
31 #define CONSTEXPR_ABOVE_CPP11_OR_INLINE constexpr
35 #if defined(_MSC_VER) && !defined(__attribute__)
36 #define __attribute__(x)
41 #ifndef CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE
42 #if defined(configBITCPY_DISABLE_CONSTEXPR_NON_LIT) || !defined(__cplusplus) || (__cplusplus < 201402L) || defined(__APPLE__)
43 #define BITCPY_CONSTEXPR_NON_LITERAL_STORAGE_SUPPORTED 0
45 #define CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE
48 #define BITCPY_CONSTEXPR_NON_LITERAL_STORAGE_SUPPORTED 1
50 #define CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE constexpr
55 #ifdef __SIZEOF_INT128__
56 #define BITCPY_INT128_CONDITIONAL_DEFINE(...) __VA_ARGS__
58 #define BITCPY_INT128_CONDITIONAL_DEFINE_C(...) __VA_ARGS__ static_assert(true, "")
61 #define BITCPY_INT128_CONDITIONAL_DEFINE(...)
63 #define BITCPY_INT128_CONDITIONAL_DEFINE_C(...) static_assert(true, "")
78 #define LIB_CPP_SERDES_VERSION_MAJOR 1
81 #define LIB_CPP_SERDES_VERSION_MINOR 3
85 #define LIB_CPP_SERDES_VERSION_MAJOR_MINOR(major, minor) (major * 1000 + minor)
88 #define LIB_CPP_SERDES_VERSION LIB_CPP_SERDES_VERSION_MAJOR_MINOR(LIB_CPP_SERDES_VERSION_MAJOR, LIB_CPP_SERDES_VERSION_MINOR)
101 return std::forward<T>(x);
109 template <
typename UnsupportedType>
110 constexpr
bool type_is_supported() {
return false; }
117 template <
typename UnsupportedType>
122 void has_no_override__DO_NOT_SPECIFY_THIS() {}
128 static constexpr
size_t default_bits = 0;
133 template <
typename UnsupportedTypeSpecialized>
134 static void format(
packet &pkt, UnsupportedTypeSpecialized &&item,
size_t bits)
137 detail::type_is_supported<UnsupportedType>(),
139 "--------------------------------------------------------------------------------------------------\n"
140 "UnsupportedType has no format definition (or bitpacking was used on a custom type with no bitpacking\n"
142 "Please add serialization support via one of the four options:\n"
143 " 1) Add 'T load() const' and 'void store(T val)' methods to your type, if it's a type wrapper such\n"
144 " as 'std::atomic<T>', CppSerdes will use the load/store during serializing/deserializing.\n"
145 " 2) Add a 'void format(serdes::packet&)' method to your type if you're allowed to modify the type\n"
146 " and want to keep the serialization format information co-located with the type.\n"
147 " 3) Inherit from 'serdes::packet_base' AND add a 'void format(serdes::packet&)' method.\n"
148 " This has the additional benefit compared to option #2 of adding extra\n"
149 " load, store, and stream (de)serialization operator methods. Note that there is no\n"
150 " memory downside to using packet_base - it is compatible with 'Empty Base Optimization'.\n"
151 " 4) Provide a custom_type<T> specialized definition for types you cannot modify. For example:\n"
152 " template<> struct serdes::custom_type<YOUR_CUSTOM_TYPE_NAME> {\n"
153 " template <typename T>\n"
154 " static void format(serdes::packet &pkt, T &&item) {\n"
155 " pkt + item.custom_stuff; // <-- example\n"
158 " NOTE: If you want your type to be able to support bit-packing (rare), you need this instead:\n"
159 " template<> struct serdes::custom_type<YOUR_CUSTOM_TYPE_NAME> {\n"
160 " static constexpr size_t default_bits = 7; // used when no bits are specified\n"
161 " template <typename T>\n"
162 " static void format(serdes::packet &pkt, T &&item, size_t bits) {\n"
163 " pkt + serdes::bitpack(item.custom_stuff, bits); // <-- example\n"
166 "--------------------------------------------------------------------------------------------------\n");
174 #if BITCPY_CONSTEXPR_SUPPORTED
175 constexpr
bool on_little_endian_platform()
178 inline bool on_little_endian_platform()
182 #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
184 #elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
186 #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64))
189 const uint16_t num = 1u;
190 return *
reinterpret_cast<const uint8_t *
>(&num) == 1u;
196 struct remove_cvref_cpp11
198 using type =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
202 template <
typename T>
203 using is_built_in_type =
204 std::integral_constant<bool,
205 std::is_pointer<typename remove_cvref_cpp11<T>::type>::value ||
206 std::is_arithmetic<typename remove_cvref_cpp11<T>::type>::value ||
207 std::is_integral<typename remove_cvref_cpp11<T>::type>::value ||
208 std::is_enum<typename remove_cvref_cpp11<T>::type>::value>;
211 template<
typename...>
using void_t_if_valid = void;
214 template <
typename,
typename =
void>
215 struct has_format_method : std::false_type
218 template <
typename T>
219 struct has_format_method<
222 decltype(std::declval<T>().format(std::declval<packet&>()))>>
228 template <
typename,
typename =
void>
229 struct has_custom_type_override : std::true_type
232 template <
typename T>
233 struct has_custom_type_override<
236 decltype(std::declval<custom_type<T>>().has_no_override__DO_NOT_SPECIFY_THIS())>>
242 template <
typename,
typename =
void>
243 struct has_custom_type_with_bit_support : std::false_type
246 template <
typename T>
247 struct has_custom_type_with_bit_support<
250 decltype(std::declval<custom_type<T>>().format(std::declval<packet&>(), std::declval<T>(), std::declval<size_t>()))>>
256 template <
typename,
typename =
void>
257 struct has_custom_type_without_bit_support : std::false_type
260 template <
typename T>
261 struct has_custom_type_without_bit_support<
264 decltype(std::declval<custom_type<T>>().format(std::declval<packet&>(), std::declval<T>()))>>
270 template <
typename T>
271 struct get_inner_wrapped_type { };
272 template <
template <
typename>
class T_wrapper,
typename T>
273 struct get_inner_wrapped_type<T_wrapper<T>>
279 template <
typename,
typename =
void>
280 struct has_load_and_store : std::false_type
283 template <
typename T,
template <
typename>
class T_wrapper>
284 struct has_load_and_store<
287 decltype(std::declval<T_wrapper<T>>().store(std::declval<T>())),
288 decltype(std::declval<const T_wrapper<T>>().load()),
289 typename std::enable_if<!has_format_method<T_wrapper<T>>::value>::type>>
295 template <
typename,
typename =
void>
296 struct has_load_and_store_of_builtin : std::false_type
299 template <
typename T,
template <
typename>
class T_wrapper>
300 struct has_load_and_store_of_builtin<
303 decltype(std::declval<T_wrapper<T>>().store(std::declval<T>())),
304 decltype(std::declval<const T_wrapper<T>>().load()),
305 typename std::enable_if<
306 !has_format_method<T_wrapper<T>>::value &&
307 is_built_in_type<T>::value
314 template <
typename T,
typename =
void>
315 struct has_data_and_size : std::false_type
319 template <
typename T>
320 struct has_data_and_size<
323 decltype(std::declval<typename remove_cvref_cpp11<T>::type>().data()),
324 decltype(std::declval<typename remove_cvref_cpp11<T>::type>().size()),
325 typename std::enable_if<!has_format_method<typename remove_cvref_cpp11<T>::type>::value>::type>>
328 using elem_type =
typename std::remove_reference<decltype(*std::declval<
typename std::remove_reference<T>::type>().data())>::type;
332 template <
typename T>
333 using supported_by_bitcpy =
334 std::integral_constant<bool,
335 is_built_in_type<T>::value ||
336 has_load_and_store_of_builtin<typename remove_cvref_cpp11<T>::type>::value>;
338 template <
typename T>
339 using requires_unsigned_type =
typename std::enable_if<
340 BITCPY_INT128_CONDITIONAL_DEFINE(
341 std::is_same<T, __uint128_t>::value ||)(!std::is_same<T, char>::value && std::is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_signed<T>::value) ||
342 (std::is_same<T, char>::value && std::is_same<char, uint8_t>::value),
344 template <
typename T>
345 using requires_bool_type =
typename std::enable_if<
346 std::is_same<T, bool>::value,
348 template <
typename T>
349 using requires_non_bool_type =
typename std::enable_if<
350 !std::is_same<T, bool>::value,
352 template <
typename T>
353 using requires_signed_type =
typename std::enable_if<
354 BITCPY_INT128_CONDITIONAL_DEFINE(std::is_same<T, __int128_t>::value ||)(!std::is_same<T, char>::value && std::is_integral<T>::value && !std::is_same<T, bool>::value && std::is_signed<T>::value) ||
355 (std::is_same<T, char>::value && !std::is_same<char, uint8_t>::value),
357 template <
typename T>
358 using requires_large_non_integral_type =
typename std::enable_if<
360 BITCPY_INT128_CONDITIONAL_DEFINE(!std::is_same<T, __uint128_t>::value &&
361 !std::is_same<T, __int128_t>::value &&) !is_sized_pointer<T>::value,
363 template <
typename T>
364 using requires_small_non_integral_type =
typename std::enable_if<
366 BITCPY_INT128_CONDITIONAL_DEFINE(!std::is_same<T, __uint128_t>::value &&
367 !std::is_same<T, __int128_t>::value &&) !std::is_integral<T>::value &&
368 !std::is_same<T, bool>::value &&
369 !has_load_and_store_of_builtin<T>::value &&
370 !is_sized_pointer<T>::value,
372 template <
typename T>
373 using requires_load_and_store_of_builtin =
typename std::enable_if<
374 has_load_and_store_of_builtin<T>::value,
376 template <
typename T>
377 using requires_data_and_size =
typename std::enable_if<
378 has_data_and_size<T>::value,
380 template <
typename T>
381 using requires_pointer_type =
typename std::enable_if<
382 std::is_pointer<T>::value,
384 template <
typename T>
385 using requires_not_pointer_or_load_store =
typename std::enable_if<
386 !std::is_pointer<T>::value &&
387 !has_load_and_store_of_builtin<T>::value,
391 struct unsigned_type_sizeof
395 struct unsigned_type_sizeof<1>
397 using type = uint8_t;
400 struct unsigned_type_sizeof<2>
402 using type = uint16_t;
405 struct unsigned_type_sizeof<4>
407 using type = uint32_t;
410 struct unsigned_type_sizeof<8>
412 using type = uint64_t;
414 BITCPY_INT128_CONDITIONAL_DEFINE_C(
416 struct unsigned_type_sizeof<16> {
417 using type = __uint128_t;
428 #ifdef configCPP_SERDES_ALLOW_REINTERPRET_BUILTINS_UB
429 template <
typename T>
430 __attribute__((
const)) const typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(const T &value) noexcept
432 return *
reinterpret_cast<const typename unsigned_type_sizeof<sizeof(T)
>::type *>(&value);
434 template <
typename T>
435 __attribute__((
const)) const volatile typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(const volatile T &value) noexcept
437 return *
reinterpret_cast<const volatile typename unsigned_type_sizeof<sizeof(T)
>::type *>(&value);
439 template <
typename T>
440 __attribute__((
const)) volatile typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(volatile T &value) noexcept
442 return *
reinterpret_cast<volatile typename unsigned_type_sizeof<sizeof(T)
>::type *>(&value);
444 template <
typename T>
445 __attribute__((
const)) typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(T &value) noexcept
447 return *
reinterpret_cast<typename unsigned_type_sizeof<sizeof(T)
>::type *>(&value);
449 #endif // #ifdef configCPP_SERDES_ALLOW_REINTERPRET_BUILTINS_UB
451 template <
typename T>
452 __attribute__((pure)) constexpr T bitmask(
const size_t onecount) noexcept
454 using Tunsigned =
typename std::make_unsigned<T>::type;
455 return static_cast<T
>((
static_cast<Tunsigned
>(~static_cast<Tunsigned>(0u)) >> ((
sizeof(Tunsigned) * 8u) - onecount)) *
static_cast<Tunsigned
>(onecount != 0));
457 BITCPY_INT128_CONDITIONAL_DEFINE_C(
459 __attribute__((pure)) constexpr __int128_t bitmask<__int128_t>(
const size_t onecount) noexcept {
460 using Tunsigned = __uint128_t;
461 return static_cast<Tunsigned
>(-(onecount != 0)) & (
static_cast<Tunsigned
>(-1) >> ((
sizeof(Tunsigned) * 8u) - onecount));
463 __attribute__((pure)) constexpr __uint128_t bitmask<__uint128_t>(
const size_t onecount) noexcept {
464 using Tunsigned = __uint128_t;
465 return static_cast<Tunsigned
>(-(onecount != 0)) & (
static_cast<Tunsigned
>(-1) >> ((
sizeof(Tunsigned) * 8u) - onecount));
467 template <
typename T, requires_
signed_type<T> * =
nullptr>
470 if (bits >=
sizeof(T) * 8u || bits == 0u)
472 const T m =
static_cast<T
>(1u) << (bits - 1u);
474 x = ((x & bitmask<T>(bits)) ^ m) - m;
476 template <
typename T,
typename =
void>
477 struct default_bitsize
479 static constexpr
size_t value =
sizeof(T) * 8u;
481 template <
typename T>
482 struct default_bitsize<T, typename std::enable_if<std::is_enum<T>::value>::type>
484 static constexpr
size_t value = default_bitsize<typename std::underlying_type<T>::type>::value;
487 struct default_bitsize<bool>
489 static constexpr
size_t value = 1u;
491 template <
typename T_val,
template <
typename>
class T_wrap>
492 struct default_bitsize<T_wrap<T_val>>
494 static constexpr
size_t value =
495 has_load_and_store<T_wrap<T_val>>::value
496 ? default_bitsize<T_val>::value
497 :
sizeof(T_wrap<T_val>) * 8u;
499 template <
typename T,
typename T2>
502 static_assert(
sizeof(T) == 0,
503 "Use big_endian_memcpy_unsigned instead. Either you tried to use a "
504 "signed type which is not supported, or a type larger than 128bits");
519 return static_cast<uint16_t
>(data[0]) << 8 |
static_cast<uint16_t
>(data[1]);
524 return (
static_cast<uint32_t
>(data[0]) << 24) |
525 (
static_cast<uint32_t
>(data[1]) << 16) |
526 (
static_cast<uint32_t
>(data[2]) << 8) |
527 static_cast<uint16_t
>(data[3]);
532 return (
static_cast<uint64_t
>(data[0]) << 56) |
533 (
static_cast<uint64_t
>(data[1]) << 48) |
534 (
static_cast<uint64_t
>(data[2]) << 40) |
535 (
static_cast<uint64_t
>(data[3]) << 32) |
536 (
static_cast<uint64_t
>(data[4]) << 24) |
537 (
static_cast<uint64_t
>(data[5]) << 16) |
538 (
static_cast<uint64_t
>(data[6]) << 8) |
539 static_cast<uint64_t
>(data[7]);
549 return static_cast<uint8_t
>(data[0] >> 8);
559 return (
static_cast<uint32_t
>(data[0]) << 16) |
static_cast<uint32_t
>(data[1]);
564 return (
static_cast<uint64_t
>(data[0]) << 48) |
565 (
static_cast<uint64_t
>(data[1]) << 32) |
566 (
static_cast<uint64_t
>(data[2]) << 16) |
567 static_cast<uint64_t
>(data[3]);
577 return static_cast<uint8_t
>(data[0] >> 24);
582 return static_cast<uint16_t
>(data[0] >> 16);
592 return (
static_cast<uint64_t
>(data[0]) << 32) |
593 static_cast<uint64_t
>(data[1]);
603 return static_cast<uint8_t
>(data[0] >> 56);
608 return static_cast<uint16_t
>(data[0] >> 48);
613 return static_cast<uint32_t
>(data[0] >> 32);
621 BITCPY_INT128_CONDITIONAL_DEFINE_C(
626 return (
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(data)) << 64) |
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(&data[8 /
sizeof(T2)]));
632 return (
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(data)) << 64) |
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(&data[8 /
sizeof(T2)]));
638 return (
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(data)) << 64) |
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(&data[8 /
sizeof(T2)]));
644 return (
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(data)) << 64) |
static_cast<__uint128_t
>(big_endian_memcpy<uint64_t, T2>(&data[8 /
sizeof(T2)]));
654 return static_cast<uint64_t
>(data[0] >> 64);
659 return static_cast<uint32_t
>(data[0] >> 96);
664 return static_cast<uint16_t
>(data[0] >> 112);
669 return static_cast<uint8_t
>(data[0] >> 120);
674 template <
typename T,
typename T2>
677 return big_endian_memcpy<
678 typename detail::unsigned_type_sizeof<
sizeof(T)>::type,
679 typename detail::unsigned_type_sizeof<
sizeof(T2)>::type>(v);
685 #endif // _BITCPY_COMMON_H_