Skip to main content

rusty_h264_common/
bit_reader.rs

1//! MSB-first bit reader with H.264 Exp-Golomb decoding.
2//!
3//! The inverse of [`crate::BitWriter`]. Operates over an RBSP byte slice
4//! (emulation-prevention bytes already removed — see [`crate::nal`]).
5
6/// Error returned when a read runs past the end of the buffer.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct OutOfData;
9
10impl core::fmt::Display for OutOfData {
11    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
12        f.write_str("bit reader ran out of data")
13    }
14}
15
16impl std::error::Error for OutOfData {}
17
18/// A big-endian, MSB-first bit reader.
19#[derive(Debug, Clone)]
20pub struct BitReader<'a> {
21    data: &'a [u8],
22    /// Absolute bit position from the start of `data`.
23    pos: usize,
24}
25
26impl<'a> BitReader<'a> {
27    /// Wraps an RBSP byte slice.
28    pub fn new(data: &'a [u8]) -> Self {
29        Self { data, pos: 0 }
30    }
31
32    /// The underlying RBSP buffer (for handing off to the CABAC engine).
33    pub fn data(&self) -> &'a [u8] {
34        self.data
35    }
36
37    /// Current bit position.
38    pub fn bit_pos(&self) -> usize {
39        self.pos
40    }
41
42    /// Total number of bits in the buffer.
43    pub fn bit_len(&self) -> usize {
44        self.data.len() * 8
45    }
46
47    /// Bits remaining.
48    pub fn bits_left(&self) -> usize {
49        self.bit_len().saturating_sub(self.pos)
50    }
51
52    /// `more_rbsp_data()` (spec §7.2): true while the read position is before the
53    /// `rbsp_stop_one_bit` (the last set bit in the buffer). Used to detect the
54    /// end of `slice_data()` when a picture is split into multiple slices.
55    pub fn more_rbsp_data(&self) -> bool {
56        // Stop bit = the last 1 bit in the buffer; in MSB-first order that is the
57        // lowest set bit of the last non-zero byte.
58        let stop = self
59            .data
60            .iter()
61            .enumerate()
62            .rev()
63            .find(|(_, &b)| b != 0)
64            .map(|(bi, &b)| bi * 8 + (7 - b.trailing_zeros() as usize));
65        match stop {
66            Some(s) => self.pos < s,
67            None => false,
68        }
69    }
70
71    /// `true` if the read position sits on a byte boundary.
72    pub fn is_byte_aligned(&self) -> bool {
73        self.pos % 8 == 0
74    }
75
76    /// Advances to the next byte boundary, consuming the intervening bits (e.g.
77    /// `pcm_alignment_zero_bit`s before an `I_PCM` payload).
78    pub fn align_to_byte(&mut self) -> Result<(), OutOfData> {
79        while self.pos % 8 != 0 {
80            self.read_bit()?;
81        }
82        Ok(())
83    }
84
85    /// Reads a single bit.
86    pub fn read_bit(&mut self) -> Result<bool, OutOfData> {
87        if self.pos >= self.bit_len() {
88            return Err(OutOfData);
89        }
90        let byte = self.data[self.pos / 8];
91        let bit = (byte >> (7 - (self.pos % 8))) & 1;
92        self.pos += 1;
93        Ok(bit == 1)
94    }
95
96    /// Reads `n` bits (`n` <= 32) as an unsigned value, MSB first. `u(n)`.
97    pub fn read_bits(&mut self, n: u32) -> Result<u32, OutOfData> {
98        // More than 32 bits cannot fit a u32. Rather than panic on a hostile
99        // length (e.g. a corrupt log2_* field driving the count), reject it.
100        if n > 32 {
101            return Err(OutOfData);
102        }
103        if n == 0 {
104            return Ok(0);
105        }
106        if n <= 24 {
107            let v = self.peek_bits(n);
108            self.skip_bits(n)?;
109            return Ok(v);
110        }
111        // n in 25..=32: two chunks (peek_bits caps at 24).
112        let hi = self.read_bits(n - 16)?;
113        let lo = self.read_bits(16)?;
114        Ok((hi << 16) | lo)
115    }
116
117    /// Peeks the next `n` bits (`n` ≤ 24) as an MSB-first value **without
118    /// consuming**, zero-filling past the end of the buffer. O(1): loads up to 4
119    /// bytes. The zero-fill lets a VLC/Exp-Golomb table match be attempted at the
120    /// stream end; the caller then [`skip_bits`](Self::skip_bits)s the matched
121    /// length, which rejects (OutOfData) if those bits ran past the buffer.
122    #[inline]
123    pub fn peek_bits(&self, n: u32) -> u32 {
124        debug_assert!(n <= 24);
125        let byte = self.pos / 8;
126        let off = (self.pos % 8) as u32;
127        // 4 bytes (zero past end), MSB-first, into a 32-bit window.
128        //
129        // FAST PATH: one RANGE check and one 4-byte big-endian load, which LLVM
130        // lowers to `mov` + `bswap`. The previous form did FOUR separate
131        // bounds-checked `get().unwrap_or(&0)` loads plus three shifts and three ORs,
132        // on a function the entropy decoder calls tens of millions of times per
133        // sequence. No `unsafe` needed — `get(range)` + `from_be_bytes` is safe and
134        // compiles to the same thing an unchecked load would.
135        //
136        // The slow arm keeps the EXACT zero-fill-past-the-end contract the doc
137        // comment promises (and that VLC matching at stream end relies on), so the
138        // two arms are indistinguishable to every caller; `peek_bits_tail_matches`
139        // pins that across the whole boundary region.
140        let acc = match self.data.get(byte..byte + 4) {
141            Some(c) => u32::from_be_bytes([c[0], c[1], c[2], c[3]]),
142            None => {
143                ((*self.data.get(byte).unwrap_or(&0) as u32) << 24)
144                    | ((*self.data.get(byte + 1).unwrap_or(&0) as u32) << 16)
145                    | ((*self.data.get(byte + 2).unwrap_or(&0) as u32) << 8)
146                    | (*self.data.get(byte + 3).unwrap_or(&0) as u32)
147            }
148        };
149        // The bit at `pos` is window bit (31 − off); take the `n` bits below it.
150        (acc >> (32 - off - n)) & ((1u32 << n) - 1)
151    }
152
153    /// Consumes `n` bits (advances the position), after a [`peek_bits`]. Rejects
154    /// if the bits run past the end of the buffer — the truncated-stream guard
155    /// that `read_bit`'s per-bit bounds check provided.
156    #[inline]
157    pub fn skip_bits(&mut self, n: u32) -> Result<(), OutOfData> {
158        if self.pos + n as usize > self.bit_len() {
159            return Err(OutOfData);
160        }
161        self.pos += n as usize;
162        Ok(())
163    }
164
165    /// Unsigned Exp-Golomb decode, `ue(v)`.
166    pub fn read_ue(&mut self) -> Result<u32, OutOfData> {
167        // Fast path: a codeword with `lz` leading zeros is `2·lz+1` bits. With
168        // `lz ≤ 11` the whole codeword fits the 24-bit peek window — find `lz`
169        // by counting leading zeros, then extract value in one shot.
170        let window = self.peek_bits(24);
171        let lz = window.leading_zeros() - 8; // leading zeros within the 24-bit window
172        if lz <= 11 {
173            let total = 2 * lz + 1;
174            self.skip_bits(total)?;
175            if lz == 0 {
176                return Ok(0);
177            }
178            let info = (window >> (24 - total)) & ((1u32 << lz) - 1);
179            return Ok((1u32 << lz) - 1 + info);
180        }
181        // ≥12 leading zeros (huge value or run of zeros): exact bit-at-a-time.
182        let mut leading_zeros = 0u32;
183        while !self.read_bit()? {
184            leading_zeros += 1;
185            // 32 leading zeros would make `1 << leading_zeros` overflow u32 (and
186            // the value is not representable anyway) — reject as malformed.
187            if leading_zeros >= 32 {
188                return Err(OutOfData);
189            }
190        }
191        if leading_zeros == 0 {
192            return Ok(0);
193        }
194        let info = self.read_bits(leading_zeros)?;
195        Ok((1u32 << leading_zeros) - 1 + info)
196    }
197
198    /// Signed Exp-Golomb decode, `se(v)`.
199    pub fn read_se(&mut self) -> Result<i32, OutOfData> {
200        let code_num = self.read_ue()?;
201        // Inverse of the se->code_num mapping.
202        let magnitude = code_num.div_ceil(2) as i32;
203        Ok(if code_num % 2 == 1 { magnitude } else { -magnitude })
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    use crate::BitWriter;
211
212/// The one-load fast path must equal the byte-at-a-time zero-fill reference at
213    /// EVERY bit position and width — including the last bytes, where the fast arm
214    /// stops applying and the tail arm takes over. A mismatch there would corrupt
215    /// VLC matching only at stream end, which is exactly the bug a mid-buffer test
216    /// would miss.
217    #[test]
218    fn peek_bits_matches_zero_fill_reference() {
219        fn reference(data: &[u8], pos: usize, n: u32) -> u32 {
220            let byte = pos / 8;
221            let off = (pos % 8) as u32;
222            let acc = ((*data.get(byte).unwrap_or(&0) as u32) << 24)
223                | ((*data.get(byte + 1).unwrap_or(&0) as u32) << 16)
224                | ((*data.get(byte + 2).unwrap_or(&0) as u32) << 8)
225                | (*data.get(byte + 3).unwrap_or(&0) as u32);
226            (acc >> (32 - off - n)) & ((1u32 << n) - 1)
227        }
228        let mut st = 0x1234_5678u32;
229        let mut rnd = || {
230            st ^= st << 13;
231            st ^= st >> 17;
232            st ^= st << 5;
233            st
234        };
235        for len in 1..24usize {
236            let data: Vec<u8> = (0..len).map(|_| (rnd() >> 7) as u8).collect();
237            let r = BitReader::new(&data);
238            for pos in 0..len * 8 {
239                for n in 1..=24u32 {
240                    let mut br = BitReader::new(&data);
241                    br.skip_bits(pos as u32).ok();
242                    // skip_bits refuses past the end; drive `pos` directly instead.
243                    let mut probe = BitReader::new(&data);
244                    while probe.bit_pos() < pos {
245                        if probe.read_bit().is_err() {
246                            break;
247                        }
248                    }
249                    if probe.bit_pos() != pos {
250                        continue;
251                    }
252                    assert_eq!(
253                        probe.peek_bits(n),
254                        reference(&data, pos, n),
255                        "len={len} pos={pos} n={n}"
256                    );
257                }
258            }
259            let _ = r;
260        }
261    }
262
263    #[test]
264    fn roundtrip_ue() {
265        for v in [0u32, 1, 2, 3, 4, 7, 8, 255, 256, 65535, u32::MAX - 1] {
266            let mut w = BitWriter::new();
267            w.write_ue(v);
268            w.align_zero();
269            let bytes = w.into_bytes();
270            let mut r = BitReader::new(&bytes);
271            assert_eq!(r.read_ue().unwrap(), v, "ue roundtrip {v}");
272        }
273    }
274
275    #[test]
276    fn roundtrip_se() {
277        for v in [0i32, 1, -1, 2, -2, 100, -100, 32767, -32768] {
278            let mut w = BitWriter::new();
279            w.write_se(v);
280            w.align_zero();
281            let bytes = w.into_bytes();
282            let mut r = BitReader::new(&bytes);
283            assert_eq!(r.read_se().unwrap(), v, "se roundtrip {v}");
284        }
285    }
286
287    #[test]
288    fn roundtrip_mixed_stream() {
289        let mut w = BitWriter::new();
290        w.write_bits(0b1011, 4);
291        w.write_ue(42);
292        w.write_se(-17);
293        w.write_bits(1, 1);
294        w.align_zero();
295        let bytes = w.into_bytes();
296
297        let mut r = BitReader::new(&bytes);
298        assert_eq!(r.read_bits(4).unwrap(), 0b1011);
299        assert_eq!(r.read_ue().unwrap(), 42);
300        assert_eq!(r.read_se().unwrap(), -17);
301        assert_eq!(r.read_bits(1).unwrap(), 1);
302    }
303
304    #[test]
305    fn reports_out_of_data() {
306        let bytes = [0x80u8];
307        let mut r = BitReader::new(&bytes);
308        assert_eq!(r.read_bits(8).unwrap(), 0x80);
309        assert_eq!(r.read_bit(), Err(OutOfData));
310    }
311
312    #[test]
313    fn peek_then_skip_matches_read_bits() {
314        let bytes = [0xB5u8, 0x3C, 0xF0, 0x0A, 0x77];
315        // At every bit offset and width, peek_bits + skip_bits must equal a
316        // consuming read_bits from a fresh reader at the same position.
317        for start in 0..16u32 {
318            for n in 1..=24u32 {
319                let mut a = BitReader::new(&bytes);
320                a.skip_bits(start).unwrap();
321                let peeked = a.peek_bits(n);
322                let pos_before = a.bit_pos();
323                a.skip_bits(n).unwrap();
324                assert_eq!(a.bit_pos(), pos_before + n as usize);
325
326                let mut b = BitReader::new(&bytes);
327                b.skip_bits(start).unwrap();
328                assert_eq!(peeked, b.read_bits(n).unwrap(), "start={start} n={n}");
329            }
330        }
331    }
332
333    #[test]
334    fn peek_zero_fills_past_end() {
335        let bytes = [0xFFu8];
336        let mut r = BitReader::new(&bytes);
337        r.skip_bits(4).unwrap();
338        // 4 real bits (1111) then zero-fill: 0b1111_0000_0000... for 12 bits.
339        assert_eq!(r.peek_bits(12), 0b1111_0000_0000);
340        // skipping past the end is rejected.
341        assert_eq!(r.skip_bits(8), Err(OutOfData));
342    }
343}