Coverage for /home/jenkins/workspace/NDS/Zserio/NDS_ZSERIO-linux-build/compiler/extensions/python/runtime/src/zserio/bitbuffer.py: 100%
47 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-12-13 15:12 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-12-13 15:12 +0000
1"""
2The module implements abstraction for holding bit sequence.
3"""
5import typing
7from zserio.exception import PythonRuntimeException
8from zserio.hashcode import HASH_SEED
9from zserio.hashcode import calc_hashcode_int32
10from zserio.bitposition import bitsize_to_bytesize
11from zserio.cppbind import import_cpp_class
13class BitBuffer:
14 """
15 Bit buffer.
17 Because bit buffer size does not have to be byte aligned (divisible by 8), it's possible that not all bits
18 of the last byte are used. In this case, only most significant bits of the corresponded size are used.
19 """
21 def __init__(self, buffer: bytes, bitsize: typing.Optional[int] = None) -> None:
22 """
23 Constructs bit buffer from bytes buffer and bit size.
25 :param buffer: Bytes-like buffer to construct from.
26 :param bitsize: Number of bits stored in buffer to use.
27 :raises PythonRuntimeException: If bitsize is out of range.
28 """
30 if bitsize is None:
31 bitsize = len(buffer) * 8
32 elif len(buffer) * 8 < bitsize:
33 raise PythonRuntimeException(f"BitBuffer: Bit size '{bitsize}' out of range "
34 f"for the given buffer byte size '{len(buffer)}'!")
35 self._buffer: bytes = buffer
36 self._bitsize: int = bitsize
38 def __eq__(self, other: object) -> bool:
39 if not isinstance(other, BitBuffer):
40 return False
42 if self._bitsize != other._bitsize:
43 return False
45 bytesize = bitsize_to_bytesize(self._bitsize)
46 if bytesize > 0:
47 if bytesize > 1:
48 if self._buffer[0:bytesize - 1] != other._buffer[0:bytesize - 1]:
49 return False
51 if self._masked_last_byte() != other._masked_last_byte():
52 return False
54 return True
56 def __hash__(self) -> int:
57 result = HASH_SEED
58 bytesize = bitsize_to_bytesize(self._bitsize)
59 if bytesize > 0:
60 if bytesize > 1:
61 for element in self._buffer[0:bytesize - 1]:
62 result = calc_hashcode_int32(result, element)
64 result = calc_hashcode_int32(result, self._masked_last_byte())
66 return result
68 @property
69 def buffer(self) -> bytes:
70 """
71 Gets the underlying byte buffer.
73 Not all bits of the last byte must be used.
75 :returns: The underlying byte buffer.
76 """
77 return self._buffer
79 @property
80 def bitsize(self) -> int:
81 """
82 Gets the number of bits stored in the bit buffer.
84 :returns: Size of the bit buffer in bits.
85 """
86 return self._bitsize
88 def _masked_last_byte(self) -> int:
89 rounded_bytesize = self._bitsize // 8
90 last_byte_bits = self._bitsize - 8 * rounded_bytesize
92 return (self._buffer[rounded_bytesize - 1] if last_byte_bits == 0 else
93 self._buffer[rounded_bytesize] & (0xFF << (8 - last_byte_bits)))
95BitBuffer = import_cpp_class("BitBuffer") or BitBuffer # type: ignore