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