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}