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 #include <stdint.h>
11 #include <cstddef>
12 #include <type_traits>
13 #include <utility>
14 #include "bitcpy_sized_pointer.h"
15 
16 // use the compilation flag "-DconfigBITCPY_COMMON_DISABLE_ATOMIC_FWD_DECL" to disable this
17 #if !defined(configBITCPY_COMMON_DISABLE_ATOMIC_FWD_DECL) && !defined(_GLIBCXX_ATOMIC) && !defined(atomic) && !defined(__APPLE__)
18 namespace std
19 {
20  template <typename _Tp>
21  struct atomic; // forward declaration
22 }
23 #endif
24 
25 // use the compilation flag "-DconfigBITCPY_DISABLE_CONSTEXPR" to disable this
26 #ifndef CONSTEXPR_ABOVE_CPP11
27 #if defined(configBITCPY_DISABLE_CONSTEXPR) || !defined(__cplusplus) || (__cplusplus < 201402L)
28 #define BITCPY_CONSTEXPR_SUPPORTED 0
30 #define CONSTEXPR_ABOVE_CPP11
32 #else
33 #define BITCPY_CONSTEXPR_SUPPORTED 1
35 #define CONSTEXPR_ABOVE_CPP11 constexpr
37 #endif
38 #endif
39 
40 // use the compilation flag "-DconfigBITCPY_DISABLE_CONSTEXPR_NON_LIT" to disable this
41 // NOTE: "Non-literal variables (and labels and gotos) in constexpr functions" not suppored by apple: https://en.cppreference.com/w/cpp/compiler_support#cpp14
42 #ifndef CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE
43 #if defined(configBITCPY_DISABLE_CONSTEXPR_NON_LIT) || !defined(__cplusplus) || (__cplusplus < 201402L) || defined(__APPLE__)
44 #define BITCPY_CONSTEXPR_NON_LITERAL_STORAGE_SUPPORTED 0
46 #define CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE
48 #else
49 #define BITCPY_CONSTEXPR_NON_LITERAL_STORAGE_SUPPORTED 1
51 #define CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE constexpr
53 #endif
54 #endif
55 
56 #ifdef __SIZEOF_INT128__
57 #define BITCPY_INT128_CONDITIONAL_DEFINE(...) __VA_ARGS__
59 #define BITCPY_INT128_CONDITIONAL_DEFINE_C(...) __VA_ARGS__ static_assert(true, "")
61 #else
62 #define BITCPY_INT128_CONDITIONAL_DEFINE(...)
64 #define BITCPY_INT128_CONDITIONAL_DEFINE_C(...) static_assert(true, "")
66 #endif
67 
69 namespace serdes
70 {
71 
73  struct info
74  {
76  static constexpr float version = 1.0;
77  };
78 
83  template <typename T>
84  constexpr T bit_length(T &&x) noexcept
85  {
86  return std::forward<T>(x);
87  }
88 
89  // implimentation details
90  namespace detail
91  {
92  template <typename>
93  struct is_std_atomic : std::false_type
94  {
95  };
96  template <typename T>
97  struct is_std_atomic<std::atomic<T>> : std::true_type
98  {
99  };
100  template <typename T>
101  using requires_unsigned_type = typename std::enable_if<
102  BITCPY_INT128_CONDITIONAL_DEFINE(
103  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) ||
104  (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
105  T>::type;
106  template <typename T>
107  using requires_bool_type = typename std::enable_if<
108  std::is_same<T, bool>::value,
109  T>::type;
110  template <typename T>
111  using requires_non_bool_type = typename std::enable_if<
112  !std::is_same<T, bool>::value,
113  T>::type;
114  template <typename T>
115  using requires_signed_type = typename std::enable_if<
116  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) ||
117  (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
118  T>::type;
119  template <typename T>
120  using requires_large_non_integral_type = typename std::enable_if<
121  (sizeof(T) > 8) &&
122  BITCPY_INT128_CONDITIONAL_DEFINE(!std::is_same<T, __uint128_t>::value &&
123  !std::is_same<T, __int128_t>::value &&) !is_sized_pointer<T>::value,
124  T>::type;
125  template <typename T>
126  using requires_small_non_integral_type = typename std::enable_if<
127  (sizeof(T) <= 8) &&
128  BITCPY_INT128_CONDITIONAL_DEFINE(!std::is_same<T, __uint128_t>::value &&
129  !std::is_same<T, __int128_t>::value &&) !std::is_integral<T>::value &&
130  !std::is_same<T, bool>::value &&
131  !is_std_atomic<T>::value &&
132  !is_sized_pointer<T>::value,
133  T>::type;
134  template <typename T>
135  using requires_pointer_type = typename std::enable_if<
136  std::is_pointer<T>::value,
137  T>::type;
138  template <typename T>
139  using requires_not_a_pointer_type = typename std::enable_if<
140  !std::is_pointer<T>::value,
141  T>::type;
142 
143  template <size_t N>
144  struct unsigned_type_sizeof
145  {
146  };
147  template <>
148  struct unsigned_type_sizeof<1>
149  {
150  using type = uint8_t;
151  };
152  template <>
153  struct unsigned_type_sizeof<2>
154  {
155  using type = uint16_t;
156  };
157  template <>
158  struct unsigned_type_sizeof<4>
159  {
160  using type = uint32_t;
161  };
162  template <>
163  struct unsigned_type_sizeof<8>
164  {
165  using type = uint64_t;
166  };
167  BITCPY_INT128_CONDITIONAL_DEFINE_C(
168  template <>
169  struct unsigned_type_sizeof<16> {
170  using type = __uint128_t;
171  };);
172  template <typename T>
173  __attribute__((const)) const typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(const T &value) noexcept
174  {
175  return *reinterpret_cast<const typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
176  }
177  template <typename T>
178  __attribute__((const)) const volatile typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(const volatile T &value) noexcept
179  {
180  return *reinterpret_cast<const volatile typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
181  }
182  template <typename T>
183  __attribute__((const)) volatile typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(volatile T &value) noexcept
184  {
185  return *reinterpret_cast<volatile typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
186  }
187  template <typename T>
188  __attribute__((const)) typename unsigned_type_sizeof<sizeof(T)>::type &reinterpret_as_unsigned(T &value) noexcept
189  {
190  return *reinterpret_cast<typename unsigned_type_sizeof<sizeof(T)>::type *>(&value);
191  }
192  template <typename T>
193  __attribute__((pure)) constexpr T bitmask(const size_t onecount) noexcept
194  {
195  using Tunsigned = typename std::make_unsigned<T>::type;
196  return static_cast<T>((static_cast<Tunsigned>(~static_cast<Tunsigned>(0u)) >> ((sizeof(Tunsigned) * 8u) - onecount)) * static_cast<Tunsigned>(onecount != 0));
197  }
198  BITCPY_INT128_CONDITIONAL_DEFINE_C(
199  template <>
200  __attribute__((pure)) constexpr __int128_t bitmask<__int128_t>(const size_t onecount) noexcept {
201  using Tunsigned = __uint128_t;
202  return static_cast<Tunsigned>(-(onecount != 0)) & (static_cast<Tunsigned>(-1) >> ((sizeof(Tunsigned) * 8u) - onecount));
203  } template <>
204  __attribute__((pure)) constexpr __uint128_t bitmask<__uint128_t>(const size_t onecount) noexcept {
205  using Tunsigned = __uint128_t;
206  return static_cast<Tunsigned>(-(onecount != 0)) & (static_cast<Tunsigned>(-1) >> ((sizeof(Tunsigned) * 8u) - onecount));
207  });
208  template <typename T, requires_signed_type<T> * = nullptr>
209  CONSTEXPR_ABOVE_CPP11 void extend_sign(T &x, const size_t bits) noexcept
210  {
211  if (bits >= sizeof(T) * 8u || bits == 0u)
212  return;
213  const T m = static_cast<T>(1u) << (bits - 1u);
214  // faster than if-branch checking for the sign bit
215  x = ((x & bitmask<T>(bits)) ^ m) - m;
216  }
217  template <typename T>
218  struct default_bitsize
219  {
220  static constexpr size_t value = sizeof(T) * 8u;
221  };
222  template <>
223  struct default_bitsize<bool>
224  {
225  static constexpr size_t value = 1u;
226  };
227 
228  template <typename T, typename T2>
229  CONSTEXPR_ABOVE_CPP11 inline T big_endian_memcpy(const T2 *const)
230  {
231  static_assert(sizeof(T) == 0,
232  "Use big_endian_memcpy_unsigned instead. Either you tried to use a "
233  "signed type which is not supported, or a type larger than 128bits");
234  }
235 
236  //
237  // uint8_t[] section
238  //
239 
240  template <>
241  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint8_t *const data)
242  {
243  return data[0];
244  }
245  template <>
246  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint8_t *const data)
247  {
248  return static_cast<uint16_t>(data[0]) << 8 | static_cast<uint16_t>(data[1]);
249  }
250  template <>
251  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint8_t *const data)
252  {
253  return (static_cast<uint32_t>(data[0]) << 24) |
254  (static_cast<uint32_t>(data[1]) << 16) |
255  (static_cast<uint32_t>(data[2]) << 8) |
256  static_cast<uint16_t>(data[3]);
257  }
258  template <>
259  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint8_t *const data)
260  {
261  return (static_cast<uint64_t>(data[0]) << 56) |
262  (static_cast<uint64_t>(data[1]) << 48) |
263  (static_cast<uint64_t>(data[2]) << 40) |
264  (static_cast<uint64_t>(data[3]) << 32) |
265  (static_cast<uint64_t>(data[4]) << 24) |
266  (static_cast<uint64_t>(data[5]) << 16) |
267  (static_cast<uint64_t>(data[6]) << 8) |
268  static_cast<uint64_t>(data[7]);
269  }
270 
271  //
272  // uint16_t[] section
273  //
274 
275  template <>
276  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint16_t *const data)
277  {
278  return static_cast<uint8_t>(data[0] >> 8);
279  }
280  template <>
281  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint16_t *const data)
282  {
283  return data[0];
284  }
285  template <>
286  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint16_t *const data)
287  {
288  return (static_cast<uint32_t>(data[0]) << 16) | static_cast<uint32_t>(data[1]);
289  }
290  template <>
291  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint16_t *const data)
292  {
293  return (static_cast<uint64_t>(data[0]) << 48) |
294  (static_cast<uint64_t>(data[1]) << 32) |
295  (static_cast<uint64_t>(data[2]) << 16) |
296  static_cast<uint64_t>(data[3]);
297  }
298 
299  //
300  // uint32_t[] section
301  //
302 
303  template <>
304  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint32_t *const data)
305  {
306  return static_cast<uint8_t>(data[0] >> 24);
307  }
308  template <>
309  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint32_t *const data)
310  {
311  return static_cast<uint16_t>(data[0] >> 16);
312  }
313  template <>
314  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint32_t *const data)
315  {
316  return data[0];
317  }
318  template <>
319  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint32_t *const data)
320  {
321  return (static_cast<uint64_t>(data[0]) << 32) |
322  static_cast<uint64_t>(data[1]);
323  }
324 
325  //
326  // uint64_t[] section
327  //
328 
329  template <>
330  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const uint64_t *const data)
331  {
332  return static_cast<uint8_t>(data[0] >> 56);
333  }
334  template <>
335  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const uint64_t *const data)
336  {
337  return static_cast<uint16_t>(data[0] >> 48);
338  }
339  template <>
340  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const uint64_t *const data)
341  {
342  return static_cast<uint32_t>(data[0] >> 32);
343  }
344  template <>
345  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const uint64_t *const data)
346  {
347  return data[0];
348  }
349 
350  BITCPY_INT128_CONDITIONAL_DEFINE_C(
351 
352  template <>
353  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint8_t *const data) {
354  using T2 = uint8_t;
355  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)]));
356  }
357 
358  template <>
359  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint16_t *const data) {
360  using T2 = uint16_t;
361  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)]));
362  }
363 
364  template <>
365  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint32_t *const data) {
366  using T2 = uint32_t;
367  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)]));
368  }
369 
370  template <>
371  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const uint64_t *const data) {
372  using T2 = uint64_t;
373  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)]));
374  }
375 
376  template <>
377  CONSTEXPR_ABOVE_CPP11 inline __uint128_t big_endian_memcpy(const __uint128_t *const data) {
378  return data[0];
379  }
380 
381  template <>
382  CONSTEXPR_ABOVE_CPP11 inline uint64_t big_endian_memcpy(const __uint128_t *const data) {
383  return static_cast<uint64_t>(data[0] >> 64);
384  }
385 
386  template <>
387  CONSTEXPR_ABOVE_CPP11 inline uint32_t big_endian_memcpy(const __uint128_t *const data) {
388  return static_cast<uint32_t>(data[0] >> 96);
389  }
390 
391  template <>
392  CONSTEXPR_ABOVE_CPP11 inline uint16_t big_endian_memcpy(const __uint128_t *const data) {
393  return static_cast<uint16_t>(data[0] >> 112);
394  }
395 
396  template <>
397  CONSTEXPR_ABOVE_CPP11 inline uint8_t big_endian_memcpy(const __uint128_t *const data) {
398  return static_cast<uint8_t>(data[0] >> 120);
399  }
400 
401  );
402 
403  template <typename T, typename T2>
404  CONSTEXPR_ABOVE_CPP11 inline T big_endian_memcpy_unsigned(const T2 *const v)
405  {
406  return big_endian_memcpy<
407  typename detail::unsigned_type_sizeof<sizeof(T)>::type,
408  typename detail::unsigned_type_sizeof<sizeof(T2)>::type>(v);
409  }
410  }
411 }
412 #endif // _BITCPY_COMMON_H_
CppSerdes library information.
Definition: bitcpy_common.h:73
CppSerdes library namespace.
Definition: bitcpy_common.h:69
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:84
Definition: bitcpy_common.h:18
Defines sized_pointer, similar to std::array but with with size as a runtime constant.
#define CONSTEXPR_ABOVE_CPP11
resolves automatically to "constexpr" if C++14 or greater
Definition: bitcpy_common.h:31