CppSerdes  1.0
A serialization/deserialization library designed with embedded systems in mind
bitcpy_common.h
Go to the documentation of this file.
1 #ifndef _BITCPY_COMMON_H_
8 #define _BITCPY_COMMON_H_
9 
10 // clang-format off
11 #include <stdint.h>
12 #include <cstddef>
13 #include <type_traits>
14 #include <utility>
15 #include <cstring>
16 #include "bitcpy_sized_pointer.h"
17 
18 // use the compilation flag "-DconfigBITCPY_DISABLE_CONSTEXPR" to disable this
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
26 #else
27 #define BITCPY_CONSTEXPR_SUPPORTED 1
29 #define CONSTEXPR_ABOVE_CPP11 constexpr
31 #define CONSTEXPR_ABOVE_CPP11_OR_INLINE constexpr
32 #endif
33 #endif
34 
35 #if defined(_MSC_VER) && !defined(__attribute__)
36 #define __attribute__(x)
37 #endif
38 
39 // use the compilation flag "-DconfigBITCPY_DISABLE_CONSTEXPR_NON_LIT" to disable this
40 // NOTE: "Non-literal variables (and labels and gotos) in constexpr functions" not suppored by apple: https://en.cppreference.com/w/cpp/compiler_support#cpp14
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
47 #else
48 #define BITCPY_CONSTEXPR_NON_LITERAL_STORAGE_SUPPORTED 1
50 #define CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE constexpr
52 #endif
53 #endif
54 
55 #ifdef __SIZEOF_INT128__
56 #define BITCPY_INT128_CONDITIONAL_DEFINE(...) __VA_ARGS__
58 #define BITCPY_INT128_CONDITIONAL_DEFINE_C(...) __VA_ARGS__ static_assert(true, "")
60 #else
61 #define BITCPY_INT128_CONDITIONAL_DEFINE(...)
63 #define BITCPY_INT128_CONDITIONAL_DEFINE_C(...) static_assert(true, "")
65 #endif
66 
68 namespace serdes
69 {
70 
72  struct info
73  {
75  static constexpr float version = 1.3f;
76 
78  #define LIB_CPP_SERDES_VERSION_MAJOR 1
79 
81  #define LIB_CPP_SERDES_VERSION_MINOR 3
82 
85  #define LIB_CPP_SERDES_VERSION_MAJOR_MINOR(major, minor) (major * 1000 + minor)
86 
88  #define LIB_CPP_SERDES_VERSION LIB_CPP_SERDES_VERSION_MAJOR_MINOR(LIB_CPP_SERDES_VERSION_MAJOR, LIB_CPP_SERDES_VERSION_MINOR)
89 
90  // Example of how to check version using a #if:
91  // #if LIB_CPP_SERDES_VERSION >= LIB_CPP_SERDES_VERSION_MAJOR_MINOR(1, 1)
92  };
93 
98  template <typename T>
99  constexpr T bit_length(T &&x) noexcept
100  {
101  return std::forward<T>(x);
102  }
103 
104  // implementation details
105  namespace detail
106  {
107  // always returns false if included - this is just used to highlight type name in
108  // static_assert failure message to make debugging easier
109  template <typename UnsupportedType>
110  constexpr bool type_is_supported() { return false; }
111  } // namespace detail
112 
113  struct packet;
114 
117  template <typename UnsupportedType>
118  struct custom_type
119  {
120  // used to detect if a type has a custom override (if this method is removed it means it has an override)
121  // so you must not specify this method name in any custom type override.
122  void has_no_override__DO_NOT_SPECIFY_THIS() {}
123 
124  // optional default bit size needed for custom non-compointtypes, where sizeof(T)*8 won't
125  // accurately determine the serialized bit size. If the default_bits setting isn't specified,
126  // and the "size_t bits" argument in the format method below isn't specified, then bitpacking
127  // will not be supported by the custom type.
128  static constexpr size_t default_bits = 0;
129 
130  // a overloadable definition for how to format some item into/out-of a serial packet,
131  // this default method will emit a compiler error if it's ever used, indicating a type
132  // has no definition for how it should be formatted into/out-of a serial packet, and one should be added.
133  template <typename UnsupportedTypeSpecialized>
134  static void format(packet &pkt, UnsupportedTypeSpecialized &&item, size_t bits)
135  {
136  static_assert(
137  detail::type_is_supported<UnsupportedType>(),
138  "\n"
139  "--------------------------------------------------------------------------------------------------\n"
140  "UnsupportedType has no format definition (or bitpacking was used on a custom type with no bitpacking\n"
141  " support)!\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"
156  " }\n"
157  " }\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"
164  " }\n"
165  " }\n"
166  "--------------------------------------------------------------------------------------------------\n");
167  }
168  };
169 
170  // implementation details
171  namespace detail
172  {
173 
174 #if BITCPY_CONSTEXPR_SUPPORTED
175  constexpr bool on_little_endian_platform()
177 #else
178  inline bool on_little_endian_platform()
180 #endif
181  {
182 #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
183  return false;
184 #elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
185  return true;
186 #elif defined(_MSC_VER) && (defined(_M_AMD64) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_X64))
187  return true;
188 #else
189  const uint16_t num = 1u;
190  return *reinterpret_cast<const uint8_t *>(&num) == 1u;
191 #endif
192  }
193 
195  template <class T>
196  struct remove_cvref_cpp11
197  {
198  using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
199  };
200 
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>;
209 
211  template<typename...> using void_t_if_valid = void;
212 
214  template <typename, typename = void>
215  struct has_format_method : std::false_type
216  {
217  };
218  template <typename T>
219  struct has_format_method<
220  T,
221  void_t_if_valid<
222  decltype(std::declval<T>().format(std::declval<packet&>()))>>
223  : std::true_type
224  {
225  };
226 
228  template <typename, typename = void>
229  struct has_custom_type_override : std::true_type
230  {
231  };
232  template <typename T>
233  struct has_custom_type_override<
234  T,
235  void_t_if_valid<
236  decltype(std::declval<custom_type<T>>().has_no_override__DO_NOT_SPECIFY_THIS())>>
237  : std::false_type
238  {
239  };
240 
242  template <typename, typename = void>
243  struct has_custom_type_with_bit_support : std::false_type
244  {
245  };
246  template <typename T>
247  struct has_custom_type_with_bit_support<
248  T,
249  void_t_if_valid<
250  decltype(std::declval<custom_type<T>>().format(std::declval<packet&>(), std::declval<T>(), std::declval<size_t>()))>>
251  : std::true_type
252  {
253  };
254 
256  template <typename, typename = void>
257  struct has_custom_type_without_bit_support : std::false_type
258  {
259  };
260  template <typename T>
261  struct has_custom_type_without_bit_support<
262  T,
263  void_t_if_valid<
264  decltype(std::declval<custom_type<T>>().format(std::declval<packet&>(), std::declval<T>()))>>
265  : std::true_type
266  {
267  };
268 
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>>
274  {
275  using type = T;
276  };
277 
279  template <typename, typename = void>
280  struct has_load_and_store : std::false_type
281  {
282  };
283  template <typename T, template <typename> class T_wrapper>
284  struct has_load_and_store<
285  T_wrapper<T>,
286  void_t_if_valid<
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>>
290  : std::true_type
291  {
292  };
293 
295  template <typename, typename = void>
296  struct has_load_and_store_of_builtin : std::false_type
297  {
298  };
299  template <typename T, template <typename> class T_wrapper>
300  struct has_load_and_store_of_builtin<
301  T_wrapper<T>,
302  void_t_if_valid<
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
308  >::type>>
309  : std::true_type
310  {
311  };
312 
314  template <typename T, typename = void>
315  struct has_data_and_size : std::false_type
316  {
317  using elem_type = T;
318  };
319  template <typename T>
320  struct has_data_and_size<
321  T,
322  void_t_if_valid<
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>>
326  : std::true_type
327  {
328  using elem_type = typename std::remove_reference<decltype(*std::declval<typename std::remove_reference<T>::type>().data())>::type;
329  };
330 
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>;
337 
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), // char is a special case, reinterpret it to uint8_t to reduce the template code generated if != uint8_t
343  T>::type;
344  template <typename T>
345  using requires_bool_type = typename std::enable_if<
346  std::is_same<T, bool>::value,
347  T>::type;
348  template <typename T>
349  using requires_non_bool_type = typename std::enable_if<
350  !std::is_same<T, bool>::value,
351  T>::type;
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), // char is a special case, reinterpret it to uint8_t to reduce the template code generated if != uint8_t
356  T>::type;
357  template <typename T>
358  using requires_large_non_integral_type = typename std::enable_if<
359  (sizeof(T) > 8) &&
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,
362  T>::type;
363  template <typename T>
364  using requires_small_non_integral_type = typename std::enable_if<
365  (sizeof(T) <= 8) &&
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,
371  T>::type;
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,
375  T>::type;
376  template <typename T>
377  using requires_data_and_size = typename std::enable_if<
378  has_data_and_size<T>::value,
379  T>::type;
380  template <typename T>
381  using requires_pointer_type = typename std::enable_if<
382  std::is_pointer<T>::value,
383  T>::type;
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,
388  T>::type;
389 
390  template <size_t N>
391  struct unsigned_type_sizeof
392  {
393  };
394  template <>
395  struct unsigned_type_sizeof<1>
396  {
397  using type = uint8_t;
398  };
399  template <>
400  struct unsigned_type_sizeof<2>
401  {
402  using type = uint16_t;
403  };
404  template <>
405  struct unsigned_type_sizeof<4>
406  {
407  using type = uint32_t;
408  };
409  template <>
410  struct unsigned_type_sizeof<8>
411  {
412  using type = uint64_t;
413  };
414  BITCPY_INT128_CONDITIONAL_DEFINE_C(
415  template <>
416  struct unsigned_type_sizeof<16> {
417  using type = __uint128_t;
418  };);
419 
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
431  {
432  return *reinterpret_cast<const typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
433  }
434  template <typename T>
435  __attribute__((const)) const volatile typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(const volatile T &value) noexcept
436  {
437  return *reinterpret_cast<const volatile typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
438  }
439  template <typename T>
440  __attribute__((const)) volatile typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(volatile T &value) noexcept
441  {
442  return *reinterpret_cast<volatile typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
443  }
444  template <typename T>
445  __attribute__((const)) typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(T &value) noexcept
446  {
447  return *reinterpret_cast<typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
448  }
449 #endif // #ifdef configCPP_SERDES_ALLOW_REINTERPRET_BUILTINS_UB
450 
451  template <typename T>
452  __attribute__((pure)) constexpr T bitmask(const size_t onecount) noexcept
453  {
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));
456  }
457  BITCPY_INT128_CONDITIONAL_DEFINE_C(
458  template <>
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));
462  } template <>
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));
466  });
467  template <typename T, requires_signed_type<T> * = nullptr>
468  CONSTEXPR_ABOVE_CPP11 void extend_sign(T &x, const size_t bits) noexcept
469  {
470  if (bits >= sizeof(T) * 8u || bits == 0u)
471  return;
472  const T m = static_cast<T>(1u) << (bits - 1u);
473  // faster than if-branch checking for the sign bit
474  x = ((x & bitmask<T>(bits)) ^ m) - m;
475  }
476  template <typename T, typename = void>
477  struct default_bitsize
478  {
479  static constexpr size_t value = sizeof(T) * 8u;
480  };
481  template <typename T>
482  struct default_bitsize<T, typename std::enable_if<std::is_enum<T>::value>::type>
483  {
484  static constexpr size_t value = default_bitsize<typename std::underlying_type<T>::type>::value;
485  };
486  template <>
487  struct default_bitsize<bool>
488  {
489  static constexpr size_t value = 1u;
490  };
491  template <typename T_val, template <typename> class T_wrap>
492  struct default_bitsize<T_wrap<T_val>>
493  {
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;
498  };
499  template <typename T, typename T2>
500  CONSTEXPR_ABOVE_CPP11 inline T big_endian_memcpy(const T2 *const)
501  {
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");
505  }
506 
507  //
508  // uint8_t[] section
509  //
510 
511  template <>
512  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint8_t *const data)
513  {
514  return data[0];
515  }
516  template <>
517  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint8_t *const data)
518  {
519  return static_cast<uint16_t>(data[0]) << 8 | static_cast<uint16_t>(data[1]);
520  }
521  template <>
522  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint8_t *const data)
523  {
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]);
528  }
529  template <>
530  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint8_t *const data)
531  {
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]);
540  }
541 
542  //
543  // uint16_t[] section
544  //
545 
546  template <>
547  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint16_t *const data)
548  {
549  return static_cast<uint8_t>(data[0] >> 8);
550  }
551  template <>
552  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint16_t *const data)
553  {
554  return data[0];
555  }
556  template <>
557  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint16_t *const data)
558  {
559  return (static_cast<uint32_t>(data[0]) << 16) | static_cast<uint32_t>(data[1]);
560  }
561  template <>
562  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint16_t *const data)
563  {
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]);
568  }
569 
570  //
571  // uint32_t[] section
572  //
573 
574  template <>
575  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint32_t *const data)
576  {
577  return static_cast<uint8_t>(data[0] >> 24);
578  }
579  template <>
580  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint32_t *const data)
581  {
582  return static_cast<uint16_t>(data[0] >> 16);
583  }
584  template <>
585  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint32_t *const data)
586  {
587  return data[0];
588  }
589  template <>
590  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint32_t *const data)
591  {
592  return (static_cast<uint64_t>(data[0]) << 32) |
593  static_cast<uint64_t>(data[1]);
594  }
595 
596  //
597  // uint64_t[] section
598  //
599 
600  template <>
601  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint64_t *const data)
602  {
603  return static_cast<uint8_t>(data[0] >> 56);
604  }
605  template <>
606  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint64_t *const data)
607  {
608  return static_cast<uint16_t>(data[0] >> 48);
609  }
610  template <>
611  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint64_t *const data)
612  {
613  return static_cast<uint32_t>(data[0] >> 32);
614  }
615  template <>
616  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint64_t *const data)
617  {
618  return data[0];
619  }
620 
621  BITCPY_INT128_CONDITIONAL_DEFINE_C(
622 
623  template <>
624  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint8_t *const data) {
625  using T2 = uint8_t;
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)]));
627  }
628 
629  template <>
630  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint16_t *const data) {
631  using T2 = uint16_t;
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)]));
633  }
634 
635  template <>
636  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint32_t *const data) {
637  using T2 = uint32_t;
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)]));
639  }
640 
641  template <>
642  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint64_t *const data) {
643  using T2 = uint64_t;
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)]));
645  }
646 
647  template <>
648  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const __uint128_t *const data) {
649  return data[0];
650  }
651 
652  template <>
653  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const __uint128_t *const data) {
654  return static_cast<uint64_t>(data[0] >> 64);
655  }
656 
657  template <>
658  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const __uint128_t *const data) {
659  return static_cast<uint32_t>(data[0] >> 96);
660  }
661 
662  template <>
663  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const __uint128_t *const data) {
664  return static_cast<uint16_t>(data[0] >> 112);
665  }
666 
667  template <>
668  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const __uint128_t *const data) {
669  return static_cast<uint8_t>(data[0] >> 120);
670  }
671 
672  );
673 
674  template <typename T, typename T2>
675  CONSTEXPR_ABOVE_CPP11 inline T big_endian_memcpy_unsigned(const T2 *const v)
676  {
677  return big_endian_memcpy<
678  typename detail::unsigned_type_sizeof<sizeof(T)>::type,
679  typename detail::unsigned_type_sizeof<sizeof(T2)>::type>(v);
680  }
681  } // namespace detail
682 } // namespace serdes
683 
684 // clang-format on
685 #endif // _BITCPY_COMMON_H_
serdes::info
CppSerdes library information.
Definition: bitcpy_common.h:72
serdes::bit_length
constexpr T bit_length(T &&x) noexcept
Does nothing but annotate a passed value as a bit length, used for writting clearer code.
Definition: bitcpy_common.h:99
bitcpy_sized_pointer.h
Defines sized_pointer, similar to std::array but with with size as a runtime constant.
serdes::packet
a serialization/deserialization helper class, with load, store, and stream operators
Definition: serdes.h:54
CONSTEXPR_ABOVE_CPP11
#define CONSTEXPR_ABOVE_CPP11
resolves automatically to "constexpr" if C++14 or greater
Definition: bitcpy_common.h:24
serdes::custom_type
wrapper construct used to let the user provide custom (de)serialization support for types without mod...
Definition: bitcpy_common.h:118
serdes::info::version
static constexpr float version
CppSerdes library version number.
Definition: bitcpy_common.h:75
serdes
CppSerdes library namespace.
Definition: bitcpy_common.h:68