Skip to main content

wedeo_codec/
bitstream.rs

1//! Bitstream reading utilities for codec implementations.
2//!
3//! Re-exports the `av-bitstream` crate's reader types and provides
4//! exp-Golomb parsing functions matching FFmpeg's `libavcodec/golomb.h`.
5
6pub use av_bitstream::bitread::{BitRead, BitReadBE, BitReadLE};
7
8use wedeo_core::{Error, Result};
9
10/// Maximum number of leading zeros allowed in an exp-Golomb code.
11///
12/// FFmpeg's `get_ue_golomb_long` supports up to 31 leading zeros (reading
13/// a 32-bit peek). We use the same limit.
14const MAX_EXP_GOLOMB_LEADING_ZEROS: u32 = 31;
15
16/// Read an unsigned exp-Golomb code (ue(v)) from a big-endian bitstream reader.
17///
18/// Matches FFmpeg's `get_ue_golomb_long` from `libavcodec/golomb.h`:
19/// count leading zeros by peeking 32 bits, then read the value bits.
20///
21/// Returns `Error::InvalidData` if the code has more than 31 leading zeros
22/// (which would require reading more than 32 bits total).
23pub fn get_ue_golomb(br: &mut BitReadBE<'_>) -> Result<u32> {
24    // Peek up to 32 bits to find the leading '1' bit.
25    // We need at least 1 bit available; peek_bits_32 returns 0 if no bits.
26    let buf = br.peek_bits_32(32);
27
28    if buf == 0 {
29        // All 32 peeked bits are zero — the code is too long.
30        return Err(Error::InvalidData);
31    }
32
33    // Count leading zeros: position of highest set bit.
34    // For buf != 0, leading_zeros is 0..=31.
35    let leading_zeros = buf.leading_zeros();
36
37    if leading_zeros > MAX_EXP_GOLOMB_LEADING_ZEROS {
38        return Err(Error::InvalidData);
39    }
40
41    // Skip the leading zeros.
42    br.skip_bits(leading_zeros as usize);
43
44    // Read (leading_zeros + 1) bits: the '1' prefix plus the value suffix.
45    let val = br.get_bits_32(leading_zeros as usize + 1);
46
47    // The decoded value is val - 1.
48    Ok(val - 1)
49}
50
51/// Read a signed exp-Golomb code (se(v)) from a big-endian bitstream reader.
52///
53/// Matches FFmpeg's `get_se_golomb_long` from `libavcodec/golomb.h`:
54/// reads an unsigned code, then maps even values to negative and odd to positive.
55///
56/// Mapping: ue=0 -> 0, ue=1 -> 1, ue=2 -> -1, ue=3 -> 2, ue=4 -> -2, ...
57pub fn get_se_golomb(br: &mut BitReadBE<'_>) -> Result<i32> {
58    let buf = get_ue_golomb(br)?;
59
60    // FFmpeg formula: sign = (buf & 1) - 1; result = ((buf >> 1) ^ sign) + 1
61    // When buf is odd:  sign = 0,  result = (buf >> 1) + 1     (positive)
62    // When buf is even: sign = -1, result = -(buf >> 1)         (negative, or 0 when buf=0)
63    let sign = (buf & 1) as i32 - 1; // 0 for odd, -1 for even
64    Ok(((buf >> 1) as i32 ^ sign) + 1)
65}
66
67/// Read a truncated exp-Golomb code (te(v)) from a big-endian bitstream reader.
68///
69/// Matches FFmpeg's `get_te_golomb` from `libavcodec/golomb.h`:
70/// if range == 2, reads a single bit and XORs with 1; otherwise falls through
71/// to unsigned exp-Golomb.
72///
73/// Panics if `range` < 1 (matching FFmpeg's `av_assert2(range >= 1)`).
74pub fn get_te_golomb(br: &mut BitReadBE<'_>, range: u32) -> Result<u32> {
75    assert!(range >= 1, "get_te_golomb: range must be >= 1");
76
77    if range == 2 {
78        Ok(u32::from(br.get_bit()) ^ 1)
79    } else {
80        get_ue_golomb(br)
81    }
82}
83
84/// Read a truncated exp-Golomb code (te(v)) with range==1 returning 0.
85///
86/// Matches FFmpeg's `get_te0_golomb` from `libavcodec/golomb.h`:
87/// if range == 1, returns 0 without reading; if range == 2, reads a single
88/// bit XOR 1; otherwise falls through to unsigned exp-Golomb.
89///
90/// Panics if `range` < 1 (matching FFmpeg's `av_assert2(range >= 1)`).
91pub fn get_te0_golomb(br: &mut BitReadBE<'_>, range: u32) -> Result<u32> {
92    assert!(range >= 1, "get_te0_golomb: range must be >= 1");
93
94    if range == 1 {
95        Ok(0)
96    } else if range == 2 {
97        Ok(u32::from(br.get_bit()) ^ 1)
98    } else {
99        get_ue_golomb(br)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    /// Helper: create a BitReadBE from a byte slice.
108    ///
109    /// The `av-bitstream` reader does 8-byte reads for cache refills, so we
110    /// pad the input to at least 8 bytes to avoid reading out of bounds.
111    fn make_reader(data: &[u8]) -> BitReadBE<'_> {
112        BitReadBE::new(data)
113    }
114
115    // --- Unsigned exp-Golomb (ue) tests ---
116
117    #[test]
118    fn ue_value_0() {
119        // 0 => code "1" => bits: 1000_0000 = 0x80
120        let data = [0x80, 0, 0, 0, 0, 0, 0, 0];
121        let mut br = make_reader(&data);
122        assert_eq!(get_ue_golomb(&mut br).unwrap(), 0);
123    }
124
125    #[test]
126    fn ue_value_1() {
127        // 1 => code "010" => bits: 0100_0000 = 0x40
128        let data = [0x40, 0, 0, 0, 0, 0, 0, 0];
129        let mut br = make_reader(&data);
130        assert_eq!(get_ue_golomb(&mut br).unwrap(), 1);
131    }
132
133    #[test]
134    fn ue_value_2() {
135        // 2 => code "011" => bits: 0110_0000 = 0x60
136        let data = [0x60, 0, 0, 0, 0, 0, 0, 0];
137        let mut br = make_reader(&data);
138        assert_eq!(get_ue_golomb(&mut br).unwrap(), 2);
139    }
140
141    #[test]
142    fn ue_value_3() {
143        // 3 => code "00100" => bits: 0010_0000 = 0x20
144        let data = [0x20, 0, 0, 0, 0, 0, 0, 0];
145        let mut br = make_reader(&data);
146        assert_eq!(get_ue_golomb(&mut br).unwrap(), 3);
147    }
148
149    #[test]
150    fn ue_value_4() {
151        // 4 => code "00101" => bits: 0010_1000 = 0x28
152        let data = [0x28, 0, 0, 0, 0, 0, 0, 0];
153        let mut br = make_reader(&data);
154        assert_eq!(get_ue_golomb(&mut br).unwrap(), 4);
155    }
156
157    #[test]
158    fn ue_value_5() {
159        // 5 => code "00110" => bits: 0011_0000 = 0x30
160        let data = [0x30, 0, 0, 0, 0, 0, 0, 0];
161        let mut br = make_reader(&data);
162        assert_eq!(get_ue_golomb(&mut br).unwrap(), 5);
163    }
164
165    #[test]
166    fn ue_value_6() {
167        // 6 => code "00111" => bits: 0011_1000 = 0x38
168        let data = [0x38, 0, 0, 0, 0, 0, 0, 0];
169        let mut br = make_reader(&data);
170        assert_eq!(get_ue_golomb(&mut br).unwrap(), 6);
171    }
172
173    #[test]
174    fn ue_sequential_values() {
175        // Read multiple ue codes in sequence: 0 (1), 1 (010), 2 (011)
176        // Bits: 1 010 011 0 = 0xA6 (1010_0110)
177        let data = [0xA6, 0, 0, 0, 0, 0, 0, 0];
178        let mut br = make_reader(&data);
179        assert_eq!(get_ue_golomb(&mut br).unwrap(), 0); // "1"
180        assert_eq!(get_ue_golomb(&mut br).unwrap(), 1); // "010"
181        assert_eq!(get_ue_golomb(&mut br).unwrap(), 2); // "011"
182    }
183
184    #[test]
185    fn ue_error_all_zeros() {
186        // All zeros — no '1' bit found in 32-bit peek.
187        let data = [0u8; 8];
188        let mut br = make_reader(&data);
189        assert_eq!(get_ue_golomb(&mut br), Err(Error::InvalidData));
190    }
191
192    // --- Signed exp-Golomb (se) tests ---
193
194    #[test]
195    fn se_mapping() {
196        // ue=0 -> se=0, ue=1 -> se=1, ue=2 -> se=-1, ue=3 -> se=2, ue=4 -> se=-2
197
198        // se=0: code for ue=0 is "1" = 0x80
199        let data = [0x80, 0, 0, 0, 0, 0, 0, 0];
200        let mut br = make_reader(&data);
201        assert_eq!(get_se_golomb(&mut br).unwrap(), 0);
202
203        // se=1: code for ue=1 is "010" = 0x40
204        let data = [0x40, 0, 0, 0, 0, 0, 0, 0];
205        let mut br = make_reader(&data);
206        assert_eq!(get_se_golomb(&mut br).unwrap(), 1);
207
208        // se=-1: code for ue=2 is "011" = 0x60
209        let data = [0x60, 0, 0, 0, 0, 0, 0, 0];
210        let mut br = make_reader(&data);
211        assert_eq!(get_se_golomb(&mut br).unwrap(), -1);
212
213        // se=2: code for ue=3 is "00100" = 0x20
214        let data = [0x20, 0, 0, 0, 0, 0, 0, 0];
215        let mut br = make_reader(&data);
216        assert_eq!(get_se_golomb(&mut br).unwrap(), 2);
217
218        // se=-2: code for ue=4 is "00101" = 0x28
219        let data = [0x28, 0, 0, 0, 0, 0, 0, 0];
220        let mut br = make_reader(&data);
221        assert_eq!(get_se_golomb(&mut br).unwrap(), -2);
222    }
223
224    // --- Truncated exp-Golomb (te) tests ---
225
226    #[test]
227    fn te_range_2_bit_0() {
228        // range==2: read 1 bit, XOR with 1.
229        // Bit = 0 => result = 0 ^ 1 = 1
230        let data = [0x00, 0, 0, 0, 0, 0, 0, 0]; // first bit is 0
231        let mut br = make_reader(&data);
232        assert_eq!(get_te_golomb(&mut br, 2).unwrap(), 1);
233    }
234
235    #[test]
236    fn te_range_2_bit_1() {
237        // range==2: read 1 bit, XOR with 1.
238        // Bit = 1 => result = 1 ^ 1 = 0
239        let data = [0x80, 0, 0, 0, 0, 0, 0, 0]; // first bit is 1
240        let mut br = make_reader(&data);
241        assert_eq!(get_te_golomb(&mut br, 2).unwrap(), 0);
242    }
243
244    #[test]
245    fn te_range_gt2_falls_through_to_ue() {
246        // range > 2: fall through to ue. Code "1" => ue=0
247        let data = [0x80, 0, 0, 0, 0, 0, 0, 0];
248        let mut br = make_reader(&data);
249        assert_eq!(get_te_golomb(&mut br, 5).unwrap(), 0);
250    }
251
252    // --- te0 variant tests ---
253
254    #[test]
255    fn te0_range_1_returns_zero() {
256        // range==1: returns 0 without reading any bits.
257        let data = [0xFF, 0, 0, 0, 0, 0, 0, 0];
258        let mut br = make_reader(&data);
259        assert_eq!(get_te0_golomb(&mut br, 1).unwrap(), 0);
260    }
261
262    #[test]
263    fn te0_range_2_reads_bit() {
264        // range==2: same as te, read 1 bit XOR 1.
265        let data = [0x80, 0, 0, 0, 0, 0, 0, 0]; // first bit = 1 => 1^1=0
266        let mut br = make_reader(&data);
267        assert_eq!(get_te0_golomb(&mut br, 2).unwrap(), 0);
268    }
269
270    #[test]
271    fn te0_range_gt2_falls_through_to_ue() {
272        // range > 2: fall through to ue. Code "010" => ue=1
273        let data = [0x40, 0, 0, 0, 0, 0, 0, 0];
274        let mut br = make_reader(&data);
275        assert_eq!(get_te0_golomb(&mut br, 8).unwrap(), 1);
276    }
277
278    // --- Panic tests ---
279
280    #[test]
281    #[should_panic(expected = "range must be >= 1")]
282    fn te_panics_on_range_0() {
283        let data = [0x80, 0, 0, 0, 0, 0, 0, 0];
284        let mut br = make_reader(&data);
285        let _ = get_te_golomb(&mut br, 0);
286    }
287
288    #[test]
289    #[should_panic(expected = "range must be >= 1")]
290    fn te0_panics_on_range_0() {
291        let data = [0x80, 0, 0, 0, 0, 0, 0, 0];
292        let mut br = make_reader(&data);
293        let _ = get_te0_golomb(&mut br, 0);
294    }
295}