Source code for zserio.bitbuffer
"""
The module implements abstraction for holding bit sequence.
"""
import typing
from zserio.exception import PythonRuntimeException
from zserio.hashcode import HASH_SEED
from zserio.hashcode import calc_hashcode_int32
from zserio.bitposition import bitsize_to_bytesize
from zserio.cppbind import import_cpp_class
[docs]class BitBuffer:
"""
Bit buffer.
Because bit buffer size does not have to be byte aligned (divisible by 8), it's possible that not all bits
of the last byte are used. In this case, only most significant bits of the corresponded size are used.
"""
def __init__(self, buffer: bytes, bitsize: typing.Optional[int] = None) -> None:
"""
Constructs bit buffer from bytes buffer and bit size.
:param buffer: Bytes-like buffer to construct from.
:param bitsize: Number of bits stored in buffer to use.
:raises PythonRuntimeException: If bitsize is out of range.
"""
if bitsize is None:
bitsize = len(buffer) * 8
elif len(buffer) * 8 < bitsize:
raise PythonRuntimeException(
f"BitBuffer: Bit size '{bitsize}' out of range "
f"for the given buffer byte size '{len(buffer)}'!"
)
self._buffer: bytes = buffer
self._bitsize: int = bitsize
def __eq__(self, other: object) -> bool:
if not isinstance(other, BitBuffer):
return False
if self._bitsize != other._bitsize:
return False
bytesize = bitsize_to_bytesize(self._bitsize)
if bytesize > 0:
if bytesize > 1:
if self._buffer[0 : bytesize - 1] != other._buffer[0 : bytesize - 1]:
return False
if self._masked_last_byte() != other._masked_last_byte():
return False
return True
def __hash__(self) -> int:
result = HASH_SEED
bytesize = bitsize_to_bytesize(self._bitsize)
if bytesize > 0:
if bytesize > 1:
for element in self._buffer[0 : bytesize - 1]:
result = calc_hashcode_int32(result, element)
result = calc_hashcode_int32(result, self._masked_last_byte())
return result
@property
def buffer(self) -> bytes:
"""
Gets the underlying byte buffer.
Not all bits of the last byte must be used.
:returns: The underlying byte buffer.
"""
return self._buffer
@property
def bitsize(self) -> int:
"""
Gets the number of bits stored in the bit buffer.
:returns: Size of the bit buffer in bits.
"""
return self._bitsize
def _masked_last_byte(self) -> int:
rounded_bytesize = self._bitsize // 8
last_byte_bits = self._bitsize - 8 * rounded_bytesize
return (
self._buffer[rounded_bytesize - 1]
if last_byte_bits == 0
else self._buffer[rounded_bytesize] & (0xFF << (8 - last_byte_bits))
)
BitBuffer = import_cpp_class("BitBuffer") or BitBuffer # type: ignore