Coverage for ccsds124/bitreader.py: 100%
31 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-29 15:01 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-29 15:01 +0000
1"""
2Sequential bit reader for decompression.
4This module provides sequential bit-level reading from bytes,
5used for parsing compressed CCSDS 124.0-B-1 streams.
7Bit Ordering:
8Bits are read MSB-first within each byte (matching BitBuffer output):
9- First bit read is bit position 7 (MSB)
10- Last bit read is bit position 0 (LSB)
12MicroPython compatible - no typing module imports.
13"""
16class BitReader:
17 """Sequential bit reader from bytes."""
19 def __init__(self, data: bytes) -> None:
20 """
21 Initialize a bit reader.
23 Args:
24 data: Bytes to read from
25 """
26 self._data = data
27 self._total_bits = len(data) * 8
28 self.position = 0
30 @property
31 def remaining(self) -> int:
32 """Number of bits remaining to read."""
33 return self._total_bits - self.position
35 def peek_bit(self) -> int:
36 """
37 Peek at the next bit without consuming it.
39 Returns:
40 Next bit value (0 or 1)
42 Raises:
43 EOFError: If no more bits available
44 """
45 if self.position >= self._total_bits:
46 raise EOFError("No more bits to read")
48 byte_index = self.position // 8
49 bit_index = self.position % 8
51 # MSB-first: bit 0 in stream is bit 7 of first byte
52 return (self._data[byte_index] >> (7 - bit_index)) & 1
54 def read_bit(self) -> int:
55 """
56 Read and consume a single bit.
58 Returns:
59 Bit value (0 or 1)
61 Raises:
62 EOFError: If no more bits available
63 """
64 bit = self.peek_bit()
65 self.position += 1
66 return bit
68 def read_bits(self, num_bits: int) -> int:
69 """
70 Read and consume multiple bits as an integer.
72 Args:
73 num_bits: Number of bits to read
75 Returns:
76 Integer value of bits (MSB-first)
78 Raises:
79 EOFError: If not enough bits available
80 """
81 if num_bits == 0:
82 return 0
84 if self.position + num_bits > self._total_bits:
85 raise EOFError(f"Not enough bits: need {num_bits}, have {self.remaining}")
87 result = 0
88 for _ in range(num_bits):
89 result = (result << 1) | self.read_bit()
91 return result
93 def align_byte(self) -> None:
94 """
95 Advance to the next byte boundary.
97 If already at a byte boundary, does nothing.
98 Used for packet separation in compressed streams.
99 """
100 bit_offset = self.position % 8
101 if bit_offset != 0:
102 self.position += 8 - bit_offset