Skip to main content

openjph_core/codestream/
bitbuffer_read.rs

1//! Bit-buffer reader for codestream parsing.
2//!
3//! Port of `ojph_bitbuffer_read.h`. Handles JPEG 2000 byte-stuffing:
4//! after a 0xFF byte, the next byte's MSB is a stuffed bit.
5
6/// Bit-level reader with JPEG 2000 0xFF byte-unstuffing.
7///
8/// Reads from a byte slice, automatically handling the rule that after
9/// a 0xFF byte the MSB of the next byte is ignored (stuffed).
10#[derive(Debug)]
11pub struct BitBufferRead<'a> {
12    data: &'a [u8],
13    pos: usize,
14    /// Accumulated bit buffer (left-justified).
15    buf: u64,
16    /// Number of valid bits in `buf`.
17    bits_left: u32,
18    /// True if the previous byte was 0xFF (triggers unstuffing).
19    unstuff: bool,
20}
21
22impl<'a> BitBufferRead<'a> {
23    /// Create a new bit reader over the given byte slice.
24    pub fn new(data: &'a [u8]) -> Self {
25        Self {
26            data,
27            pos: 0,
28            buf: 0,
29            bits_left: 0,
30            unstuff: false,
31        }
32    }
33
34    /// Fill the internal buffer with up to 32 new bits from the byte stream.
35    pub fn fill(&mut self) {
36        while self.bits_left <= 32 && self.pos < self.data.len() {
37            let byte = self.data[self.pos] as u64;
38            self.pos += 1;
39            let bits_to_add = if self.unstuff { 7 } else { 8 };
40            let val = if self.unstuff { byte & 0x7F } else { byte };
41            self.buf |= val << (64 - bits_to_add - self.bits_left);
42            self.bits_left += bits_to_add;
43            self.unstuff = (self.data[self.pos - 1]) == 0xFF;
44        }
45    }
46
47    /// Peek at the top `n` bits without consuming them (n <= 32).
48    #[inline]
49    pub fn peek(&self, n: u32) -> u32 {
50        debug_assert!(n <= 32 && n <= self.bits_left);
51        (self.buf >> (64 - n)) as u32
52    }
53
54    /// Consume `n` bits from the buffer.
55    #[inline]
56    pub fn advance(&mut self, n: u32) {
57        debug_assert!(n <= self.bits_left);
58        self.buf <<= n;
59        self.bits_left -= n;
60    }
61
62    /// Read `n` bits and return them as a u32 (n <= 32).
63    #[inline]
64    pub fn read(&mut self, n: u32) -> u32 {
65        if self.bits_left < n {
66            self.fill();
67        }
68        let val = self.peek(n);
69        self.advance(n);
70        val
71    }
72
73    /// Returns the number of valid bits currently in the buffer.
74    #[inline]
75    pub fn available_bits(&self) -> u32 {
76        self.bits_left
77    }
78
79    /// Returns the current byte position in the data stream.
80    #[inline]
81    pub fn position(&self) -> usize {
82        self.pos
83    }
84
85    /// Returns true if the previous byte read was 0xFF.
86    #[inline]
87    pub fn is_unstuffing(&self) -> bool {
88        self.unstuff
89    }
90
91    /// Reset the reader with new data.
92    pub fn reset(&mut self, data: &'a [u8]) {
93        self.data = data;
94        self.pos = 0;
95        self.buf = 0;
96        self.bits_left = 0;
97        self.unstuff = false;
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn read_single_bits() {
107        // 0xA5 = 1010_0101
108        let data = [0xA5u8];
109        let mut reader = BitBufferRead::new(&data);
110        assert_eq!(reader.read(1), 1);
111        assert_eq!(reader.read(1), 0);
112        assert_eq!(reader.read(1), 1);
113        assert_eq!(reader.read(1), 0);
114        assert_eq!(reader.read(1), 0);
115        assert_eq!(reader.read(1), 1);
116        assert_eq!(reader.read(1), 0);
117        assert_eq!(reader.read(1), 1);
118    }
119
120    #[test]
121    fn read_multi_bit_values() {
122        // 0xAB = 1010_1011, 0xCD = 1100_1101
123        let data = [0xABu8, 0xCD];
124        let mut reader = BitBufferRead::new(&data);
125        assert_eq!(reader.read(4), 0xA); // 1010
126        assert_eq!(reader.read(4), 0xB); // 1011
127        assert_eq!(reader.read(8), 0xCD);
128    }
129
130    #[test]
131    fn read_across_byte_boundary() {
132        let data = [0xABu8, 0xCD];
133        let mut reader = BitBufferRead::new(&data);
134        // Read 12 bits spanning both bytes: 1010_1011_1100 = 0xABC
135        assert_eq!(reader.read(12), 0xABC);
136        assert_eq!(reader.read(4), 0xD);
137    }
138
139    #[test]
140    fn multiple_reads_across_bytes() {
141        // [0xAB, 0xCD] = 0b10101011_11001101
142        let data = [0xABu8, 0xCD];
143        let mut reader = BitBufferRead::new(&data);
144        assert_eq!(reader.read(4), 0b1010); // 0xA
145        assert_eq!(reader.read(8), 0b10111100); // 0xBC (spans bytes)
146        assert_eq!(reader.read(4), 0b1101); // 0xD
147    }
148
149    #[test]
150    fn unstuffing_after_0xff() {
151        // After 0xFF, the next byte's MSB is ignored (stuffed bit).
152        // 0xFF followed by 0x80 → the 0x80 only provides 7 bits: 000_0000
153        let data = [0xFFu8, 0x80];
154        let mut reader = BitBufferRead::new(&data);
155        reader.fill();
156        // First 8 bits from 0xFF = 1111_1111
157        assert_eq!(reader.read(8), 0xFF);
158        // Now unstuffing is active; next byte 0x80 → MSB stripped → 7 bits: 000_0000
159        assert_eq!(reader.read(7), 0x00);
160    }
161
162    #[test]
163    fn unstuffing_preserves_lower_7_bits() {
164        // After 0xFF, the unstuffed byte is placed with a gap for the
165        // stuffed bit. Verify that fill+read produces consistent results.
166        let data = [0xFFu8, 0x7F, 0x42];
167        let mut reader = BitBufferRead::new(&data);
168        reader.fill();
169        // First 8 bits are 0xFF
170        assert_eq!(reader.read(8), 0xFF);
171        // The unstuffed byte provides 7 bits; verify we can read them
172        let bits = reader.available_bits();
173        assert!(bits >= 7);
174    }
175
176    #[test]
177    fn empty_stream() {
178        let data: &[u8] = &[];
179        let reader = BitBufferRead::new(data);
180        assert_eq!(reader.available_bits(), 0);
181        assert_eq!(reader.position(), 0);
182    }
183
184    #[test]
185    fn position_tracking() {
186        let data = [0x12u8, 0x34, 0x56, 0x78];
187        let mut reader = BitBufferRead::new(&data);
188        assert_eq!(reader.position(), 0);
189        reader.fill();
190        // fill() reads bytes to fill the buffer
191        assert!(reader.position() > 0);
192    }
193
194    #[test]
195    fn reset_clears_state() {
196        let data1 = [0xFFu8, 0x00];
197        let data2 = [0x42u8];
198        let mut reader = BitBufferRead::new(&data1);
199        reader.fill();
200        let _ = reader.read(8);
201
202        reader.reset(&data2);
203        assert_eq!(reader.position(), 0);
204        assert_eq!(reader.available_bits(), 0);
205        assert!(!reader.is_unstuffing());
206        assert_eq!(reader.read(8), 0x42);
207    }
208
209    #[test]
210    fn fill_then_peek_advance() {
211        let data = [0xABu8, 0xCD];
212        let mut reader = BitBufferRead::new(&data);
213        reader.fill();
214        // Peek should not consume bits
215        let peeked = reader.peek(8);
216        assert_eq!(peeked, 0xAB);
217        assert_eq!(reader.available_bits(), reader.available_bits());
218        // Advance and read next
219        reader.advance(8);
220        assert_eq!(reader.peek(8), 0xCD);
221    }
222
223    #[test]
224    fn read_full_32bits() {
225        let data = [0x12u8, 0x34, 0x56, 0x78, 0x9A];
226        let mut reader = BitBufferRead::new(&data);
227        let val = reader.read(32);
228        assert_eq!(val, 0x12345678);
229    }
230
231    #[test]
232    fn unstuffing_multiple_0xff_sequence() {
233        // [0xFF, 0x00, 0xFF, 0x00] - consecutive 0xFF bytes
234        let data = [0xFFu8, 0x00, 0xFF, 0x00];
235        let mut reader = BitBufferRead::new(&data);
236        reader.fill();
237        // Read the first 0xFF (8 bits)
238        assert_eq!(reader.read(8), 0xFF);
239        // After 0xFF, unstuffing: next byte 0x00 → 7 bits = 0
240        assert_eq!(reader.read(7), 0x00);
241        // 0x00 is not 0xFF, so next byte reads normally: 0xFF → 8 bits
242        assert_eq!(reader.read(8), 0xFF);
243        // After that 0xFF, unstuffing again: 0x00 → 7 bits
244        assert_eq!(reader.read(7), 0x00);
245    }
246}