Skip to main content

yscv_video/
hevc_cabac.rs

1//! HEVC CABAC (Context-based Adaptive Binary Arithmetic Coding) entropy decoder.
2//!
3//! Implements the arithmetic decoding engine and context model management
4//! specified in ITU-T H.265 sections 9.3.  This module provides:
5//!
6//! - [`CabacDecoder`] — the arithmetic decoding engine that reads compressed
7//!   bitstream data and produces binary decisions.
8//! - [`ContextModel`] — adaptive probability model with MPS/LPS tracking.
9//! - Binarization helpers for truncated Rice, fixed-length, unary, and
10//!   Exp-Golomb coded syntax elements.
11
12// ---------------------------------------------------------------------------
13// State transition tables (ITU-T H.265, Table 9-45)
14// ---------------------------------------------------------------------------
15
16/// State transition after decoding the **Least Probable Symbol** (LPS).
17/// Indexed by `pStateIdx` (0..=63).
18#[rustfmt::skip]
19const TRANS_IDX_LPS: [u8; 64] = [
20     0,  0,  1,  2,  2,  4,  4,  5,
21     6,  7,  8,  9,  9, 11, 11, 12,
22    13, 13, 15, 15, 16, 16, 18, 18,
23    19, 19, 21, 21, 22, 22, 23, 24,
24    24, 25, 26, 26, 27, 27, 28, 29,
25    29, 30, 30, 30, 31, 32, 32, 33,
26    33, 33, 34, 34, 35, 35, 35, 36,
27    36, 36, 37, 37, 37, 38, 38, 63,
28];
29
30/// State transition after decoding the **Most Probable Symbol** (MPS).
31/// Indexed by `pStateIdx` (0..=63).
32#[rustfmt::skip]
33const TRANS_IDX_MPS: [u8; 64] = [
34     1,  2,  3,  4,  5,  6,  7,  8,
35     9, 10, 11, 12, 13, 14, 15, 16,
36    17, 18, 19, 20, 21, 22, 23, 24,
37    25, 26, 27, 28, 29, 30, 31, 32,
38    33, 34, 35, 36, 37, 38, 39, 40,
39    41, 42, 43, 44, 45, 46, 47, 48,
40    49, 50, 51, 52, 53, 54, 55, 56,
41    57, 58, 59, 60, 61, 62, 62, 63,
42];
43
44// ---------------------------------------------------------------------------
45// Range LPS table (ITU-T H.265, Table 9-48)
46// ---------------------------------------------------------------------------
47
48/// `rangeTabLps[pStateIdx][qRangeIdx]`
49///
50/// 64 rows (one per probability state) x 4 columns (one per quarter-range
51/// index derived from the current interval range).
52#[rustfmt::skip]
53const RANGE_TAB_LPS: [[u8; 4]; 64] = [
54    [128, 176, 208, 240],
55    [128, 167, 197, 227],
56    [128, 158, 187, 216],
57    [123, 150, 178, 205],
58    [116, 142, 169, 195],
59    [111, 135, 160, 185],
60    [105, 128, 152, 175],
61    [100, 122, 144, 166],
62    [ 95, 116, 137, 158],
63    [ 90, 110, 130, 150],
64    [ 85, 104, 123, 142],
65    [ 81,  99, 117, 135],
66    [ 77,  94, 111, 128],
67    [ 73,  89, 105, 122],
68    [ 69,  85, 100, 116],
69    [ 66,  80,  95, 110],
70    [ 62,  76,  90, 104],
71    [ 59,  72,  86,  99],
72    [ 56,  69,  81,  94],
73    [ 53,  65,  77,  89],
74    [ 51,  62,  73,  85],
75    [ 48,  59,  69,  80],
76    [ 46,  56,  66,  76],
77    [ 43,  53,  63,  72],
78    [ 41,  50,  59,  69],
79    [ 39,  48,  56,  65],
80    [ 37,  45,  54,  62],
81    [ 35,  43,  51,  59],
82    [ 33,  41,  48,  56],
83    [ 32,  39,  46,  53],
84    [ 30,  37,  43,  50],
85    [ 29,  35,  41,  48],
86    [ 27,  33,  39,  45],
87    [ 26,  31,  37,  43],
88    [ 24,  30,  35,  41],
89    [ 23,  28,  33,  39],
90    [ 22,  27,  32,  37],
91    [ 21,  26,  30,  35],
92    [ 20,  24,  29,  33],
93    [ 19,  23,  27,  31],
94    [ 18,  22,  26,  30],
95    [ 17,  21,  25,  28],
96    [ 16,  20,  23,  27],
97    [ 15,  19,  22,  25],
98    [ 14,  18,  21,  24],
99    [ 14,  17,  20,  23],
100    [ 13,  16,  19,  22],
101    [ 12,  15,  18,  21],
102    [ 12,  14,  17,  20],
103    [ 11,  14,  16,  19],
104    [ 11,  13,  15,  18],
105    [ 10,  12,  15,  17],
106    [ 10,  12,  14,  16],
107    [  9,  11,  13,  15],
108    [  9,  11,  12,  14],
109    [  8,  10,  12,  14],
110    [  8,   9,  11,  13],
111    [  7,   9,  11,  12],
112    [  7,   9,  10,  12],
113    [  7,   8,  10,  11],
114    [  6,   8,   9,  11],
115    [  6,   7,   9,  10],
116    [  6,   7,   8,   9],
117    [  2,   2,   2,   2],
118];
119
120// ---------------------------------------------------------------------------
121// Context model
122// ---------------------------------------------------------------------------
123
124/// Adaptive probability context model for CABAC (ITU-T H.265, 9.3.1).
125///
126/// Each context stores a 6-bit probability state index (`state`, 0..=63) and
127/// the value of the Most Probable Symbol (`mps`, 0 or 1).
128#[derive(Debug, Clone)]
129pub struct ContextModel {
130    /// Probability state index (0 = equiprobable, 63 = most skewed).
131    pub state: u8,
132    /// Most Probable Symbol value (0 or 1).
133    pub mps: u8,
134}
135
136impl ContextModel {
137    /// Create a context model from an initialisation value (Table 9-4).
138    ///
139    /// The `init_value` encodes both slope and offset for deriving the initial
140    /// state at a given slice QP.  When no QP context is available yet, a
141    /// default QP of 26 is assumed.
142    pub fn new(init_value: u8) -> Self {
143        let mut ctx = ContextModel { state: 0, mps: 0 };
144        ctx.init(26, init_value);
145        ctx
146    }
147
148    /// (Re-)initialise the context for a given `slice_qp` and `init_value`
149    /// (ITU-T H.265, 9.3.1.1).
150    pub fn init(&mut self, slice_qp: i32, init_value: u8) {
151        let slope = ((init_value >> 4) as i32) * 5 - 45;
152        let offset = (((init_value & 15) as i32) << 3) - 16;
153        let init_state = ((slope * (slice_qp.clamp(0, 51) - 16)) >> 4) + offset;
154        let pre_ctx_state = init_state.clamp(1, 126);
155
156        if pre_ctx_state <= 63 {
157            self.state = (63 - pre_ctx_state) as u8;
158            self.mps = 0;
159        } else {
160            self.state = (pre_ctx_state - 64) as u8;
161            self.mps = 1;
162        }
163    }
164}
165
166// ---------------------------------------------------------------------------
167// CABAC arithmetic decoding engine
168// ---------------------------------------------------------------------------
169
170/// CABAC arithmetic decoder for HEVC (ITU-T H.265, 9.3.3).
171///
172/// Reads a byte-aligned NAL unit payload and exposes methods for
173/// context-modelled decisions, bypass bins, and terminating bins.
174pub struct CabacDecoder<'a> {
175    /// Raw NAL unit payload bytes.
176    data: &'a [u8],
177    /// Current byte offset into `data`.
178    offset: usize,
179    /// Number of bits still available in the partially-consumed byte.
180    bits_left: u32,
181    /// Current arithmetic coding range (9-bit value, initialised to 510).
182    range: u32,
183    /// Current arithmetic coding offset / value.
184    value: u32,
185}
186
187impl<'a> CabacDecoder<'a> {
188    /// Construct a new CABAC decoder from a NAL payload slice.
189    ///
190    /// The caller is expected to provide the slice data beginning at the first
191    /// CABAC-coded byte (i.e. immediately after the slice segment header has
192    /// been consumed by a different reader).
193    pub fn new(data: &'a [u8]) -> Self {
194        let mut dec = CabacDecoder {
195            data,
196            offset: 0,
197            bits_left: 0,
198            range: 510,
199            value: 0,
200        };
201        // Bootstrap: read 9 bits into `value` (spec 9.3.2.2).
202        dec.value = dec.read_bits(9);
203        dec
204    }
205
206    // ------------------------------------------------------------------
207    // Bit-level I/O
208    // ------------------------------------------------------------------
209
210    /// Read a single bit from the bitstream.
211    fn read_bit(&mut self) -> u32 {
212        if self.bits_left == 0 {
213            if self.offset < self.data.len() {
214                self.bits_left = 8;
215                self.offset += 1;
216            } else {
217                // Past end of stream — return 0 to avoid panic. Real streams
218                // are always long enough; this is a safety net.
219                return 0;
220            }
221        }
222        self.bits_left -= 1;
223        let byte = self.data[self.offset - 1];
224        (u32::from(byte) >> self.bits_left) & 1
225    }
226
227    /// Read `n` bits (MSB-first) from the bitstream.
228    fn read_bits(&mut self, n: u32) -> u32 {
229        let mut val = 0u32;
230        for _ in 0..n {
231            val = (val << 1) | self.read_bit();
232        }
233        val
234    }
235
236    // ------------------------------------------------------------------
237    // Core decoding primitives (spec 9.3.3.2)
238    // ------------------------------------------------------------------
239
240    /// Renormalise the decoder state (spec 9.3.3.2.2).
241    ///
242    /// Called after every decision bin to ensure `range` stays in
243    /// \[256, 510\].
244    fn renormalize(&mut self) {
245        while self.range < 256 {
246            self.range <<= 1;
247            self.value = (self.value << 1) | self.read_bit();
248        }
249    }
250
251    /// Decode one bin using context-modelled arithmetic coding
252    /// (spec 9.3.3.2.1).
253    ///
254    /// The context model is read and **updated** after decoding.
255    /// Returns `true` for bin value 1, `false` for bin value 0.
256    pub fn decode_decision(&mut self, ctx: &mut ContextModel) -> bool {
257        let q_range_idx = (self.range >> 6) & 3;
258        let range_lps = u32::from(RANGE_TAB_LPS[ctx.state as usize][q_range_idx as usize]);
259        self.range -= range_lps;
260
261        let bin_val;
262        if self.value < self.range {
263            // MPS path
264            bin_val = ctx.mps;
265            ctx.state = TRANS_IDX_MPS[ctx.state as usize];
266        } else {
267            // LPS path
268            bin_val = 1 - ctx.mps;
269            self.value -= self.range;
270            self.range = range_lps;
271
272            if ctx.state == 0 {
273                ctx.mps = 1 - ctx.mps;
274            }
275            ctx.state = TRANS_IDX_LPS[ctx.state as usize];
276        }
277
278        self.renormalize();
279        bin_val != 0
280    }
281
282    /// Decode one bin in bypass (equiprobable) mode (spec 9.3.3.2.3).
283    ///
284    /// No context model is used; the two symbols are equally likely.
285    pub fn decode_bypass(&mut self) -> bool {
286        self.value = (self.value << 1) | self.read_bit();
287
288        if self.value >= self.range {
289            self.value -= self.range;
290            true
291        } else {
292            false
293        }
294    }
295
296    /// Decode the terminating bin (spec 9.3.3.2.4).
297    ///
298    /// Used to detect end-of-slice or end-of-sub-stream.  The interval is
299    /// reduced by 2 first; if the remaining value falls in the upper
300    /// sub-range the terminating condition is signalled.
301    pub fn decode_terminate(&mut self) -> bool {
302        self.range -= 2;
303        if self.value >= self.range {
304            true
305        } else {
306            self.renormalize();
307            false
308        }
309    }
310
311    // ------------------------------------------------------------------
312    // Syntax-element binarization helpers
313    // ------------------------------------------------------------------
314
315    /// Decode a **truncated Rice** (TR) binarized syntax element
316    /// (spec 9.3.2.2).
317    ///
318    /// - `ctx` — context model array; the first entry is used for the prefix
319    ///   unary part, subsequent entries for further bins.
320    /// - `c_max` — maximum value (capped by the binarization).
321    /// - `c_rice_param` — Rice parameter controlling the suffix length.
322    ///
323    /// The prefix is decoded as a **truncated unary** code using context
324    /// models, and the suffix (if any) is decoded in bypass mode.
325    pub fn decode_tr(&mut self, ctx: &mut [ContextModel], c_max: u32, c_rice_param: u32) -> u32 {
326        let prefix_max = c_max >> c_rice_param;
327        // Truncated unary prefix
328        let mut prefix = 0u32;
329        while prefix < prefix_max {
330            let ctx_idx = prefix.min((ctx.len() as u32).saturating_sub(1)) as usize;
331            if self.decode_decision(&mut ctx[ctx_idx]) {
332                prefix += 1;
333            } else {
334                break;
335            }
336        }
337
338        // Suffix — `c_rice_param` bypass bins (binary representation of remainder)
339        let suffix = if c_rice_param > 0 {
340            self.decode_fl_bypass(c_rice_param)
341        } else {
342            0
343        };
344
345        let value = (prefix << c_rice_param) + suffix;
346        value.min(c_max)
347    }
348
349    /// Decode a **fixed-length** (FL) binarized syntax element using bypass
350    /// bins (spec 9.3.3.2.3, used via 9.3.2.4).
351    ///
352    /// Reads `n_bits` bypass bins and returns the resulting value
353    /// (MSB-first).
354    pub fn decode_fl(&mut self, n_bits: u32) -> u32 {
355        self.decode_fl_bypass(n_bits)
356    }
357
358    /// Inner helper: read `n_bits` bypass bins MSB-first.
359    fn decode_fl_bypass(&mut self, n_bits: u32) -> u32 {
360        let mut val = 0u32;
361        for _ in 0..n_bits {
362            val = (val << 1) | u32::from(self.decode_bypass());
363        }
364        val
365    }
366
367    /// Decode a **unary** (U) binarized syntax element using context models.
368    ///
369    /// Returns the number of `1` bins seen before the first `0` bin, capped
370    /// at `max`.  Context index for each bin position `k` is
371    /// `min(k, ctx.len() - 1)`.
372    pub fn decode_unary(&mut self, ctx: &mut [ContextModel], max: u32) -> u32 {
373        let mut val = 0u32;
374        while val < max {
375            let ctx_idx = val.min((ctx.len() as u32).saturating_sub(1)) as usize;
376            if self.decode_decision(&mut ctx[ctx_idx]) {
377                val += 1;
378            } else {
379                break;
380            }
381        }
382        val
383    }
384
385    /// Decode an **Exp-Golomb** coded syntax element using bypass bins
386    /// (spec 9.3.2.5, k-th order).
387    ///
388    /// `k` is the order parameter.  Returns the decoded unsigned value.
389    pub fn decode_eg(&mut self, k: u32) -> u32 {
390        // Prefix: count leading ones (Exp-Golomb uses bypass bins).
391        let mut leading = 0u32;
392        while self.decode_bypass() {
393            leading += 1;
394            // Safety limit — real streams never exceed ~32.
395            if leading > 31 {
396                break;
397            }
398        }
399
400        // The stop bit (0) has already been consumed by the failing
401        // `decode_bypass` above.
402        //
403        // Suffix: read `leading + k` bypass bins.
404        let suffix_len = leading + k;
405        let suffix = self.decode_fl_bypass(suffix_len);
406
407        // Value = ((1 << leading) - 1) << k  +  suffix
408        if leading >= 32 {
409            return suffix;
410        }
411        ((1u32 << leading) - 1).wrapping_shl(k).wrapping_add(suffix)
412    }
413}
414
415// ---------------------------------------------------------------------------
416// Tests
417// ---------------------------------------------------------------------------
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422
423    // -- Context model tests ------------------------------------------------
424
425    #[test]
426    fn context_init_default_qp() {
427        let ctx = ContextModel::new(154);
428        // init_value = 154 => slope = (154 >> 4) * 5 - 45 = 9*5-45 = 0
429        // offset = ((154 & 15) << 3) - 16 = (10 << 3) - 16 = 80 - 16 = 64
430        // init_state = (0 * (26-16)) >> 4 + 64 = 64
431        // pre_ctx_state = clamp(64, 1, 126) = 64
432        // pre_ctx_state > 63 => state = 64 - 64 = 0, mps = 1
433        assert_eq!(ctx.state, 0);
434        assert_eq!(ctx.mps, 1);
435    }
436
437    #[test]
438    fn context_init_high_state() {
439        let ctx = ContextModel::new(255);
440        // slope = (255 >> 4)*5 - 45 = 15*5-45 = 30
441        // offset = ((255 & 15) << 3) - 16 = (15*8)-16 = 104
442        // init_state = (30 * (26-16)) >> 4 + 104 = 300>>4 + 104 = 18+104 = 122
443        // pre_ctx_state = clamp(122, 1, 126) = 122
444        // 122 > 63 => state = 122-64 = 58, mps = 1
445        assert_eq!(ctx.state, 58);
446        assert_eq!(ctx.mps, 1);
447    }
448
449    #[test]
450    fn context_init_low_state() {
451        let ctx = ContextModel::new(0);
452        // slope = 0*5-45 = -45
453        // offset = 0-16 = -16
454        // init_state = (-45*(26-16))>>4 + (-16) = -450>>4 + (-16) = -28 + (-16) = -44
455        // pre_ctx_state = clamp(-44, 1, 126) = 1
456        // 1 <= 63 => state = 63 - 1 = 62, mps = 0
457        assert_eq!(ctx.state, 62);
458        assert_eq!(ctx.mps, 0);
459    }
460
461    #[test]
462    fn context_state_transitions() {
463        let mut ctx = ContextModel { state: 0, mps: 0 };
464        // After MPS at state 0, transition to state 1
465        let old_mps = ctx.mps;
466        ctx.state = TRANS_IDX_MPS[ctx.state as usize];
467        assert_eq!(ctx.state, 1);
468        assert_eq!(ctx.mps, old_mps);
469
470        // After LPS at state 0, state stays 0 but MPS flips
471        ctx.state = 0;
472        ctx.mps = 0;
473        // LPS at state 0 causes MPS flip
474        if ctx.state == 0 {
475            ctx.mps = 1 - ctx.mps;
476        }
477        ctx.state = TRANS_IDX_LPS[ctx.state as usize];
478        assert_eq!(ctx.state, 0);
479        assert_eq!(ctx.mps, 1);
480    }
481
482    #[test]
483    fn context_init_reinit() {
484        let mut ctx = ContextModel::new(154);
485        let orig_state = ctx.state;
486        let orig_mps = ctx.mps;
487        // Reinitialize with same parameters should give the same result.
488        ctx.init(26, 154);
489        assert_eq!(ctx.state, orig_state);
490        assert_eq!(ctx.mps, orig_mps);
491
492        // Reinitialize with different QP should change state.
493        ctx.init(40, 154);
494        // slope=0, offset=64 => init_state = 0 + 64 = 64
495        // Same result because slope is 0.
496        assert_eq!(ctx.state, 0);
497        assert_eq!(ctx.mps, 1);
498    }
499
500    // -- CABAC decoder tests ------------------------------------------------
501
502    #[test]
503    fn cabac_init() {
504        // Ensure decoder construction from minimal data does not panic.
505        let data = [0x00, 0x00, 0x01, 0xFF];
506        let dec = CabacDecoder::new(&data);
507        assert_eq!(dec.range, 510);
508        // value should have consumed 9 bits = 1 full byte + 1 bit
509        // data[0] = 0x00, data[1] = 0x00 => 9 MSBs = 0b0_0000_0000 = 0
510        assert_eq!(dec.value, 0);
511    }
512
513    #[test]
514    fn cabac_bypass_known_pattern() {
515        // Encode bit pattern 1,0,1,1,0,0,1,0 as raw bytes.
516        // After the 9-bit init bootstrap, bypass reads one bit at a time.
517        //
518        // The bypass decode doubles `value` and adds a new bit, then
519        // compares against `range`.  With range=510 after init, after the
520        // first bypass bin the range stays 510 and each call essentially
521        // reads one raw bit (since range > 256 and stays constant in
522        // bypass mode).
523        //
524        // We need to set up bytes so the 9-bit bootstrap reads a known
525        // value and then subsequent bits form our pattern.
526        //
527        // Let's build a bitstream where the first 9 bits are 0 (value=0)
528        // and the following 8 bits are the pattern 10110010.
529        //
530        // Bit layout:  0_0000_0000  1_0110_010(padding)
531        //   byte 0: 0000_0000  = 0x00
532        //   byte 1: 0101_1001  = 0x59
533        //   byte 2: 0000_0000  = 0x00  (padding)
534        let data = [0x00u8, 0x59, 0x00];
535        let mut dec = CabacDecoder::new(&data);
536        assert_eq!(dec.value, 0);
537        assert_eq!(dec.range, 510);
538
539        // With value=0 and range=510, bypass reads the next bit and
540        // doubles value.  If new value >= range => 1, else => 0.
541        //
542        // Because value starts at 0 and range at 510, each bypass step
543        // is: value = value*2 + bit.
544        //
545        // The maths here depend on accumulated value vs range, so we just
546        // verify round-trip consistency by decoding all 8 bits.
547        let mut bits = Vec::new();
548        for _ in 0..8 {
549            bits.push(dec.decode_bypass());
550        }
551        // The first several bypass bins with value=0 and range=510 will
552        // keep producing false until accumulated value exceeds range.
553        // We mainly check no panics and deterministic output.
554        assert_eq!(bits.len(), 8);
555    }
556
557    #[test]
558    fn cabac_terminate_no_end() {
559        // Build a stream where terminate returns false (not end of slice).
560        // range after init = 510.  terminate subtracts 2 => 508.
561        // If value < 508 => not terminated, renormalize.
562        // value = 0 < 508 => false.
563        let data = [0x00u8; 4];
564        let mut dec = CabacDecoder::new(&data);
565        assert!(!dec.decode_terminate());
566    }
567
568    #[test]
569    fn cabac_terminate_end() {
570        // Build stream where the initial 9-bit value equals range-2.
571        // range = 510, so we need value = 508 = 0b1_1111_1100.
572        // As 9 bits: 1_1111_1100.
573        //   byte 0: 1111_1110 = 0xFE
574        //   byte 1: 0xxx_xxxx — we only need 1 bit from here: 0.
575        let data = [0xFE, 0x00];
576        let mut dec = CabacDecoder::new(&data);
577        // value should be 0b111111100 = 508
578        assert_eq!(dec.value, 508);
579        assert!(dec.decode_terminate());
580    }
581
582    #[test]
583    fn cabac_decision_basic() {
584        // Construct a stream with known bits and verify a context-modelled
585        // decode does not panic and produces a deterministic result.
586        let data = [0x00u8; 8];
587        let mut dec = CabacDecoder::new(&data);
588        let mut ctx = ContextModel::new(154);
589
590        // Decode several decision bins — the exact values depend on the
591        // arithmetic state, but it should not panic.
592        let mut results = Vec::new();
593        for _ in 0..10 {
594            results.push(dec.decode_decision(&mut ctx));
595        }
596        assert_eq!(results.len(), 10);
597    }
598
599    #[test]
600    fn cabac_fl_decode() {
601        // Fixed-length decode of 3 bypass bins from known bits.
602        let data = [0x00u8; 4];
603        let mut dec = CabacDecoder::new(&data);
604        let val = dec.decode_fl(3);
605        // With value=0 and range=510, 3 bypass bins starting from 0:
606        // each step doubles value (0) + next bit (0) => all zeros.
607        assert_eq!(val, 0);
608    }
609
610    #[test]
611    fn cabac_unary_decode() {
612        // Unary decode with all-zero stream should produce 0 immediately
613        // (first bin = 0 = stop).
614        let data = [0x00u8; 8];
615        let mut dec = CabacDecoder::new(&data);
616        let mut ctx = [ContextModel::new(154)];
617
618        // With value=0 and a context where mps=1, the first bin decodes
619        // the MPS or LPS depending on range_lps.  We just verify
620        // determinism and no panics.
621        let val = dec.decode_unary(&mut ctx, 5);
622        // The exact value depends on the arithmetic engine state; we
623        // verify it is within bounds.
624        assert!(val <= 5);
625    }
626
627    #[test]
628    fn cabac_eg_decode() {
629        // Exp-Golomb decode from all-zero stream.
630        let data = [0x00u8; 8];
631        let mut dec = CabacDecoder::new(&data);
632        let val = dec.decode_eg(0);
633        // With value=0 and range=510, first bypass bin = false (0), so
634        // leading = 0, suffix_len = 0, suffix = 0, value = 0.
635        assert_eq!(val, 0);
636    }
637
638    #[test]
639    fn range_tab_lps_sanity() {
640        // Verify a few known entries from the spec table.
641        assert_eq!(RANGE_TAB_LPS[0][0], 128);
642        assert_eq!(RANGE_TAB_LPS[0][3], 240);
643        assert_eq!(RANGE_TAB_LPS[63][0], 2);
644        assert_eq!(RANGE_TAB_LPS[63][3], 2);
645        // Row 12: [77, 94, 111, 128]
646        assert_eq!(RANGE_TAB_LPS[12][0], 77);
647        assert_eq!(RANGE_TAB_LPS[12][3], 128);
648    }
649
650    #[test]
651    fn trans_tables_sanity() {
652        // MPS transitions should be monotonically non-decreasing.
653        for i in 0..63 {
654            assert!(TRANS_IDX_MPS[i] >= i as u8);
655        }
656        // LPS transitions should be <= current state (convergence to 0).
657        for i in 0..63 {
658            assert!(TRANS_IDX_LPS[i] <= i as u8);
659        }
660        // Last MPS entry stays at 63.
661        assert_eq!(TRANS_IDX_MPS[63], 63);
662        // Last LPS entry is 63 (special case for state 63).
663        assert_eq!(TRANS_IDX_LPS[63], 63);
664    }
665
666    #[test]
667    fn cabac_decode_tr_basic() {
668        let data = [0x00u8; 8];
669        let mut dec = CabacDecoder::new(&data);
670        let mut ctx = [ContextModel::new(154), ContextModel::new(154)];
671
672        let val = dec.decode_tr(&mut ctx, 4, 0);
673        assert!(val <= 4);
674    }
675
676    #[test]
677    fn cabac_bypass_long_sequence() {
678        // Decode many bypass bins from a known non-trivial pattern to
679        // exercise the renormalization / bit-reading path.
680        let data: Vec<u8> = (0..32).collect();
681        let mut dec = CabacDecoder::new(&data);
682        let mut count_true = 0u32;
683        for _ in 0..100 {
684            if dec.decode_bypass() {
685                count_true += 1;
686            }
687        }
688        // Just verify it ran without panic and produced some mix.
689        assert!(count_true <= 100);
690    }
691}