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        let acc = ((*self.data.get(byte).unwrap_or(&0) as u32) << 24)
129            | ((*self.data.get(byte + 1).unwrap_or(&0) as u32) << 16)
130            | ((*self.data.get(byte + 2).unwrap_or(&0) as u32) << 8)
131            | (*self.data.get(byte + 3).unwrap_or(&0) as u32);
132        // The bit at `pos` is window bit (31 − off); take the `n` bits below it.
133        (acc >> (32 - off - n)) & ((1u32 << n) - 1)
134    }
135
136    /// Consumes `n` bits (advances the position), after a [`peek_bits`]. Rejects
137    /// if the bits run past the end of the buffer — the truncated-stream guard
138    /// that `read_bit`'s per-bit bounds check provided.
139    #[inline]
140    pub fn skip_bits(&mut self, n: u32) -> Result<(), OutOfData> {
141        if self.pos + n as usize > self.bit_len() {
142            return Err(OutOfData);
143        }
144        self.pos += n as usize;
145        Ok(())
146    }
147
148    /// Unsigned Exp-Golomb decode, `ue(v)`.
149    pub fn read_ue(&mut self) -> Result<u32, OutOfData> {
150        // Fast path: a codeword with `lz` leading zeros is `2·lz+1` bits. With
151        // `lz ≤ 11` the whole codeword fits the 24-bit peek window — find `lz`
152        // by counting leading zeros, then extract value in one shot.
153        let window = self.peek_bits(24);
154        let lz = window.leading_zeros() - 8; // leading zeros within the 24-bit window
155        if lz <= 11 {
156            let total = 2 * lz + 1;
157            self.skip_bits(total)?;
158            if lz == 0 {
159                return Ok(0);
160            }
161            let info = (window >> (24 - total)) & ((1u32 << lz) - 1);
162            return Ok((1u32 << lz) - 1 + info);
163        }
164        // ≥12 leading zeros (huge value or run of zeros): exact bit-at-a-time.
165        let mut leading_zeros = 0u32;
166        while !self.read_bit()? {
167            leading_zeros += 1;
168            // 32 leading zeros would make `1 << leading_zeros` overflow u32 (and
169            // the value is not representable anyway) — reject as malformed.
170            if leading_zeros >= 32 {
171                return Err(OutOfData);
172            }
173        }
174        if leading_zeros == 0 {
175            return Ok(0);
176        }
177        let info = self.read_bits(leading_zeros)?;
178        Ok((1u32 << leading_zeros) - 1 + info)
179    }
180
181    /// Signed Exp-Golomb decode, `se(v)`.
182    pub fn read_se(&mut self) -> Result<i32, OutOfData> {
183        let code_num = self.read_ue()?;
184        // Inverse of the se->code_num mapping.
185        let magnitude = code_num.div_ceil(2) as i32;
186        Ok(if code_num % 2 == 1 { magnitude } else { -magnitude })
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use crate::BitWriter;
194
195    #[test]
196    fn roundtrip_ue() {
197        for v in [0u32, 1, 2, 3, 4, 7, 8, 255, 256, 65535, u32::MAX - 1] {
198            let mut w = BitWriter::new();
199            w.write_ue(v);
200            w.align_zero();
201            let bytes = w.into_bytes();
202            let mut r = BitReader::new(&bytes);
203            assert_eq!(r.read_ue().unwrap(), v, "ue roundtrip {v}");
204        }
205    }
206
207    #[test]
208    fn roundtrip_se() {
209        for v in [0i32, 1, -1, 2, -2, 100, -100, 32767, -32768] {
210            let mut w = BitWriter::new();
211            w.write_se(v);
212            w.align_zero();
213            let bytes = w.into_bytes();
214            let mut r = BitReader::new(&bytes);
215            assert_eq!(r.read_se().unwrap(), v, "se roundtrip {v}");
216        }
217    }
218
219    #[test]
220    fn roundtrip_mixed_stream() {
221        let mut w = BitWriter::new();
222        w.write_bits(0b1011, 4);
223        w.write_ue(42);
224        w.write_se(-17);
225        w.write_bits(1, 1);
226        w.align_zero();
227        let bytes = w.into_bytes();
228
229        let mut r = BitReader::new(&bytes);
230        assert_eq!(r.read_bits(4).unwrap(), 0b1011);
231        assert_eq!(r.read_ue().unwrap(), 42);
232        assert_eq!(r.read_se().unwrap(), -17);
233        assert_eq!(r.read_bits(1).unwrap(), 1);
234    }
235
236    #[test]
237    fn reports_out_of_data() {
238        let bytes = [0x80u8];
239        let mut r = BitReader::new(&bytes);
240        assert_eq!(r.read_bits(8).unwrap(), 0x80);
241        assert_eq!(r.read_bit(), Err(OutOfData));
242    }
243
244    #[test]
245    fn peek_then_skip_matches_read_bits() {
246        let bytes = [0xB5u8, 0x3C, 0xF0, 0x0A, 0x77];
247        // At every bit offset and width, peek_bits + skip_bits must equal a
248        // consuming read_bits from a fresh reader at the same position.
249        for start in 0..16u32 {
250            for n in 1..=24u32 {
251                let mut a = BitReader::new(&bytes);
252                a.skip_bits(start).unwrap();
253                let peeked = a.peek_bits(n);
254                let pos_before = a.bit_pos();
255                a.skip_bits(n).unwrap();
256                assert_eq!(a.bit_pos(), pos_before + n as usize);
257
258                let mut b = BitReader::new(&bytes);
259                b.skip_bits(start).unwrap();
260                assert_eq!(peeked, b.read_bits(n).unwrap(), "start={start} n={n}");
261            }
262        }
263    }
264
265    #[test]
266    fn peek_zero_fills_past_end() {
267        let bytes = [0xFFu8];
268        let mut r = BitReader::new(&bytes);
269        r.skip_bits(4).unwrap();
270        // 4 real bits (1111) then zero-fill: 0b1111_0000_0000... for 12 bits.
271        assert_eq!(r.peek_bits(12), 0b1111_0000_0000);
272        // skipping past the end is rejected.
273        assert_eq!(r.skip_bits(8), Err(OutOfData));
274    }
275}