Dev Essential  1.6.3
Loading...
Searching...
No Matches
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
22namespace a_util {
23namespace memory {
24// define all needed error types and values locally
26_MAKE_RESULT(-4, ERR_POINTER);
27_MAKE_RESULT(-5, ERR_INVALID_ARG);
29
31typedef enum {
32 bit_little_endian = 1,
33 bit_big_endian = 2,
34} Endianess;
35
41
42namespace detail {
50std::string formatBits(uint64_t value);
51
66 Endianess endianess,
67 size_t bit_length);
68
73template <typename T>
75protected:
89 static a_util::result::Result readSignal(uint8_t* buffer,
90 size_t start_bit,
91 size_t bit_length,
92 T* value,
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 */
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 */
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
455template <typename T, int is_signed, int is_floating_point>
457
459template <typename T>
460class Converter<T, 0, 0> : public ConverterBase<T> {
461public:
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
500template <typename T>
501class Converter<T, 1, 0> : public ConverterBase<T> {
502public:
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
552template <typename T>
553class Converter<T, 1, 1> : public ConverterBase<T> {
554public:
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_ERROR_DESCRIPTION(ERR_INVALID_ARG, "bit length is not a multiple of byte length");
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_ERROR_DESCRIPTION(ERR_INVALID_ARG, "bit length is not a multiple of byte length");
600 }
601 }
602};
603
604} // namespace detail
605
608public:
612 BitSerializer(void* data, size_t data_size)
613 : _buffer(static_cast<uint8_t*>(data)),
614 _buffer_bytes(data_size),
615 _buffer_bits(_buffer_bytes * 8)
616 {
617 }
618
622 BitSerializer() : _buffer(NULL), _buffer_bytes(0), _buffer_bits(0)
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>
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
688private:
690 uint8_t* _buffer;
692 size_t _buffer_bytes;
694 size_t _buffer_bits;
695
708 a_util::result::Result checkForInvalidArguments(size_t start_bit,
709 size_t bit_length,
710 size_t size_variable)
711 {
712 if (!_buffer) {
713 RETURN_ERROR_DESCRIPTION(ERR_POINTER, "the buffer is not allocated respectively nullptr");
714 }
715
716 // Check invalid starting point
717 if (start_bit >= _buffer_bits) {
718 RETURN_ERROR_DESCRIPTION(ERR_INVALID_ARG, "the start point to read/write is invalid (start bit index exceeds the buffer)");
719 }
720
721 // Check out of buffer bounds or length < 1
722 if ((bit_length < 1) || (_buffer_bits < start_bit + bit_length)) {
723 RETURN_ERROR_DESCRIPTION(ERR_INVALID_ARG, "the length to read/write is zero or the end point to read/write is invalid (end bit index exceeds the buffer)");
724 }
725
726 // Check variable size
727 if (size_variable * 8 < bit_length) {
728 RETURN_ERROR_DESCRIPTION(ERR_INVALID_ARG, "the size of the variable to read into / write from is invalid (exceeds the passed size to read/write)");
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
std::string formatBits(uint64_t value)
a_util::result::Result convertSignalEndianess(uint64_t *signal, Endianess endianess, size_t bit_length)
BitSerializer(void *data, size_t data_size)
Definition bitserializer.h:612
a_util::result::Result read(size_t start_bit, size_t bit_length, T *value, Endianess endianess=get_platform_endianess())
Definition bitserializer.h:644
a_util::result::Result write(size_t start_bit, size_t bit_length, T value, Endianess endianess=get_platform_endianess())
Definition bitserializer.h:675
BitSerializer()
Definition bitserializer.h:622
Definition bitserializer.h:74
static a_util::result::Result readSignal(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess=get_platform_endianess())
Definition bitserializer.h:89
static a_util::result::Result cutLeadingBits(uint64_t *value, size_t bit_length)
Definition bitserializer.h:385
static a_util::result::Result writeSignal(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess=get_platform_endianess())
Definition bitserializer.h:290
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)
Definition bitserializer.h:415
static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
Definition bitserializer.h:492
static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess)
Definition bitserializer.h:474
static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess)
Definition bitserializer.h:515
static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
Definition bitserializer.h:542
static a_util::result::Result read(uint8_t *buffer, size_t start_bit, size_t bit_length, T *value, Endianess endianess)
Definition bitserializer.h:567
static a_util::result::Result write(uint8_t *buffer, size_t start_bit, size_t bit_length, T value, Endianess endianess)
Definition bitserializer.h:591
Template converter class to differentiate between float, signed and unsigned integer values.
Definition bitserializer.h:456
Definition result_type_decl.h:34
#define RETURN_ERROR_DESCRIPTION(_errcode,...)
Definition error_def.h:39
Serves as component for memory access and management.
Definition memory.h:20
Endianess
Enum describing the endianess.
Definition bitserializer.h:31
Endianess get_platform_endianess()
bool copy(void *dest, std::size_t dest_size, const void *source, std::size_t bytes_to_copy)
Serves as component for functionality handling error and return types.
Definition result.h:20
Serves as the root component, with common functionality documented in core functionality.
Definition base.h:24
#define _MAKE_RESULT(_no, _label)
Definition result_info_decl.h:36