ADTF  3.18.2
workspace/conan/dev_essential/1.3.3/dw/stable/package/37682420cd166e229516a41c8d6a139a0b13e1e1/include/ddl/codec/bitserializer.h
Go to the documentation of this file.
1 
14 #ifndef A_UTILS_UTIL_MEMORY_BITSERIALIZER_INCLUDED
15 #define A_UTILS_UTIL_MEMORY_BITSERIALIZER_INCLUDED
16 
17 #include <a_util/memory.h>
18 #include <a_util/result.h>
19 
20 #include <algorithm>
21 
22 namespace a_util {
23 namespace memory {
24 // define all needed error types and values locally
26 _MAKE_RESULT(-4, ERR_POINTER);
27 _MAKE_RESULT(-5, ERR_INVALID_ARG);
29 
31 typedef enum {
32  bit_little_endian = 1,
33  bit_big_endian = 2,
34 } Endianess;
35 
41 
42 namespace detail {
50 std::string formatBits(uint64_t value);
51 
66  Endianess endianess,
67  size_t bit_length);
68 
73 template <typename T>
75 protected:
89  static a_util::result::Result readSignal(uint8_t* buffer,
90  size_t start_bit,
91  size_t bit_length,
92  T* value,
93  Endianess endianess = get_platform_endianess())
94  {
95  /*
96  * offset_end offset_start
97  * _ ____
98  * | | | |
99  * ....|...abcde|fghijklm|no......|.... Buffer (index 0 on the right end side)
100  * |_______________|
101  * bit_length ^
102  * |
103  * start_bit
104  */
105 
106  // 1) COPY relevant bytes of Buffer content to result variable.
107  uint64_t result = 0; // Result value
108  uint64_t ninth_byte = 0; // variable to eventually store a ninth byte from buffer
109  size_t bytes_to_read = 0;
110  copyBytesFromBuffer(buffer, &result, start_bit, bit_length, &ninth_byte, &bytes_to_read);
111 
112  // 2) TRIM unrelevant bits and SHIFT to align value.
113 
114  // Number of bits the start position is offset from 0 (0 for aligned signal)
115  size_t offset_start = start_bit % 8;
116  // Number of bits the end position is offset from the end of the last byte (0 for complete
117  // bytes)
118  size_t offset_end = (8 - ((start_bit + bit_length) % 8)) % 8;
119 
120  /**********************************************************************************************
121  * Distinguish between LE and BE operating systems to get the shift operations right *
122  **********************************************************************************************/
123  // On LE System
124  if (get_platform_endianess() == bit_little_endian) {
125  /* Use bit mask to remove bits on the higher end, which do not belong to the value to
126  * read.
127  *
128  * ...|...abcde|fghijklm|no......| => 000|000abcde|fghijklm|no......|
129  *
130  */
131  cutLeadingBits(&result, bit_length + offset_start);
132 
133  /* Shift right to align start at position 0 (also trims the right end).
134  *
135  * 000|000abcde|fghijklm|no......| => 000|00000000|0abcdefg|hijklmno|
136  *
137  */
138  result >>= offset_start;
139 
140  // Eventually get bits from the copied 9th byte.
141  if (ninth_byte >
142  0) // nothing to take care of if nothing was copied or all copied bits are 0.
143  {
144  // deleteAll unwanted bits from ninth byte.
145  cutLeadingBits(&ninth_byte, (8 - offset_end));
146  size_t bit_size = sizeof(result) * 8;
147  // Shift requested bits from the 9th byte into the right position to be combined
148  // with result.
149  ninth_byte <<= (bit_size - offset_start);
150  // merge value together from all nine bytes.
151  result = result | ninth_byte;
152  }
153 
154  // BE Signal needs byte order swapping.
155  if (endianess == bit_big_endian) {
156  // Only for reading partial bytes. Filling the missing bits differs from LE Signal.
157  if (bit_length % 8 != 0) {
158  /* Shift left to align end position.
159  *
160  * 000|0abcdefg|hijklmno| => 000|abcdefgh|ijklmno0|
161  *
162  */
163  result <<= (offset_end + offset_start) % 8;
164 
165  /* Shift bits within MSByte, filling the gap with 0s.
166  *
167  * 000|abcdefgh|ijklmno0| => 000|abcdefgh|0ijklmno|
168  * ^ ^
169  * MSByte MSByte
170  */
171  uint8_t* ms_byte = (uint8_t*)&result;
172  ms_byte[0] >>= (offset_end + offset_start) % 8;
173  }
174 
175  /* swap bytes to LE.
176  *
177  * 000|abcdefgh|0ijklmno| => 000|0ijklmno|abcdefgh|
178  *
179  */
180  detail::convertSignalEndianess(&result, endianess, bit_length);
181  }
182  }
183  // On BE System
184  else {
185  /* swap bytes to simulate LE shifting operations.
186  *
187  * |...abcde|fghijklm|no......|... => ...|no......|fghijklm|...abcde|
188  *
189  */
190  detail::convertSignalEndianess(&result, bit_little_endian, sizeof(result) * 8);
191 
192  // LE Signal
193  if (endianess == bit_little_endian) {
194  /* Use bit mask to remove bits on the higher end, which do not belong to the value
195  * to read.
196  *
197  * ...|no......|fghijklm|...abcde| => ...|no......|fghijklm|000abcde|
198  *
199  */
200  cutLeadingBits(&result,
201  (sizeof(result) * 8) - offset_end); // Cut away only offset_end bits.
202 
203  /* Shift right to align bits within LSByte (BE Shifting!).
204  *
205  * ...|no......|fghijklm|000abcde| => ...|hijklmno|0abcdefg|00000000|
206  *
207  */
208  result >>= offset_start;
209 
210  /* Shift right to align LSByte on the left side (BE Shifting!).
211  *
212  * ...|hijklmno|0abcdefg|00000000| => |hijklmno|0abcdefg|000
213  *
214  * Shift over all
215  * empty bytes = (all bits - occupied bits (bit_length) - already shifted bits) /
216  * number of bytes
217  */
218  result >>=
219  (((sizeof(result) * 8) - bit_length - offset_start) / sizeof(result)) * 8;
220 
221  // No further byte swap, because there has been one swap before the shift operations
222  // already.
223  }
224  // BE Signal
225  else {
226  /* Shift left to align bits within LSByte (now rightmost byte because of byte swap).
227  * Also deletes bits from end offset.
228  *
229  * ...|no......|fghijklm|...abcde| => ...|........|ijklmno.|abcdefgh|
230  *
231  */
232  result <<= offset_end;
233 
234  // Only for reading partial bytes. Filling the missing bits differs from LE Signal.
235  if (bit_length % 8 != 0) {
236  /* Shift bits within MSByte to move 0s to the highest bits (also deleting all
237  * unwanted bits from start offset).
238  *
239  * ...|........|ijklmno.|abcdefgh| => ...|........|0ijklmno|abcdefgh|
240  *
241  */
242  uint8_t* ms_byte =
243  (uint8_t*)&result + (bit_length / 8); // position of the value's MSByte
244  ms_byte[0] >>=
245  (offset_end + offset_start) % 8; // amount of unused bits within MSByte
246  }
247 
248  /* swap bytes back to BE
249  *
250  * ...|........|0ijklmno|abcdefgh| => |abcdefgh|0ijklmno|...
251  *
252  */
253  detail::convertSignalEndianess(&result,
254  bit_little_endian,
255  sizeof(result) *
256  8); // Change the simulated LE value back
257 
258  /* Use bit mask to remove bits on the higher end, which do not belong to the value
259  * to read.
260  *
261  * |abcdefgh|0ijklmno|... => |abcdefgh|0ijklmno|000
262  *
263  * Remove everything behind the value length plus the gap within MSByte.
264  */
265  cutLeadingBits(&result, bit_length + (offset_end + offset_start) % 8);
266  }
267  }
268 
269  // Copy the resulting value to the target variable. No Casting! Data might be lost
270  // otherwise.
271  size_t sz = (std::min)(sizeof(*value), sizeof(result));
272  a_util::memory::copy(value, sz, &result, sz);
273 
274  return a_util::result::SUCCESS;
275  }
276 
290  static a_util::result::Result writeSignal(uint8_t* buffer,
291  size_t start_bit,
292  size_t bit_length,
293  T value,
294  Endianess endianess = get_platform_endianess())
295  {
296  // 1) Copy relevant bytes of Buffer content to be overwritten.
297  uint64_t buffer_copy = 0;
298  uint64_t ninth_byte = 0; // storage variable for the ninth bit from buffer
299  size_t bytes_to_read = 0;
301  buffer, &buffer_copy, start_bit, bit_length, &ninth_byte, &bytes_to_read);
302 
303  // 2) Erase Bits from Buffer copy that will be overwritten TODO: BE LE system difference
304  // important here? Number of bits the start position is offset from 0
305  size_t offset_start = start_bit % 8;
306  // Number of bits the end position is offset from the end of the last byte
307  size_t offset_end = (8 - ((start_bit + bit_length) % 8)) % 8;
308  uint64_t mask_left = ~0ULL;
309  if ((bit_length + offset_start) >= (sizeof(mask_left) * 8)) {
310  mask_left = 0;
311  }
312  else {
313  mask_left = ~0ULL;
314  mask_left <<= (bit_length + offset_start);
315  }
316  uint64_t mask = ~0ULL;
317  mask <<= offset_start;
318  mask = ~mask;
319  mask |= mask_left;
320 
321  buffer_copy &= mask;
322 
323  // 3) Copy value to UInt64 variable to work with.
324  uint64_t signal;
325  a_util::memory::copy(&signal, sizeof(signal), &value, sizeof(signal));
326 
327  // 4) Keep only nLength bits: Remove bits that should not be written to the Buffer.
328  cutLeadingBits(&signal, bit_length);
329 
330  // 5) Shift to align at start bit position.
331  int shift_amount = (int)offset_start; // Initialized to fit LE Signal shift
332 
333  // BE Signal
334  if (endianess == bit_big_endian) {
335  // swap bytes
336  detail::convertSignalEndianess(&signal, endianess, bit_length);
337  // Remove gap for partial bytes within MSByte
338  uint8_t* ms_byte = (uint8_t*)&signal;
339  int ms_shift = (8 - (bit_length % 8)) % 8;
340  ms_byte[0] <<= ms_shift;
341  shift_amount -= ms_shift;
342  }
343 
344  // Copy most significant byte to ninth buffer byte before losing bits with the shift.
345  if ((offset_start + bit_length) > (sizeof(signal) * 8)) {
346  uint64_t signal_for_ninth_byte = signal;
347  signal_for_ninth_byte >>= (sizeof(signal) - 1) * 8;
348  signal_for_ninth_byte >>= (8 - offset_start); // Only LE Signal
349  uint64_t mask_to_set = ~0ULL;
350  mask_to_set <<= (8 - offset_end);
351  ninth_byte &= mask_to_set;
352  ninth_byte |= signal_for_ninth_byte;
353  }
354 
355  if (shift_amount < 0) {
356  signal >>= std::abs(shift_amount);
357  }
358  else {
359  signal <<= shift_amount;
360  }
361 
362  // 7) Write bytes with integrated signal back to the buffer.
363  buffer_copy |= signal;
364 
365  size_t sz = (std::min)(bytes_to_read, sizeof(signal));
366  a_util::memory::copy(buffer + (start_bit / 8), sz, &buffer_copy, sz);
367 
368  // Eventually copy ninth byte back to buffer
369  if (bytes_to_read > sizeof(signal)) {
370  a_util::memory::copy(buffer + (start_bit / 8) + sizeof(signal), 1, &ninth_byte, 1);
371  }
372 
373  return a_util::result::SUCCESS;
374  }
375 
385  static a_util::result::Result cutLeadingBits(uint64_t* value, size_t bit_length)
386  {
387  size_t bit_size = (sizeof(*value) * 8);
388  if (bit_length < bit_size) {
389  uint64_t mask = ~0ULL;
390  mask >>= (bit_size - bit_length);
391  *value &= mask;
392  }
393 
394  return a_util::result::SUCCESS;
395  }
396 
416  uint64_t* value,
417  size_t start_bit,
418  size_t bit_length,
419  uint64_t* ninth_byte,
420  size_t* bytes_to_read)
421  {
422  // Byte within the buffer to start reading at
423  size_t start_byte = start_bit / 8;
424 
425  // Number of bits to read: signal length + bits to fill in the offset on both sides for
426  // unaligned signals
427  size_t bits_to_read = bit_length + (start_bit % 8);
428  if ((bits_to_read % 8) > 0) {
429  bits_to_read += (8 - (bits_to_read % 8));
430  }
431  // Number of bytes to read from the buffer
432  *bytes_to_read = bits_to_read / 8;
433 
434  // Copy up to 8 bytes to result
435  if (*bytes_to_read > (size_t)sizeof(*value)) {
436  a_util::memory::copy(value, sizeof(*value), buffer + start_byte, sizeof(*value));
437  }
438  else {
439  a_util::memory::copy(value, *bytes_to_read, buffer + start_byte, *bytes_to_read);
440  }
441 
442  // The max signal size is 8 byte, but if the signal is not aligned, it might spread over 9
443  // bytes.
444 
445  if (*bytes_to_read > sizeof(*value)) {
446  // Get a copy of the most significant byte, which could not yet be saved to result
447  a_util::memory::copy(ninth_byte, 1, buffer + start_byte + sizeof(*value), 1);
448  }
449 
450  return a_util::result::SUCCESS;
451  }
452 };
453 
455 template <typename T, int is_signed, int is_floating_point>
456 class Converter;
457 
459 template <typename T>
460 class Converter<T, 0, 0> : public ConverterBase<T> {
461 public:
475  uint8_t* buffer, size_t start_bit, size_t bit_length, T* value, Endianess endianess)
476  {
477  return ConverterBase<T>::readSignal(buffer, start_bit, bit_length, value, endianess);
478  }
479 
493  uint8_t* buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
494  {
495  return ConverterBase<T>::writeSignal(buffer, start_bit, bit_length, value, endianess);
496  }
497 };
498 
500 template <typename T>
501 class Converter<T, 1, 0> : public ConverterBase<T> {
502 public:
516  uint8_t* buffer, size_t start_bit, size_t bit_length, T* value, Endianess endianess)
517  {
519  ConverterBase<T>::readSignal(buffer, start_bit, bit_length, value, endianess);
520  if (res != a_util::result::SUCCESS) {
521  return res;
522  }
523 
524  // replicate sign bit
525  *value <<= (sizeof(T) * 8) - bit_length;
526  *value >>= (sizeof(T) * 8) - bit_length;
527  return a_util::result::SUCCESS;
528  }
529 
543  uint8_t* buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
544  {
545  // Nothing special to take care of for writing signed integers, compared to writing unsigned
546  // integers.
547  return ConverterBase<T>::writeSignal(buffer, start_bit, bit_length, value, endianess);
548  }
549 };
550 
552 template <typename T>
553 class Converter<T, 1, 1> : public ConverterBase<T> {
554 public:
568  uint8_t* buffer, size_t start_bit, size_t bit_length, T* value, Endianess endianess)
569  {
570  // Read only values of size tFloat
571  if (sizeof(T) * 8 == bit_length) {
572  return ConverterBase<T>::readSignal(buffer, start_bit, bit_length, value, endianess);
573  }
574  else {
575  return ERR_INVALID_ARG;
576  }
577  }
578 
592  uint8_t* buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
593  {
594  // Write only values of size tFloat
595  if (sizeof(T) * 8 == bit_length) {
596  return ConverterBase<T>::writeSignal(buffer, start_bit, bit_length, value, endianess);
597  }
598  else {
599  return ERR_INVALID_ARG;
600  }
601  }
602 };
603 
604 } // namespace detail
605 
608 public:
612  BitSerializer(void* data, size_t data_size)
613  : _buffer(static_cast<uint8_t*>(data)),
614  _buffer_bytes(data_size),
616  {
617  }
618 
623  {
624  }
625 
643  template <typename T>
644  a_util::result::Result read(size_t start_bit,
645  size_t bit_length,
646  T* value,
647  Endianess endianess = get_platform_endianess())
648  {
649  using Converter =
650  detail::Converter<T, std::is_signed<T>::value, std::is_floating_point<T>::value>;
651  // Check if in range
652  const auto result_code = checkForInvalidArguments(start_bit, bit_length, sizeof(T));
653  return !result_code ? result_code :
654  Converter::read(_buffer, start_bit, bit_length, value, endianess);
655  }
656 
674  template <typename T>
675  a_util::result::Result write(size_t start_bit,
676  size_t bit_length,
677  T value,
678  Endianess endianess = get_platform_endianess())
679  {
680  using Converter =
681  detail::Converter<T, std::is_signed<T>::value, std::is_floating_point<T>::value>;
682  // Check if in range
683  const auto result_code = checkForInvalidArguments(start_bit, bit_length, sizeof(T));
684  return !result_code ? result_code :
685  Converter::write(_buffer, start_bit, bit_length, value, endianess);
686  }
687 
688 private:
690  uint8_t* _buffer;
694  size_t _buffer_bits;
695 
709  size_t bit_length,
710  size_t size_variable)
711  {
712  if (!_buffer) {
713  return ERR_POINTER;
714  }
715 
716  // Check invalid starting point
717  if (start_bit >= _buffer_bits) {
718  return ERR_INVALID_ARG;
719  }
720 
721  // Check out of buffer bounds or length < 1
722  if ((bit_length < 1) || (_buffer_bits < start_bit + bit_length)) {
723  return ERR_INVALID_ARG;
724  }
725 
726  // Check variable size
727  if (size_variable * 8 < bit_length) {
728  return ERR_INVALID_ARG;
729  }
730 
731  return a_util::result::SUCCESS;
732  }
733 };
734 
735 } // namespace memory
736 } // namespace a_util
737 
738 #endif // A_UTILS_UTIL_MEMORY_BITSERIALIZER_INCLUDED
a_util::result::Result write(size_t start_bit, size_t bit_length, T value, Endianess endianess=get_platform_endianess())
Write value to bitfield.
a_util::result::Result read(size_t start_bit, size_t bit_length, T *value, Endianess endianess=get_platform_endianess())
Read value from bitfield.
a_util::result::Result checkForInvalidArguments(size_t start_bit, size_t bit_length, size_t size_variable)
Check if the parameters for the reading and writing access are valid.
static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
Write unsigned integer to bitfield.
static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess)
Read unsigned integer from bitfield.
static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
Write signed integer to bitfield.
static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess)
Read signed integer from bitfield.
static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
Write tFloat to bitfield.
static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess)
Read tFloat from bitfield.
static a_util::result::Result cutLeadingBits(uint64_t *value, size_t bit_length)
Set the highest bits of a uint64_t value to zero.
static a_util::result::Result writeSignal(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess=get_platform_endianess())
Write value to bitfield.
static a_util::result::Result copyBytesFromBuffer(uint8_t *buffer, uint64_t *value, size_t start_bit, size_t bit_length, uint64_t *ninth_byte, size_t *bytes_to_read)
Copy bytes_to_read number of bytes from the buffer to value and ninth_byte.
static a_util::result::Result readSignal(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess=get_platform_endianess())
Read value from bitfield.
A common result class usable as return value throughout.
Common include for component a_util::memory.
Endianess get_platform_endianess()
Returns the endianess of the platform.
bool copy(void *dest, std::size_t dest_size, const void *source, std::size_t bytes_to_copy)
Portable safe memcopy.
Serves as the root component, with common functionality documented in core functionality.
Definition: base.h:24
#define _MAKE_RESULT(_no, _label)
Create a result type and a constant instance of this type in an unnamed namespace.
Common include for component a_util::result.
std::string formatBits(uint64_t value)
Format the bit pattern of a uint64_t value to a string Used for debug purposes.
a_util::result::Result convertSignalEndianess(uint64_t *signal, Endianess endianess, size_t bit_length)
Convert the endianess of a signal by correctly swapping the byte order if required.