CppSerdes  1.0
A serialization/deserialization library designed with embedded systems in mind
bitcpy_to_array.h
Go to the documentation of this file.
1 #ifndef _BITCPY_TO_ARRAY_H_
9 #define _BITCPY_TO_ARRAY_H_
10 
11 #include "bitcpy_common.h"
12 
14 namespace serdes
15 {
26  template <typename T_array, typename T_val, detail::requires_unsigned_type<T_val> * = nullptr>
27  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_array *const dest, const T_val source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
28  {
29  constexpr size_t bits_per_T_array = sizeof(T_array) * 8u;
30  constexpr size_t bits_per_T_array_minus_one = bits_per_T_array - 1u;
31  size_t array_write_index = bit_offset / bits_per_T_array;
32  const size_t bit_offset_from_start_index = bit_offset & bits_per_T_array_minus_one; // same as (bit_offset % sizeof(T_array)*8)
33 
34  // shortcut 1: handle exact bit size and offset match
35  if (bits == bits_per_T_array && bit_offset_from_start_index == 0)
36  {
37  dest[array_write_index] = source;
38  return bits;
39  }
40 
41  // shortcut 2: if the write doesn't need to be split across array elements
42  const size_t number_of_bits_after_index = bit_offset_from_start_index + bits;
43  if (number_of_bits_after_index <= bits_per_T_array)
44  {
45  if (bits == 0) // unlikely. so we reduce the chances of needing to do this check until the last moment
46  return bits;
47  const T_array unaligned_mask = detail::bitmask<T_array>(bits);
48  const size_t alignment_shift = bits_per_T_array - number_of_bits_after_index;
49  T_array &dest_array_element = dest[array_write_index];
50 
51  // zero out intended destination bits only in the mask's range
52  dest_array_element &= ~(unaligned_mask << alignment_shift);
53 
54  // fill in with the new bits from the source
55  dest_array_element |= static_cast<T_array>(source & unaligned_mask) << alignment_shift;
56  return bits;
57  }
58 
59  // if the write DOES need to be split across > 1 array elements
60  const size_t num_array_elements_touched = (number_of_bits_after_index + bits_per_T_array - 1u) / bits_per_T_array;
61  const size_t bits_in_first_element = bits_per_T_array - bit_offset_from_start_index;
62  size_t bits_remaining = bits - bits_in_first_element;
63  {
64  const T_array aligned_mask = detail::bitmask<T_array>(bits_in_first_element);
65  T_array &dest_array_first_element = dest[array_write_index];
66  dest_array_first_element &= ~aligned_mask;
67  dest_array_first_element |= static_cast<T_array>(source >> bits_remaining) & aligned_mask;
68  }
69  for (size_t i = 1u; i < num_array_elements_touched - 1u; i++)
70  {
71  ++array_write_index;
72  bits_remaining -= bits_per_T_array;
73  dest[array_write_index] = static_cast<T_array>(source >> bits_remaining);
74  }
75  {
76  ++array_write_index;
77  const size_t alignment_shift = bits_per_T_array - bits_remaining;
78  const T_array aligned_mask = detail::bitmask<T_array>(bits_remaining) << alignment_shift;
79  T_array &dest_array_last_element = dest[array_write_index];
80  dest_array_last_element &= ~aligned_mask;
81  dest_array_last_element |= (static_cast<T_array>(source) << alignment_shift) & aligned_mask;
82  }
83  return bits;
84  }
85 
96  template <typename T_array, typename T_val, detail::requires_bool_type<T_val> * = nullptr>
97  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_array *const dest, const T_val source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
98  {
99  if (bits == 1)
100  {
101  constexpr size_t bits_per_T_array = sizeof(T_array) * 8u;
102  constexpr size_t bits_per_T_array_minus_one = bits_per_T_array - 1u;
103  const T_array bit_mask = static_cast<T_array>(1u) << (bits_per_T_array_minus_one - (bit_offset & bits_per_T_array_minus_one));
104  const size_t array_index = bit_offset / bits_per_T_array;
105  if (source)
106  dest[array_index] |= bit_mask;
107  else
108  dest[array_index] &= ~bit_mask;
109  return bits;
110  }
111  const uint_fast8_t bool_as_uint = static_cast<uint_fast8_t>(source) & static_cast<uint_fast8_t>(1u);
112  return bitcpy(dest, bool_as_uint, bit_offset, bits);
113  }
114 
125  template <typename T_array, typename T_val, detail::requires_small_non_integral_type<T_val> * = nullptr>
126  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_array *const dest, const T_val source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
127  {
128  #ifdef configCPP_SERDES_ALLOW_REINTERPRET_BUILTINS_UB
129  return bitcpy(dest, detail::reinterpret_as_unsigned(source), bit_offset, bits);
130  #else
131  typename detail::unsigned_type_sizeof<sizeof(T_val)>::type source_copy;
132  std::memcpy(&source_copy, &source, sizeof(T_val));
133  return bitcpy(dest, source_copy, bit_offset, bits);
134  #endif
135  }
136 
147  template <typename T_array, typename T_val, detail::requires_signed_type<T_val> * = nullptr>
148  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_array *const dest, const T_val source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
149  {
150  #ifdef configCPP_SERDES_ALLOW_REINTERPRET_BUILTINS_UB
151  return bitcpy(dest, detail::reinterpret_as_unsigned(source), bit_offset, bits);
152  #else
153  typename detail::unsigned_type_sizeof<sizeof(T_val)>::type source_copy;
154  std::memcpy(&source_copy, &source, sizeof(T_val));
155  return bitcpy(dest, source_copy, bit_offset, bits);
156  #endif
157  }
158 
170  template <typename T_array, typename T_val, detail::requires_unsigned_type<T_val> * = nullptr>
171  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_array *const dest, const sized_pointer<const T_val> &source, const size_t bit_offset, const size_t bits) noexcept
172  {
173  constexpr size_t bits_per_T_val = sizeof(T_val) * 8;
174  const size_t total_bits_T_val = bits_per_T_val * source.size;
175  if (bits == total_bits_T_val)
176  {
177  for (size_t i = 0; i < source.size; i++)
178  bitcpy(dest, source.value[i], bit_offset + i * bits_per_T_val, bits_per_T_val);
179  }
180  else if (bits < total_bits_T_val)
181  {
182  const size_t bit_difference = total_bits_T_val - bits;
183  const size_t starting_source_word = bit_difference / bits_per_T_val;
184  const size_t last_bit_index = source.size - 1u;
185  const size_t last_bit_suboffset = bits_per_T_val * last_bit_index - bit_difference;
186  const size_t remainder_bits = bits - last_bit_suboffset;
187  const std::make_signed<size_t>::type offset_per_loop = bit_offset - bit_difference;
188  for (size_t i = starting_source_word; i < source.size - 1u; i++)
189  bitcpy(dest, source.value[i], offset_per_loop + i * bits_per_T_val, bits_per_T_val);
190  bitcpy(dest, source.value[last_bit_index], bit_offset + last_bit_suboffset, remainder_bits);
191  }
192  else
193  {
194  size_t bit_difference = bits - total_bits_T_val;
195  bitcpy(dest, source.value[0], bit_offset, bits_per_T_val + bit_difference);
196  bit_difference += bit_offset;
197  for (size_t i = 1; i < source.size; i++)
198  {
199  bit_difference += bits_per_T_val;
200  bitcpy(dest, source.value[i], bit_difference, bits_per_T_val);
201  }
202  }
203  return bits;
204  }
205 
215  template <typename T_array, typename T_val, detail::requires_unsigned_type<T_val> * = nullptr>
216  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_array *const dest, const sized_pointer<const T_val> &source, const size_t bit_offset = 0) noexcept
217  {
218  return bitcpy(dest, source, bit_offset, source.bit_capacity());
219  }
220 
231  template <typename T_array, typename T_val, detail::requires_large_non_integral_type<T_val> * = nullptr>
232  CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE size_t bitcpy(T_array *const dest, const T_val &source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
233  {
234  const sized_pointer<const uint8_t> source_array(reinterpret_cast<const uint8_t *const>(&source), sizeof(T_val));
235  return bitcpy(dest, source_array, bit_offset, bits);
236  }
237 
247  template <typename T_array = void, typename T_val = void>
248  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(sized_pointer<T_array> dest, const T_val &source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
249  {
250  if (dest.bit_capacity() < (bits + bit_offset))
251  return 0;
252  return bitcpy(dest.value, source, bit_offset, bits);
253  }
254 
267  template <typename T_array, typename T_val, template<typename> class T_wrap, detail::requires_load_and_store_of_builtin<T_wrap<T_val>> * = nullptr>
268  size_t bitcpy(T_array *const dest, const T_wrap<T_val> &source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
269  {
270  return bitcpy(dest, source.load(), bit_offset, bits);
271  }
272 
283  template <typename T_val>
284  CONSTEXPR_ABOVE_CPP11 size_t bitcpy(sized_pointer<void> &dest, const T_val &source, const size_t bit_offset = 0, const size_t bits = detail::default_bitsize<T_val>::value) noexcept
285  {
286  if (dest.bit_capacity() < (bits + bit_offset))
287  return 0;
288  switch (dest.element_size)
289  {
290  case 1:
291  return bitcpy(reinterpret_cast<uint8_t *>(dest.value), source, bit_offset, bits);
292  case 2:
293  return bitcpy(reinterpret_cast<uint16_t *>(dest.value), source, bit_offset, bits);
294  case 4:
295  return bitcpy(reinterpret_cast<uint32_t *>(dest.value), source, bit_offset, bits);
296  case 8:
297  return bitcpy(reinterpret_cast<uint64_t *>(dest.value), source, bit_offset, bits);
298  default:
299  break;
300  }
301  return 0;
302  }
303 }
304 
305 #endif // _BITCPY_TO_ARRAY_H_
CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE
#define CONSTEXPR_ABOVE_CPP11_AND_NON_LITERAL_STORAGE
resolves automatically to "constexpr" if C++14 or greater
Definition: bitcpy_common.h:46
bitcpy_common.h
Defines common bitcpy details as well as a info::version number, and bit_length function.
serdes::sized_pointer< void >
Holds a pointer to a void array (with type information stored as a runtime element size paramenter) w...
Definition: bitcpy_sized_pointer.h:48
serdes::sized_pointer
Holds a pointer to an array (with its type information) with a constant size.
Definition: bitcpy_sized_pointer.h:19
CONSTEXPR_ABOVE_CPP11
#define CONSTEXPR_ABOVE_CPP11
resolves automatically to "constexpr" if C++14 or greater
Definition: bitcpy_common.h:24
serdes::sized_pointer::bit_capacity
size_t bit_capacity() const noexcept
returns the number of bits in the array
Definition: bitcpy_sized_pointer.h:39
serdes::bitcpy
CONSTEXPR_ABOVE_CPP11 size_t bitcpy(T_val &dest, const T_array *const source, const size_t bit_offset=0, const size_t bits=detail::default_bitsize< T_val >::value) noexcept
[[deserialize, uint destination]] copies the specified number of bits from an array into a value
Definition: bitcpy_from_array.h:29
serdes
CppSerdes library namespace.
Definition: bitcpy_common.h:68