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}