Coverage for ccsds124/bitbuffer.py: 100%

40 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-06-29 15:01 +0000

1""" 

2Variable-length bit buffer for building compressed output. 

3 

4This module provides a dynamically-growing bit buffer for constructing 

5compressed output streams. Bits are appended sequentially using MSB-first 

6ordering as required by CCSDS 124.0-B-1. 

7 

8Bit Ordering: 

9Bits are appended MSB-first within each byte: 

10- First bit appended goes to bit position 7 

11- Second bit goes to position 6, etc. 

12 

13MicroPython compatible - no typing module imports. 

14""" 

15 

16# Import for type hints only - works at runtime 

17if False: # noqa: SIM108 

18 from ccsds124.bitvector import BitVector 

19 

20 

21class BitBuffer: 

22 """Variable-length bit buffer for building compressed output.""" 

23 

24 def __init__(self) -> None: 

25 """Initialize an empty bit buffer.""" 

26 self._data = bytearray() 

27 self.num_bits = 0 

28 

29 def clear(self) -> None: 

30 """Clear the buffer.""" 

31 self._data = bytearray() 

32 self.num_bits = 0 

33 

34 def append_bit(self, bit: int) -> None: 

35 """ 

36 Append a single bit to the buffer. 

37 

38 Args: 

39 bit: Bit value (0 or non-zero for 1) 

40 """ 

41 byte_index = self.num_bits // 8 

42 bit_index = self.num_bits % 8 

43 

44 # Extend buffer if needed 

45 while byte_index >= len(self._data): 

46 self._data.append(0) 

47 

48 # CCSDS uses MSB-first bit ordering: first bit goes to position 7 

49 if bit: 

50 self._data[byte_index] |= 1 << (7 - bit_index) 

51 

52 self.num_bits += 1 

53 

54 def append_bits(self, data: bytes, num_bits: int) -> None: 

55 """ 

56 Append multiple bits from bytes. 

57 

58 Args: 

59 data: Source bytes 

60 num_bits: Number of bits to append (MSB-first) 

61 """ 

62 for i in range(num_bits): 

63 byte_index = i // 8 

64 bit_index = i % 8 

65 

66 # Extract bits MSB-first (bit 7, 6, 5, ..., 0) 

67 bit = (data[byte_index] >> (7 - bit_index)) & 1 

68 self.append_bit(bit) 

69 

70 def append_bitvector(self, bv: "BitVector") -> None: 

71 """ 

72 Append all bits from a BitVector. 

73 

74 Args: 

75 bv: BitVector to append 

76 """ 

77 # Calculate number of bytes from bit length 

78 num_bytes = (bv.length + 7) // 8 

79 

80 # CCSDS MSB-first: bytes in order, bits within each byte from MSB to LSB 

81 for byte_idx in range(num_bytes): 

82 bits_in_this_byte = 8 

83 

84 # Last byte may have fewer than 8 bits 

85 if byte_idx == num_bytes - 1: 

86 remainder = bv.length % 8 

87 if remainder != 0: 

88 bits_in_this_byte = remainder 

89 

90 # With MSB-first bitvector indexing: bit 0 is MSB, bit 7 is LSB 

91 # We want to append bits in order: MSB first, LSB last 

92 start_bit = byte_idx * 8 

93 for bit_offset in range(bits_in_this_byte): 

94 pos = start_bit + bit_offset 

95 bit = bv.get_bit(pos) 

96 self.append_bit(bit) 

97 

98 def to_bytes(self) -> bytes: 

99 """ 

100 Convert buffer contents to bytes. 

101 

102 Returns: 

103 Bytes representation of the buffer 

104 """ 

105 if self.num_bits == 0: 

106 return b"" 

107 

108 # Calculate number of bytes needed 

109 num_bytes = (self.num_bits + 7) // 8 

110 return bytes(self._data[:num_bytes])