ADTF
builds/digitalwerk/solutions/adtf_content/adtf_base/adtf_core/src/libraries/a_utils/include/a_utils/core/bitserializer.h
Go to the documentation of this file.
1 
7 #include <type_traits>
8 #include <limits.h>
9 
10 #ifndef _BIT_SERIALIZER_HEADER_
11 #define _BIT_SERIALIZER_HEADER_
12 
13 namespace A_UTILS_NS
14 {
15  namespace bithelpers
16  {
24 
41  tResult ConvertSignalEndianess(tUInt64 *pSignal, tInt nEndianess, tSize nBitLength);
42  } // namespace bithelpers
43 
44  namespace bitconverters
45  {
50  template<typename T>
51  class ConverterBase {
52 
53  protected:
54 
71  static tResult ReadSignal(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T* pValue,
72  tInt nEndianess = PLATFORM_BYTEORDER)
73  {
74  /*
75  * nOffsetEnd nOffsetStart
76  * _ ____
77  * | | | |
78  * ....|...abcde|fghijklm|no......|.... Buffer (index 0 on the right end side)
79  * |_______________|
80  * nBitLength ^
81  * |
82  * nStartBit
83  */
84 
85  // 1) COPY relevant bytes of Buffer content to result variable.
86  tUInt64 nResult = 0; // Result value
87  tUInt64 nNinthByte = 0; // variable to eventually store a ninth byte from buffer
88  tSize nBytesToRead = 0;
89  CopyBytesFromBuffer(pBuffer, &nResult, nStartBit, nBitLength, &nNinthByte, &nBytesToRead);
90 
91  // 2) TRIM unrelevant bits and SHIFT to align value.
92 
93  // Number of bits the start position is offset from 0 (0 for aligned signal)
94  tSize nOffsetStart = nStartBit % CHAR_BIT;
95  // Number of bits the end position is offset from the end of the last byte (0 for complete bytes)
96  tSize nOffsetEnd = (CHAR_BIT - ((nStartBit + nBitLength) % CHAR_BIT)) % CHAR_BIT;
97 
98  /**********************************************************************************************
99  * Distinguish between LE and BE operating systems to get the shift operations right *
100  **********************************************************************************************/
101  // On LE System
103  {
104  /* Use bit mask to remove bits on the higher end, which do not belong to the value to read.
105  *
106  * ...|...abcde|fghijklm|no......| => 000|000abcde|fghijklm|no......|
107  *
108  */
109  CutLeadingBits(&nResult, nBitLength + nOffsetStart);
110 
111  /* Shift right to align start at position 0 (also trims the right end).
112  *
113  * 000|000abcde|fghijklm|no......| => 000|00000000|0abcdefg|hijklmno|
114  *
115  */
116  nResult >>= nOffsetStart;
117 
118  // Eventually get bits from the copied 9th byte.
119  if (nNinthByte > 0) // nothing to take care of if nothing was copied or all copied bits are 0.
120  {
121  // Delete unwanted bits from ninth byte.
122  CutLeadingBits(&nNinthByte, (CHAR_BIT - nOffsetEnd));
123  tSize nBitSize = sizeof(nResult) * CHAR_BIT;
124  // Shift requested bits from the 9th byte into the right position to be combined with nResult.
125  nNinthByte <<= (nBitSize - nOffsetStart);
126  // Merge value together from all nine bytes.
127  nResult = nResult | nNinthByte;
128  }
129 
130  // BE Signal needs byte order swapping.
131  if (nEndianess == PLATFORM_BIG_ENDIAN_8)
132  {
133  // Only for reading partial bytes. Filling the missing bits differs from LE Signal.
134  if (nBitLength % CHAR_BIT != 0)
135  {
136  /* Shift left to align end position.
137  *
138  * 000|0abcdefg|hijklmno| => 000|abcdefgh|ijklmno0|
139  *
140  */
141  nResult <<= (nOffsetEnd + nOffsetStart) % CHAR_BIT;
142 
143  /* Shift bits within MSByte, filling the gap with 0s.
144  *
145  * 000|abcdefgh|ijklmno0| => 000|abcdefgh|0ijklmno|
146  * ^ ^
147  * MSByte MSByte
148  */
149  tUInt8 *pMSByte = (tUInt8*)&nResult;
150  pMSByte[0] >>= (nOffsetEnd + nOffsetStart) % CHAR_BIT;
151  }
152 
153  /* Swap bytes to LE.
154  *
155  * 000|abcdefgh|0ijklmno| => 000|0ijklmno|abcdefgh|
156  *
157  */
158  bithelpers::ConvertSignalEndianess(&nResult, nEndianess, nBitLength);
159  }
160  }
161  // On BE System
162  else
163  {
164  /* Swap bytes to simulate LE shifting operations.
165  *
166  * |...abcde|fghijklm|no......|... => ...|no......|fghijklm|...abcde|
167  *
168  */
169  bithelpers::ConvertSignalEndianess(&nResult, PLATFORM_LITTLE_ENDIAN_8, sizeof(nResult) * CHAR_BIT);
170 
171  // LE Signal
172  if (nEndianess == PLATFORM_LITTLE_ENDIAN_8)
173  {
174  /* Use bit mask to remove bits on the higher end, which do not belong to the value to read.
175  *
176  * ...|no......|fghijklm|...abcde| => ...|no......|fghijklm|000abcde|
177  *
178  */
179  CutLeadingBits(&nResult, (sizeof(nResult) * CHAR_BIT) - nOffsetEnd); // Cut away only nOffsetEnd bits.
180 
181  /* Shift right to align bits within LSByte (BE Shifting!).
182  *
183  * ...|no......|fghijklm|000abcde| => ...|hijklmno|0abcdefg|00000000|
184  *
185  */
186  nResult >>= nOffsetStart;
187 
188  /* Shift right to align LSByte on the left side (BE Shifting!).
189  *
190  * ...|hijklmno|0abcdefg|00000000| => |hijklmno|0abcdefg|000
191  *
192  * Shift over all
193  * empty bytes = (all bits - occupied bits (nBitLength) - already shifted bits) / number of bytes
194  */
195  nResult >>= (((sizeof(nResult) * CHAR_BIT) - nBitLength - nOffsetStart) / sizeof(nResult)) * CHAR_BIT;
196 
197  // No further byte swap, because there has been one swap before the shift operations already.
198  }
199  // BE Signal
200  else
201  {
202  /* Shift left to align bits within LSByte (now rightmost byte because of byte swap).
203  * Also deletes bits from end offset.
204  *
205  * ...|no......|fghijklm|...abcde| => ...|........|ijklmno.|abcdefgh|
206  *
207  */
208  nResult <<= nOffsetEnd;
209 
210  // Only for reading partial bytes. Filling the missing bits differs from LE Signal.
211  if (nBitLength % CHAR_BIT != 0)
212  {
213  /* Shift bits within MSByte to move 0s to the highest bits (also deleting all unwanted bits from start offset).
214  *
215  * ...|........|ijklmno.|abcdefgh| => ...|........|0ijklmno|abcdefgh|
216  *
217  */
218  tUInt8 *pMSByte = (tUInt8*)&nResult + (nBitLength / CHAR_BIT); // position of the value's MSByte
219  pMSByte[0] >>= (nOffsetEnd + nOffsetStart) % CHAR_BIT; // amount of unused bits within MSByte
220  }
221 
222  /* Swap bytes back to BE
223  *
224  * ...|........|0ijklmno|abcdefgh| => |abcdefgh|0ijklmno|...
225  *
226  */
227  bithelpers::ConvertSignalEndianess(&nResult, PLATFORM_LITTLE_ENDIAN_8, sizeof(nResult) * CHAR_BIT); // Change the simulated LE value back
228 
229  /* Use bit mask to remove bits on the higher end, which do not belong to the value to read.
230  *
231  * |abcdefgh|0ijklmno|... => |abcdefgh|0ijklmno|000
232  *
233  * Remove everything behind the value length plus the gap within MSByte.
234  */
235  CutLeadingBits(&nResult, nBitLength + (nOffsetEnd + nOffsetStart) % CHAR_BIT);
236  }
237  }
238 
239  // Copy the resulting value to the target variable. No Casting! Data might be lost otherwise.
240  cMemoryBlock::MemCopy(pValue, &nResult, (tSize)(std::min)(sizeof(*pValue), sizeof(nResult)));
241 
243  }
244 
261  static tResult WriteSignal(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue,
262  tInt nEndianess = PLATFORM_BYTEORDER)
263  {
264  // 1) Copy relevant bytes of Buffer content to be overwritten.
265  tUInt64 nBufferCopy = 0;
266  tUInt64 nNinthByte = 0; // storage variable for the ninth bit from buffer
267  tSize nBytesToRead = 0;
268  CopyBytesFromBuffer(pBuffer, &nBufferCopy, nStartBit, nBitLength, &nNinthByte, &nBytesToRead);
269 
270  // 2) Erase Bits from Buffer copy that will be overwritten TODO: BE LE system difference important here?
271  // Number of bits the start position is offset from 0
272  tSize nOffsetStart = nStartBit % CHAR_BIT;
273  // Number of bits the end position is offset from the end of the last byte
274  tSize nOffsetEnd = (CHAR_BIT - ((nStartBit + nBitLength) % CHAR_BIT)) % CHAR_BIT;
275  tUInt64 nMaskLeft = ~0ULL;
276  if ((nBitLength + nOffsetStart) >= (sizeof(nMaskLeft) * CHAR_BIT))
277  {
278  nMaskLeft = 0;
279  }
280  else
281  {
282  nMaskLeft = ~0ULL;
283  nMaskLeft <<= (nBitLength + nOffsetStart);
284  }
285  tUInt64 nMask = ~0ULL;
286  nMask <<= nOffsetStart;
287  nMask = ~nMask;
288  nMask |= nMaskLeft;
289 
290  nBufferCopy &= nMask;
291 
292  // 3) Copy nValue to UInt64 variable to work with.
293  tUInt64 nSignal;
294  cMemoryBlock::MemCopy(&nSignal, &nValue, (tSize)sizeof(nSignal));
295 
296  // 4) Keep only nLength bits: Remove bits that should not be written to the Buffer.
297  CutLeadingBits(&nSignal, nBitLength);
298 
299  // 5) Shift to align at start bit position.
300  tInt nShiftAmount = (tInt)nOffsetStart; // Initialized to fit LE Signal shift
301 
302  // BE Signal
303  if (nEndianess == PLATFORM_BIG_ENDIAN_8)
304  {
305  // Swap bytes
306  bithelpers::ConvertSignalEndianess(&nSignal, nEndianess, nBitLength);
307  // Remove gap for partial bytes within MSByte
308  tUInt8 *pMSByte = (tUInt8*)&nSignal;
309  tInt nMSBShift = (CHAR_BIT - (nBitLength % CHAR_BIT)) % CHAR_BIT;
310  pMSByte[0] <<= nMSBShift;
311  nShiftAmount -= nMSBShift;
312  }
313 
314  // Copy most significant byte to ninth buffer byte before losing bits with the shift.
315  if ((nOffsetStart + nBitLength) > (sizeof(nSignal) * CHAR_BIT))
316  {
317  tUInt64 nSignalForNinthByte = nSignal;
318  nSignalForNinthByte >>= (sizeof(nSignal) - 1) * CHAR_BIT;
319  nSignalForNinthByte >>= (CHAR_BIT - nOffsetStart); // Only LE Signal
320  tUInt64 nMask = ~0ULL;
321  nMask <<= (CHAR_BIT - nOffsetEnd);
322  nNinthByte &= nMask;
323  nNinthByte |= nSignalForNinthByte;
324  }
325 
326  if (nShiftAmount < 0)
327  {
328  nSignal >>= std::abs(nShiftAmount);
329  }
330  else
331  {
332  nSignal <<= nShiftAmount;
333  }
334 
335  // 7) Write bytes with integrated signal back to the buffer.
336  nBufferCopy |= nSignal;
337 
338  cMemoryBlock::MemCopy(pBuffer + (nStartBit / CHAR_BIT), &nBufferCopy, (std::min)(nBytesToRead, (tSize)sizeof(nSignal)));
339 
340  // Eventually copy ninth byte back to buffer
341  if (nBytesToRead > sizeof(nSignal))
342  {
343  cMemoryBlock::MemCopy(pBuffer + (nStartBit / CHAR_BIT) + sizeof(nSignal), &nNinthByte, 1);
344  }
345 
347  }
348 
358  static tResult CutLeadingBits(tUInt64 *pValue, tSize nBitLength)
359  {
360  tSize nBitSize = (sizeof(pValue) * CHAR_BIT);
361  if (nBitLength < nBitSize)
362  {
363  tUInt64 mask = ~0ULL;
364  mask >>= (nBitSize - nBitLength);
365  *pValue &= mask;
366  }
367 
369  }
370 
387  static tResult CopyBytesFromBuffer(tUInt8 *pBuffer, tUInt64 *pValue, tSize nStartBit, tSize nBitLength, tUInt64 *pNinthByte, tSize *pBytesToRead)
388  {
389  // Byte within the buffer to start reading at
390  tSize nStartByte = nStartBit / CHAR_BIT;
391 
392  // Number of bits to read: signal length + bits to fill in the offset on both sides for unaligned signals
393  tSize nBitsToRead = nBitLength + (nStartBit % CHAR_BIT);
394  if ((nBitsToRead % CHAR_BIT) > 0)
395  {
396  nBitsToRead += (CHAR_BIT - (nBitsToRead % CHAR_BIT));
397  }
398  // Number of bytes to read from the buffer
399  *pBytesToRead = nBitsToRead / CHAR_BIT;
400 
401  // Copy up to 8 bytes to nResult
402  if (*pBytesToRead > (tSize)sizeof(pValue))
403  {
404  cMemoryBlock::MemCopy(pValue, pBuffer + nStartByte, (tSize)sizeof(pValue));
405  }
406  else
407  {
408  cMemoryBlock::MemCopy(pValue, pBuffer + nStartByte, *pBytesToRead);
409  }
410 
411  // The max signal size is 8 byte, but if the signal is not aligned, it might spread over 9 bytes.
412 
413  if (*pBytesToRead > sizeof(*pValue))
414  {
415  // Get a copy of the most significant byte, which could not yet be saved to nResult
416  cMemoryBlock::MemCopy(pNinthByte, pBuffer + nStartByte + sizeof(*pValue), 1);
417  }
418 
420  }
421  };
422 
426  template<typename T, int IS_SIGNED, int IS_FLOATING_POINT> class Converter;
427 
431  template<typename T>
432  class Converter<T, 0, 0> : public ConverterBase<T>
433  {
434  public:
435 
451  static tResult Read(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T* pValue,
452  tInt nEndianess)
453  {
454  return ConverterBase<T>::ReadSignal(pBuffer, nStartBit, nBitLength, pValue, nEndianess);
455  }
456 
472  static tResult Write(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue,
473  tInt nEndianess)
474  {
475  return ConverterBase<T>::WriteSignal(pBuffer, nStartBit, nBitLength, nValue, nEndianess);
476  }
477  };
478 
482  template<typename T>
483  class Converter<T, 1, 0> : public ConverterBase<T>
484  {
485  public:
486 
502  static tResult Read(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T* pValue,
503  tInt nEndianess)
504  {
505  RETURN_IF_FAILED(ConverterBase<T>::ReadSignal(pBuffer, nStartBit, nBitLength, pValue, nEndianess));
506  // replicate sign bit
507  *pValue <<= (sizeof(T) * CHAR_BIT) - nBitLength;
508  *pValue >>= (sizeof(T) * CHAR_BIT) - nBitLength;
510  }
511 
527  static tResult Write(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue,
528  tInt nEndianess)
529  {
530  // Nothing special to take care of for writing signed integers, compared to writing unsigned integers.
531  return ConverterBase<T>::WriteSignal(pBuffer, nStartBit, nBitLength, nValue, nEndianess);
532  }
533  };
534 
538  template<typename T>
539  class Converter<T, 1, 1> : public ConverterBase<T>
540  {
541 
542  public:
543 
559  static tResult Read(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T* pValue,
560  tInt nEndianess)
561  {
562  // Read only values of size tFloat
563  if (sizeof(T) * CHAR_BIT == nBitLength)
564  {
565  return ConverterBase<T>::ReadSignal(pBuffer, nStartBit, nBitLength, pValue, nEndianess);
566  }
567  else
568  {
569  return ERR_INVALID_ARG;
570  }
571  }
572 
588  static tResult Write(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue,
589  tInt nEndianess)
590  {
591  // Write only values of size tFloat
592  if (sizeof(T) * CHAR_BIT == nBitLength)
593  {
594  return ConverterBase<T>::WriteSignal(pBuffer, nStartBit, nBitLength, nValue, nEndianess);
595  }
596  else
597  {
598  return ERR_INVALID_ARG;
599  }
600  }
601  };
602 
603 
604  } // namespace bitconverters
605 
612 {
613  public:
614 
619  {
620  m_pBuffer = reinterpret_cast<tUInt8*>(pMemoryBlock->GetPtr());
621  m_nBufferBytes = pMemoryBlock->GetSize();
622  m_nBufferBits = m_nBufferBytes * CHAR_BIT;;
623  }
624 
628  cBitSerializer(tVoid* pData, tSize nDataSize) : cBitSerializer()
629  {
630  m_pBuffer = static_cast<tUInt8*>(pData);
631  m_nBufferBytes = nDataSize;
632  m_nBufferBits = m_nBufferBytes * CHAR_BIT;
633  }
634 
639  {
640  m_pBuffer = nullptr;
641  m_nBufferBytes = 0;
642  m_nBufferBits = 0;
643  }
644 
648  virtual ~cBitSerializer()
649  {
650 
651  }
652 
673  template<typename T>
674  tResult Read(tSize nStartBit, tSize nBitLength, T* pValue,
675  tInt nEndianess = PLATFORM_BYTEORDER)
676  {
677  // Check if in range
678  tResult resultCode = CheckForInvalidArguments(nStartBit, nBitLength, sizeof(T));
679  if (resultCode.IsFailed())
680  {
681  return resultCode;
682  }
683 
684  // Call template function
686  std::is_floating_point<T>::value>
687  ::Read(m_pBuffer, nStartBit, nBitLength, pValue, nEndianess);
688 
690  }
691 
712  template<typename T>
713  tResult Write(tSize nStartBit, tSize nBitLength, T nValue,
714  tInt nEndianess = PLATFORM_BYTEORDER)
715  {
716  // Check if in range
717  tResult resultCode = CheckForInvalidArguments(nStartBit, nBitLength, sizeof(T));
718  if (resultCode.IsFailed())
719  {
720  return resultCode;
721  }
722 
723  // Call template function
725  std::is_floating_point<T>::value>
726  ::Write(m_pBuffer, nStartBit, nBitLength, nValue, nEndianess);
727 
729  }
730 
731 
732  private:
733 
740 
753  tResult CheckForInvalidArguments(tSize nStartBit, tSize nBitLength, tSize nSizeVariable)
754  {
756 
757  // Check invalid starting point
758  if (nStartBit >= m_nBufferBits)
759  {
760  RETURN_ERROR(ERR_INVALID_ARG);
761  }
762 
763  // Check out of buffer bounds or length < 1
764  if ((nBitLength < 1)
765  || (m_nBufferBits < nStartBit + nBitLength))
766  {
767  RETURN_ERROR(ERR_INVALID_ARG);
768  }
769 
770  // Check variable size
771  if (nSizeVariable * CHAR_BIT < nBitLength)
772  {
773  RETURN_ERROR(ERR_INVALID_ARG);
774  }
775 
777  }
778 
779 };
780 
781 } //namespace A_UTILS_NS
782 
783 #endif // __BIT_SERIALIZER_HEADER
uint8_t tUInt8
type definition for unsigned integer values (8bit) (platform and compiler independent type).
void tVoid
The tVoid is always the definition for the void (non-type).
int tInt
type definition for signed integer value (platform and compiler dependent type).
size_t tSize
type definition for a array size values, map size values etc.
uint64_t tUInt64
type definition for unsigned integer values (64bit) (platform and compiler independent type).
#define RETURN_IF_FAILED(s)
Return if expression is failed, which requires the calling function's return type to be tResult.
#define RETURN_NOERROR
Return status ERR_NOERROR, which requires the calling function's return type to be tResult.
#define RETURN_ERROR(code)
Return specific error code, which requires the calling function's return type to be tResult.
#define RETURN_IF_POINTER_NULL(_ptr)
Return ERR_POINTER if _ptr is nullptr, which requires the calling function's return type to be tResul...
tResult ConvertSignalEndianess(tUInt64 *pSignal, tInt nEndianess, tSize nBitLength)
Convert the endianess of a signal by correctly swapping the byte order if required.
tVoid PrintBits(tUInt64 nValue)
Write the bit pattern of a tUInt64 value to a string and prints it.
static tResult Read(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T *pValue, tInt nEndianess)
Read unsigned integer from bitfield.
static tResult Write(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue, tInt nEndianess)
Write unsigned integer to bitfield.
static tResult Read(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T *pValue, tInt nEndianess)
Read signed integer from bitfield.
static tResult Write(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue, tInt nEndianess)
Write signed integer to bitfield.
static tResult Read(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T *pValue, tInt nEndianess)
Read tFloat from bitfield.
static tResult Write(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue, tInt nEndianess)
Write tFloat to bitfield.
static tResult ReadSignal(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T *pValue, tInt nEndianess=PLATFORM_BYTEORDER)
Read value from bitfield.
static tResult WriteSignal(tUInt8 *pBuffer, tSize nStartBit, tSize nBitLength, T nValue, tInt nEndianess=PLATFORM_BYTEORDER)
Write value to bitfield.
static tResult CopyBytesFromBuffer(tUInt8 *pBuffer, tUInt64 *pValue, tSize nStartBit, tSize nBitLength, tUInt64 *pNinthByte, tSize *pBytesToRead)
Copy pBytesToRead number of bytes from the buffer to pValue and pNinthByte.
static tResult CutLeadingBits(tUInt64 *pValue, tSize nBitLength)
Set the highest bits of a tUInt64 value to zero.
tResult Read(tSize nStartBit, tSize nBitLength, T *pValue, tInt nEndianess=PLATFORM_BYTEORDER)
Read value from bitfield.
tResult Write(tSize nStartBit, tSize nBitLength, T nValue, tInt nEndianess=PLATFORM_BYTEORDER)
Write value to bitfield.
tResult CheckForInvalidArguments(tSize nStartBit, tSize nBitLength, tSize nSizeVariable)
Check if the parameters for the reading and writing access are valid.
Memory block class.
Definition: memoryblock.h:20
static tVoid MemCopy(tVoid *pDest, const tVoid *pSrc, tSize nCount)
Copies data from one buffer to another.
tVoid * GetPtr() const
Get a pointer to the allocated memory.
tSize GetSize() const
Get the amount of allocated memory in bytes.
tBool IsFailed() const
Check whether this result object contains an error != ERR_NOERROR.
#define PLATFORM_BIG_ENDIAN_8
defines the big endianess value, that will be retrieved by
Definition: constants.h:121
#define PLATFORM_BYTEORDER
defines a link to __get_platform_byteorder.
Definition: constants.h:124
#define PLATFORM_LITTLE_ENDIAN_8
defines the little endianess value, that will be retrieved by
Definition: constants.h:118
ADTF A_UTIL Namespace - Within adtf this is used as adtf::util or adtf_util.
Definition: d_ptr.h:11