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

1""" 

2The module implements abstraction for holding bit sequence. 

3""" 

4 

5import typing 

6 

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 

12 

13class BitBuffer: 

14 """ 

15 Bit buffer. 

16 

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 """ 

20 

21 def __init__(self, buffer: bytes, bitsize: typing.Optional[int] = None) -> None: 

22 """ 

23 Constructs bit buffer from bytes buffer and bit size. 

24 

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 """ 

29 

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 

37 

38 def __eq__(self, other: object) -> bool: 

39 if not isinstance(other, BitBuffer): 

40 return False 

41 

42 if self._bitsize != other._bitsize: 

43 return False 

44 

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 

50 

51 if self._masked_last_byte() != other._masked_last_byte(): 

52 return False 

53 

54 return True 

55 

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) 

63 

64 result = calc_hashcode_int32(result, self._masked_last_byte()) 

65 

66 return result 

67 

68 @property 

69 def buffer(self) -> bytes: 

70 """ 

71 Gets the underlying byte buffer. 

72 

73 Not all bits of the last byte must be used. 

74 

75 :returns: The underlying byte buffer. 

76 """ 

77 return self._buffer 

78 

79 @property 

80 def bitsize(self) -> int: 

81 """ 

82 Gets the number of bits stored in the bit buffer. 

83 

84 :returns: Size of the bit buffer in bits. 

85 """ 

86 return self._bitsize 

87 

88 def _masked_last_byte(self) -> int: 

89 rounded_bytesize = self._bitsize // 8 

90 last_byte_bits = self._bitsize - 8 * rounded_bytesize 

91 

92 return (self._buffer[rounded_bytesize - 1] if last_byte_bits == 0 else 

93 self._buffer[rounded_bytesize] & (0xFF << (8 - last_byte_bits))) 

94 

95BitBuffer = import_cpp_class("BitBuffer") or BitBuffer # type: ignore