#![allow(dead_code)]
use crate::bitstream::{BitError, BitReader};
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum CavlcError {
#[error("bitstream read failed: {0}")]
Bitstream(#[from] BitError),
#[error("no matching coeff_token codeword (nC={nc})")]
UnknownCoeffToken { nc: i32 },
#[error("level_prefix exceeded 32 leading zeros")]
LevelPrefixOverflow,
#[error("no matching total_zeros codeword (total_coeff={tc})")]
UnknownTotalZeros { tc: u32 },
#[error("no matching run_before codeword (zeros_left={zl})")]
UnknownRunBefore { zl: u32 },
#[error("invalid total_coeff={tc} for CAVLC (max {max})")]
InvalidTotalCoeff { tc: u32, max: u32 },
#[error("invalid trailing_ones={t1} for total_coeff={tc}")]
InvalidTrailingOnes { t1: u32, tc: u32 },
#[error("invalid CAVLC block span start_idx={start_idx} > end_idx={end_idx}")]
InvalidBlockSpan { start_idx: u32, end_idx: u32 },
#[error("invalid CAVLC max_num_coeff={max} (legal: 4, 8, 15, 16)")]
InvalidMaxNumCoeff { max: u32 },
#[error("CAVLC block span {span} exceeds max_num_coeff={max}")]
BlockSpanExceedsMax { span: u32, max: u32 },
}
pub type CavlcResult<T> = Result<T, CavlcError>;
#[derive(Debug, Clone, Copy)]
pub enum CoeffTokenContext {
Numeric(i32),
ChromaDc420,
ChromaDc422,
}
impl CoeffTokenContext {
pub(crate) fn select_table(self) -> &'static [CoeffTokenRow] {
match self {
CoeffTokenContext::Numeric(nc) => {
if nc < 2 {
&TABLE_9_5_COL0
} else if nc < 4 {
&TABLE_9_5_COL1
} else if nc < 8 {
&TABLE_9_5_COL2
} else {
&TABLE_9_5_COL3
}
}
CoeffTokenContext::ChromaDc420 => &TABLE_9_5_COL_NC_M1,
CoeffTokenContext::ChromaDc422 => &TABLE_9_5_COL_NC_M2,
}
}
fn nc_for_error(self) -> i32 {
match self {
CoeffTokenContext::Numeric(nc) => nc,
CoeffTokenContext::ChromaDc420 => -1,
CoeffTokenContext::ChromaDc422 => -2,
}
}
}
fn decode_vlc<T: Copy>(
r: &mut BitReader<'_>,
rows: &[(u32, u8, T)],
) -> Result<Option<T>, BitError> {
let max_len = rows.iter().map(|(_, l, _)| *l).max().unwrap_or(0);
if max_len == 0 {
return Ok(None);
}
let mut acc: u32 = 0;
let mut len: u8 = 0;
while len < max_len {
acc = (acc << 1) | r.u(1)?;
len += 1;
for (bits, rlen, value) in rows {
if *rlen == len && *bits == acc {
return Ok(Some(*value));
}
}
}
Ok(None)
}
pub(crate) type CoeffTokenRow = (u32, u8, (u32, u32));
#[rustfmt::skip]
static TABLE_9_5_COL0: [CoeffTokenRow; 62] = [
(0b1, 1, (0, 0)),
(0b000101, 6, (1, 0)),
(0b01, 2, (1, 1)),
(0b00000111, 8, (2, 0)),
(0b000100, 6, (2, 1)),
(0b001, 3, (2, 2)),
(0b000000111, 9, (3, 0)),
(0b00000110, 8, (3, 1)),
(0b0000101, 7, (3, 2)),
(0b00011, 5, (3, 3)),
(0b0000000111, 10, (4, 0)),
(0b000000110, 9, (4, 1)),
(0b00000101, 8, (4, 2)),
(0b000011, 6, (4, 3)),
(0b00000000111, 11, (5, 0)),
(0b0000000110, 10, (5, 1)),
(0b000000101, 9, (5, 2)),
(0b0000100, 7, (5, 3)),
(0b0000000001111, 13, (6, 0)),
(0b00000000110, 11, (6, 1)),
(0b0000000101, 10, (6, 2)),
(0b00000100, 8, (6, 3)),
(0b0000000001011, 13, (7, 0)),
(0b0000000001110, 13, (7, 1)),
(0b00000000101, 11, (7, 2)),
(0b000000100, 9, (7, 3)),
(0b0000000001000, 13, (8, 0)),
(0b0000000001010, 13, (8, 1)),
(0b0000000001101, 13, (8, 2)),
(0b0000000100, 10, (8, 3)),
(0b00000000001111, 14, (9, 0)),
(0b00000000001110, 14, (9, 1)),
(0b0000000001001, 13, (9, 2)),
(0b00000000100, 11, (9, 3)),
(0b00000000001011, 14, (10, 0)),
(0b00000000001010, 14, (10, 1)),
(0b00000000001101, 14, (10, 2)),
(0b0000000001100, 13, (10, 3)),
(0b000000000001111, 15, (11, 0)),
(0b000000000001110, 15, (11, 1)),
(0b00000000001001, 14, (11, 2)),
(0b00000000001100, 14, (11, 3)),
(0b000000000001011, 15, (12, 0)),
(0b000000000001010, 15, (12, 1)),
(0b000000000001101, 15, (12, 2)),
(0b00000000001000, 14, (12, 3)),
(0b0000000000001111, 16, (13, 0)),
(0b000000000000001, 15, (13, 1)),
(0b000000000001001, 15, (13, 2)),
(0b000000000001100, 15, (13, 3)),
(0b0000000000001011, 16, (14, 0)),
(0b0000000000001110, 16, (14, 1)),
(0b0000000000001101, 16, (14, 2)),
(0b000000000001000, 15, (14, 3)),
(0b0000000000000111, 16, (15, 0)),
(0b0000000000001010, 16, (15, 1)),
(0b0000000000001001, 16, (15, 2)),
(0b0000000000001100, 16, (15, 3)),
(0b0000000000000100, 16, (16, 0)),
(0b0000000000000110, 16, (16, 1)),
(0b0000000000000101, 16, (16, 2)),
(0b0000000000001000, 16, (16, 3)),
];
#[rustfmt::skip]
static TABLE_9_5_COL1: [CoeffTokenRow; 62] = [
(0b11, 2, (0, 0)),
(0b001011, 6, (1, 0)),
(0b10, 2, (1, 1)),
(0b000111, 6, (2, 0)),
(0b00111, 5, (2, 1)),
(0b011, 3, (2, 2)),
(0b0000111, 7, (3, 0)),
(0b001010, 6, (3, 1)),
(0b001001, 6, (3, 2)),
(0b0101, 4, (3, 3)),
(0b00000111, 8, (4, 0)),
(0b000110, 6, (4, 1)),
(0b000101, 6, (4, 2)),
(0b0100, 4, (4, 3)),
(0b00000100, 8, (5, 0)),
(0b0000110, 7, (5, 1)),
(0b0000101, 7, (5, 2)),
(0b00110, 5, (5, 3)),
(0b000000111, 9, (6, 0)),
(0b00000110, 8, (6, 1)),
(0b00000101, 8, (6, 2)),
(0b001000, 6, (6, 3)),
(0b00000001111, 11, (7, 0)),
(0b000000110, 9, (7, 1)),
(0b000000101, 9, (7, 2)),
(0b000100, 6, (7, 3)),
(0b00000001011, 11, (8, 0)),
(0b00000001110, 11, (8, 1)),
(0b00000001101, 11, (8, 2)),
(0b0000100, 7, (8, 3)),
(0b000000001111, 12, (9, 0)),
(0b00000001010, 11, (9, 1)),
(0b00000001001, 11, (9, 2)),
(0b000000100, 9, (9, 3)),
(0b000000001011, 12, (10, 0)),
(0b000000001110, 12, (10, 1)),
(0b000000001101, 12, (10, 2)),
(0b00000001100, 11, (10, 3)),
(0b000000001000, 12, (11, 0)),
(0b000000001010, 12, (11, 1)),
(0b000000001001, 12, (11, 2)),
(0b00000001000, 11, (11, 3)),
(0b0000000001111, 13, (12, 0)),
(0b0000000001110, 13, (12, 1)),
(0b0000000001101, 13, (12, 2)),
(0b000000001100, 12, (12, 3)),
(0b0000000001011, 13, (13, 0)),
(0b0000000001010, 13, (13, 1)),
(0b0000000001001, 13, (13, 2)),
(0b0000000001100, 13, (13, 3)),
(0b0000000000111, 13, (14, 0)),
(0b00000000001011, 14, (14, 1)),
(0b0000000000110, 13, (14, 2)),
(0b0000000001000, 13, (14, 3)),
(0b00000000001001, 14, (15, 0)),
(0b00000000001000, 14, (15, 1)),
(0b00000000001010, 14, (15, 2)),
(0b0000000000001, 13, (15, 3)),
(0b00000000000111, 14, (16, 0)),
(0b00000000000110, 14, (16, 1)),
(0b00000000000101, 14, (16, 2)),
(0b00000000000100, 14, (16, 3)),
];
#[rustfmt::skip]
static TABLE_9_5_COL2: [CoeffTokenRow; 62] = [
(0b1111, 4, (0, 0)),
(0b001111, 6, (1, 0)),
(0b1110, 4, (1, 1)),
(0b001011, 6, (2, 0)),
(0b01111, 5, (2, 1)),
(0b1101, 4, (2, 2)),
(0b001000, 6, (3, 0)),
(0b01100, 5, (3, 1)),
(0b01110, 5, (3, 2)),
(0b1100, 4, (3, 3)),
(0b0001111, 7, (4, 0)),
(0b01010, 5, (4, 1)),
(0b01011, 5, (4, 2)),
(0b1011, 4, (4, 3)),
(0b0001011, 7, (5, 0)),
(0b01000, 5, (5, 1)),
(0b01001, 5, (5, 2)),
(0b1010, 4, (5, 3)),
(0b0001001, 7, (6, 0)),
(0b001110, 6, (6, 1)),
(0b001101, 6, (6, 2)),
(0b1001, 4, (6, 3)),
(0b0001000, 7, (7, 0)),
(0b001010, 6, (7, 1)),
(0b001001, 6, (7, 2)),
(0b1000, 4, (7, 3)),
(0b00001111, 8, (8, 0)),
(0b0001110, 7, (8, 1)),
(0b0001101, 7, (8, 2)),
(0b01101, 5, (8, 3)),
(0b00001011, 8, (9, 0)),
(0b00001110, 8, (9, 1)),
(0b0001010, 7, (9, 2)),
(0b001100, 6, (9, 3)),
(0b000001111, 9, (10, 0)),
(0b00001010, 8, (10, 1)),
(0b00001101, 8, (10, 2)),
(0b0001100, 7, (10, 3)),
(0b000001011, 9, (11, 0)),
(0b000001110, 9, (11, 1)),
(0b00001001, 8, (11, 2)),
(0b00001100, 8, (11, 3)),
(0b000001000, 9, (12, 0)),
(0b000001010, 9, (12, 1)),
(0b000001101, 9, (12, 2)),
(0b00001000, 8, (12, 3)),
(0b0000001101, 10, (13, 0)),
(0b000000111, 9, (13, 1)),
(0b000001001, 9, (13, 2)),
(0b000001100, 9, (13, 3)),
(0b0000001001, 10, (14, 0)),
(0b0000001100, 10, (14, 1)),
(0b0000001011, 10, (14, 2)),
(0b0000001010, 10, (14, 3)),
(0b0000000101, 10, (15, 0)),
(0b0000001000, 10, (15, 1)),
(0b0000000111, 10, (15, 2)),
(0b0000000110, 10, (15, 3)),
(0b0000000001, 10, (16, 0)),
(0b0000000100, 10, (16, 1)),
(0b0000000011, 10, (16, 2)),
(0b0000000010, 10, (16, 3)),
];
#[rustfmt::skip]
static TABLE_9_5_COL3: [CoeffTokenRow; 62] = [
(0b000011, 6, (0, 0)),
(0b000000, 6, (1, 0)),
(0b000001, 6, (1, 1)),
(0b000100, 6, (2, 0)),
(0b000101, 6, (2, 1)),
(0b000110, 6, (2, 2)),
(0b001000, 6, (3, 0)),
(0b001001, 6, (3, 1)),
(0b001010, 6, (3, 2)),
(0b001011, 6, (3, 3)),
(0b001100, 6, (4, 0)),
(0b001101, 6, (4, 1)),
(0b001110, 6, (4, 2)),
(0b001111, 6, (4, 3)),
(0b010000, 6, (5, 0)),
(0b010001, 6, (5, 1)),
(0b010010, 6, (5, 2)),
(0b010011, 6, (5, 3)),
(0b010100, 6, (6, 0)),
(0b010101, 6, (6, 1)),
(0b010110, 6, (6, 2)),
(0b010111, 6, (6, 3)),
(0b011000, 6, (7, 0)),
(0b011001, 6, (7, 1)),
(0b011010, 6, (7, 2)),
(0b011011, 6, (7, 3)),
(0b011100, 6, (8, 0)),
(0b011101, 6, (8, 1)),
(0b011110, 6, (8, 2)),
(0b011111, 6, (8, 3)),
(0b100000, 6, (9, 0)),
(0b100001, 6, (9, 1)),
(0b100010, 6, (9, 2)),
(0b100011, 6, (9, 3)),
(0b100100, 6, (10, 0)),
(0b100101, 6, (10, 1)),
(0b100110, 6, (10, 2)),
(0b100111, 6, (10, 3)),
(0b101000, 6, (11, 0)),
(0b101001, 6, (11, 1)),
(0b101010, 6, (11, 2)),
(0b101011, 6, (11, 3)),
(0b101100, 6, (12, 0)),
(0b101101, 6, (12, 1)),
(0b101110, 6, (12, 2)),
(0b101111, 6, (12, 3)),
(0b110000, 6, (13, 0)),
(0b110001, 6, (13, 1)),
(0b110010, 6, (13, 2)),
(0b110011, 6, (13, 3)),
(0b110100, 6, (14, 0)),
(0b110101, 6, (14, 1)),
(0b110110, 6, (14, 2)),
(0b110111, 6, (14, 3)),
(0b111000, 6, (15, 0)),
(0b111001, 6, (15, 1)),
(0b111010, 6, (15, 2)),
(0b111011, 6, (15, 3)),
(0b111100, 6, (16, 0)),
(0b111101, 6, (16, 1)),
(0b111110, 6, (16, 2)),
(0b111111, 6, (16, 3)),
];
#[rustfmt::skip]
static TABLE_9_5_COL_NC_M1: [CoeffTokenRow; 14] = [
(0b01, 2, (0, 0)),
(0b000111, 6, (1, 0)),
(0b1, 1, (1, 1)),
(0b000100, 6, (2, 0)),
(0b000110, 6, (2, 1)),
(0b001, 3, (2, 2)),
(0b000011, 6, (3, 0)),
(0b0000011, 7, (3, 1)),
(0b0000010, 7, (3, 2)),
(0b000101, 6, (3, 3)),
(0b000010, 6, (4, 0)),
(0b00000011, 8, (4, 1)),
(0b00000010, 8, (4, 2)),
(0b0000000, 7, (4, 3)),
];
#[rustfmt::skip]
static TABLE_9_5_COL_NC_M2: [CoeffTokenRow; 30] = [
(0b1, 1, (0, 0)),
(0b0001111, 7, (1, 0)),
(0b01, 2, (1, 1)),
(0b0001110, 7, (2, 0)),
(0b0001101, 7, (2, 1)),
(0b001, 3, (2, 2)),
(0b000000111, 9, (3, 0)),
(0b0001100, 7, (3, 1)),
(0b0001011, 7, (3, 2)),
(0b00001, 5, (3, 3)),
(0b000000110, 9, (4, 0)),
(0b000000101, 9, (4, 1)),
(0b0001010, 7, (4, 2)),
(0b000001, 6, (4, 3)),
(0b0000000111, 10, (5, 0)),
(0b0000000110, 10, (5, 1)),
(0b000000100, 9, (5, 2)),
(0b0001001, 7, (5, 3)),
(0b00000000111, 11, (6, 0)),
(0b00000000110, 11, (6, 1)),
(0b0000000101, 10, (6, 2)),
(0b0001000, 7, (6, 3)),
(0b000000000111, 12, (7, 0)),
(0b000000000110, 12, (7, 1)),
(0b00000000101, 11, (7, 2)),
(0b0000000100, 10, (7, 3)),
(0b0000000000111, 13, (8, 0)),
(0b000000000101, 12, (8, 1)),
(0b000000000100, 12, (8, 2)),
(0b00000000100, 11, (8, 3)),
];
#[derive(Debug, Clone, Copy)]
pub enum TotalZerosTable {
Luma,
ChromaDc420,
ChromaDc422,
}
#[rustfmt::skip]
static TZ_LUMA_TZVLC_1: [(u32, u8, u32); 16] = [
(0b1, 1, 0),
(0b011, 3, 1),
(0b010, 3, 2),
(0b0011, 4, 3),
(0b0010, 4, 4),
(0b00011, 5, 5),
(0b00010, 5, 6),
(0b000011, 6, 7),
(0b000010, 6, 8),
(0b0000011, 7, 9),
(0b0000010, 7, 10),
(0b00000011, 8, 11),
(0b00000010, 8, 12),
(0b000000011, 9, 13),
(0b000000010, 9, 14),
(0b000000001, 9, 15),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_2: [(u32, u8, u32); 15] = [
(0b111, 3, 0),
(0b110, 3, 1),
(0b101, 3, 2),
(0b100, 3, 3),
(0b011, 3, 4),
(0b0101, 4, 5),
(0b0100, 4, 6),
(0b0011, 4, 7),
(0b0010, 4, 8),
(0b00011, 5, 9),
(0b00010, 5, 10),
(0b000011, 6, 11),
(0b000010, 6, 12),
(0b000001, 6, 13),
(0b000000, 6, 14),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_3: [(u32, u8, u32); 14] = [
(0b0101, 4, 0),
(0b111, 3, 1),
(0b110, 3, 2),
(0b101, 3, 3),
(0b0100, 4, 4),
(0b0011, 4, 5),
(0b100, 3, 6),
(0b011, 3, 7),
(0b0010, 4, 8),
(0b00011, 5, 9),
(0b00010, 5, 10),
(0b000001, 6, 11),
(0b00001, 5, 12),
(0b000000, 6, 13),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_4: [(u32, u8, u32); 13] = [
(0b00011, 5, 0),
(0b111, 3, 1),
(0b0101, 4, 2),
(0b0100, 4, 3),
(0b110, 3, 4),
(0b101, 3, 5),
(0b100, 3, 6),
(0b0011, 4, 7),
(0b011, 3, 8),
(0b0010, 4, 9),
(0b00010, 5, 10),
(0b00001, 5, 11),
(0b00000, 5, 12),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_5: [(u32, u8, u32); 12] = [
(0b0101, 4, 0),
(0b0100, 4, 1),
(0b0011, 4, 2),
(0b111, 3, 3),
(0b110, 3, 4),
(0b101, 3, 5),
(0b100, 3, 6),
(0b011, 3, 7),
(0b0010, 4, 8),
(0b00001, 5, 9),
(0b0001, 4, 10),
(0b00000, 5, 11),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_6: [(u32, u8, u32); 11] = [
(0b000001, 6, 0),
(0b00001, 5, 1),
(0b111, 3, 2),
(0b110, 3, 3),
(0b101, 3, 4),
(0b100, 3, 5),
(0b011, 3, 6),
(0b010, 3, 7),
(0b0001, 4, 8),
(0b001, 3, 9),
(0b000000, 6, 10),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_7: [(u32, u8, u32); 10] = [
(0b000001, 6, 0),
(0b00001, 5, 1),
(0b101, 3, 2),
(0b100, 3, 3),
(0b011, 3, 4),
(0b11, 2, 5),
(0b010, 3, 6),
(0b0001, 4, 7),
(0b001, 3, 8),
(0b000000, 6, 9),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_8: [(u32, u8, u32); 9] = [
(0b000001, 6, 0),
(0b0001, 4, 1),
(0b00001, 5, 2),
(0b011, 3, 3),
(0b11, 2, 4),
(0b10, 2, 5),
(0b010, 3, 6),
(0b001, 3, 7),
(0b000000, 6, 8),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_9: [(u32, u8, u32); 8] = [
(0b000001, 6, 0),
(0b000000, 6, 1),
(0b0001, 4, 2),
(0b11, 2, 3),
(0b10, 2, 4),
(0b001, 3, 5),
(0b01, 2, 6),
(0b00001, 5, 7),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_10: [(u32, u8, u32); 7] = [
(0b00001, 5, 0),
(0b00000, 5, 1),
(0b001, 3, 2),
(0b11, 2, 3),
(0b10, 2, 4),
(0b01, 2, 5),
(0b0001, 4, 6),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_11: [(u32, u8, u32); 6] = [
(0b0000, 4, 0),
(0b0001, 4, 1),
(0b001, 3, 2),
(0b010, 3, 3),
(0b1, 1, 4),
(0b011, 3, 5),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_12: [(u32, u8, u32); 5] = [
(0b0000, 4, 0),
(0b0001, 4, 1),
(0b01, 2, 2),
(0b1, 1, 3),
(0b001, 3, 4),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_13: [(u32, u8, u32); 4] = [
(0b000, 3, 0),
(0b001, 3, 1),
(0b1, 1, 2),
(0b01, 2, 3),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_14: [(u32, u8, u32); 3] = [
(0b00, 2, 0),
(0b01, 2, 1),
(0b1, 1, 2),
];
#[rustfmt::skip]
static TZ_LUMA_TZVLC_15: [(u32, u8, u32); 2] = [
(0b0, 1, 0),
(0b1, 1, 1),
];
#[rustfmt::skip]
static TZ_CHROMA_420_TZVLC_1: [(u32, u8, u32); 4] = [
(0b1, 1, 0),
(0b01, 2, 1),
(0b001, 3, 2),
(0b000, 3, 3),
];
#[rustfmt::skip]
static TZ_CHROMA_420_TZVLC_2: [(u32, u8, u32); 3] = [
(0b1, 1, 0),
(0b01, 2, 1),
(0b00, 2, 2),
];
#[rustfmt::skip]
static TZ_CHROMA_420_TZVLC_3: [(u32, u8, u32); 2] = [
(0b1, 1, 0),
(0b0, 1, 1),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_1: [(u32, u8, u32); 8] = [
(0b1, 1, 0),
(0b010, 3, 1),
(0b011, 3, 2),
(0b0010, 4, 3),
(0b0011, 4, 4),
(0b0001, 4, 5),
(0b00001, 5, 6),
(0b00000, 5, 7),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_2: [(u32, u8, u32); 7] = [
(0b000, 3, 0),
(0b01, 2, 1),
(0b001, 3, 2),
(0b100, 3, 3),
(0b101, 3, 4),
(0b110, 3, 5),
(0b111, 3, 6),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_3: [(u32, u8, u32); 6] = [
(0b000, 3, 0),
(0b001, 3, 1),
(0b01, 2, 2),
(0b10, 2, 3),
(0b110, 3, 4),
(0b111, 3, 5),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_4: [(u32, u8, u32); 5] = [
(0b110, 3, 0),
(0b00, 2, 1),
(0b01, 2, 2),
(0b10, 2, 3),
(0b111, 3, 4),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_5: [(u32, u8, u32); 4] = [
(0b00, 2, 0),
(0b01, 2, 1),
(0b10, 2, 2),
(0b11, 2, 3),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_6: [(u32, u8, u32); 3] = [
(0b00, 2, 0),
(0b01, 2, 1),
(0b1, 1, 2),
];
#[rustfmt::skip]
static TZ_CHROMA_422_TZVLC_7: [(u32, u8, u32); 2] = [
(0b0, 1, 0),
(0b1, 1, 1),
];
pub(crate) fn tz_luma_table(tzvlc_index: u32) -> Option<&'static [(u32, u8, u32)]> {
Some(match tzvlc_index {
1 => &TZ_LUMA_TZVLC_1,
2 => &TZ_LUMA_TZVLC_2,
3 => &TZ_LUMA_TZVLC_3,
4 => &TZ_LUMA_TZVLC_4,
5 => &TZ_LUMA_TZVLC_5,
6 => &TZ_LUMA_TZVLC_6,
7 => &TZ_LUMA_TZVLC_7,
8 => &TZ_LUMA_TZVLC_8,
9 => &TZ_LUMA_TZVLC_9,
10 => &TZ_LUMA_TZVLC_10,
11 => &TZ_LUMA_TZVLC_11,
12 => &TZ_LUMA_TZVLC_12,
13 => &TZ_LUMA_TZVLC_13,
14 => &TZ_LUMA_TZVLC_14,
15 => &TZ_LUMA_TZVLC_15,
_ => return None,
})
}
pub(crate) fn tz_chroma_420_table(tzvlc_index: u32) -> Option<&'static [(u32, u8, u32)]> {
Some(match tzvlc_index {
1 => &TZ_CHROMA_420_TZVLC_1,
2 => &TZ_CHROMA_420_TZVLC_2,
3 => &TZ_CHROMA_420_TZVLC_3,
_ => return None,
})
}
pub(crate) fn tz_chroma_422_table(tzvlc_index: u32) -> Option<&'static [(u32, u8, u32)]> {
Some(match tzvlc_index {
1 => &TZ_CHROMA_422_TZVLC_1,
2 => &TZ_CHROMA_422_TZVLC_2,
3 => &TZ_CHROMA_422_TZVLC_3,
4 => &TZ_CHROMA_422_TZVLC_4,
5 => &TZ_CHROMA_422_TZVLC_5,
6 => &TZ_CHROMA_422_TZVLC_6,
7 => &TZ_CHROMA_422_TZVLC_7,
_ => return None,
})
}
#[rustfmt::skip]
static RB_ZL_1: [(u32, u8, u32); 2] = [
(0b1, 1, 0), (0b0, 1, 1), ];
#[rustfmt::skip]
static RB_ZL_2: [(u32, u8, u32); 3] = [
(0b1, 1, 0), (0b01, 2, 1), (0b00, 2, 2), ];
#[rustfmt::skip]
static RB_ZL_3: [(u32, u8, u32); 4] = [
(0b11, 2, 0), (0b10, 2, 1), (0b01, 2, 2), (0b00, 2, 3), ];
#[rustfmt::skip]
static RB_ZL_4: [(u32, u8, u32); 5] = [
(0b11, 2, 0), (0b10, 2, 1), (0b01, 2, 2), (0b001, 3, 3), (0b000, 3, 4), ];
#[rustfmt::skip]
static RB_ZL_5: [(u32, u8, u32); 6] = [
(0b11, 2, 0), (0b10, 2, 1), (0b011, 3, 2), (0b010, 3, 3), (0b001, 3, 4), (0b000, 3, 5), ];
#[rustfmt::skip]
static RB_ZL_6: [(u32, u8, u32); 7] = [
(0b11, 2, 0), (0b000, 3, 1), (0b001, 3, 2), (0b011, 3, 3), (0b010, 3, 4), (0b101, 3, 5), (0b100, 3, 6), ];
#[rustfmt::skip]
static RB_ZL_GT_6: [(u32, u8, u32); 15] = [
(0b111, 3, 0), (0b110, 3, 1), (0b101, 3, 2), (0b100, 3, 3), (0b011, 3, 4), (0b010, 3, 5), (0b001, 3, 6), (0b0001, 4, 7), (0b00001, 5, 8), (0b000001, 6, 9), (0b0000001, 7, 10), (0b00000001, 8, 11), (0b000000001, 9, 12), (0b0000000001, 10, 13), (0b00000000001, 11, 14), ];
pub(crate) fn rb_table(zeros_left: u32) -> &'static [(u32, u8, u32)] {
match zeros_left {
1 => &RB_ZL_1,
2 => &RB_ZL_2,
3 => &RB_ZL_3,
4 => &RB_ZL_4,
5 => &RB_ZL_5,
6 => &RB_ZL_6,
_ => &RB_ZL_GT_6, }
}
pub fn decode_coeff_token(
r: &mut BitReader<'_>,
ctx: CoeffTokenContext,
) -> CavlcResult<(u32, u32)> {
let table = ctx.select_table();
match decode_vlc(r, table)? {
Some(v) => Ok(v),
None => Err(CavlcError::UnknownCoeffToken {
nc: ctx.nc_for_error(),
}),
}
}
pub fn decode_level(
r: &mut BitReader<'_>,
suffix_length: u32,
is_first_level_after_t1_lt_3: bool,
) -> CavlcResult<(i32, u32)> {
let level_prefix = read_level_prefix(r)?;
let level_suffix_size: u32 = if level_prefix == 14 && suffix_length == 0 {
4
} else if level_prefix >= 15 {
level_prefix - 3
} else {
suffix_length
};
let level_suffix: u32 = if level_suffix_size > 0 {
r.u(level_suffix_size)?
} else {
0
};
let mut level_code: i64 =
((core::cmp::min(15, level_prefix) as i64) << suffix_length) + level_suffix as i64;
if level_prefix >= 15 && suffix_length == 0 {
level_code += 15;
}
if level_prefix >= 16 {
level_code += (1_i64 << (level_prefix - 3)) - 4096;
}
if is_first_level_after_t1_lt_3 {
level_code += 2;
}
let level_val: i32 = if level_code & 1 == 0 {
((level_code + 2) >> 1) as i32
} else {
((-level_code - 1) >> 1) as i32
};
let mut next_sl = suffix_length;
if next_sl == 0 {
next_sl = 1;
}
let abs_level = level_val.unsigned_abs();
if abs_level > (3u32 << (next_sl - 1)) && next_sl < 6 {
next_sl += 1;
}
Ok((level_val, next_sl))
}
fn read_level_prefix(r: &mut BitReader<'_>) -> CavlcResult<u32> {
let mut leading: u32 = 0;
loop {
if leading > 25 {
return Err(CavlcError::LevelPrefixOverflow);
}
if r.u(1)? == 1 {
return Ok(leading);
}
leading += 1;
}
}
pub fn decode_total_zeros(
r: &mut BitReader<'_>,
total_coeff: u32,
table: TotalZerosTable,
) -> CavlcResult<u32> {
let rows = match table {
TotalZerosTable::Luma => tz_luma_table(total_coeff),
TotalZerosTable::ChromaDc420 => tz_chroma_420_table(total_coeff),
TotalZerosTable::ChromaDc422 => tz_chroma_422_table(total_coeff),
}
.ok_or(CavlcError::UnknownTotalZeros { tc: total_coeff })?;
match decode_vlc(r, rows)? {
Some(v) => Ok(v),
None => Err(CavlcError::UnknownTotalZeros { tc: total_coeff }),
}
}
pub fn decode_run_before(r: &mut BitReader<'_>, zeros_left: u32) -> CavlcResult<u32> {
if zeros_left == 0 {
return Ok(0);
}
let rows = rb_table(zeros_left);
match decode_vlc(r, rows)? {
Some(v) => Ok(v),
None => Err(CavlcError::UnknownRunBefore { zl: zeros_left }),
}
}
pub fn parse_residual_block_cavlc(
r: &mut BitReader<'_>,
ctx: CoeffTokenContext,
start_idx: u32,
end_idx: u32,
max_num_coeff: u32,
) -> CavlcResult<Vec<i32>> {
if start_idx > end_idx {
return Err(CavlcError::InvalidBlockSpan { start_idx, end_idx });
}
if !matches!(max_num_coeff, 4 | 8 | 15 | 16) {
return Err(CavlcError::InvalidMaxNumCoeff { max: max_num_coeff });
}
let span_u32 = end_idx - start_idx + 1;
if span_u32 > max_num_coeff {
return Err(CavlcError::BlockSpanExceedsMax {
span: span_u32,
max: max_num_coeff,
});
}
let span = span_u32 as usize;
let mut coeff_level = vec![0i32; span];
let (total_coeff, trailing_ones) = decode_coeff_token(r, ctx)?;
if total_coeff == 0 {
return Ok(coeff_level);
}
if total_coeff > max_num_coeff {
return Err(CavlcError::InvalidTotalCoeff {
tc: total_coeff,
max: max_num_coeff,
});
}
if trailing_ones > 3 || trailing_ones > total_coeff {
return Err(CavlcError::InvalidTrailingOnes {
t1: trailing_ones,
tc: total_coeff,
});
}
let mut level_val = vec![0i32; total_coeff as usize];
for i in 0..trailing_ones {
let sign = r.u(1)?;
level_val[i as usize] = if sign == 0 { 1 } else { -1 };
}
let mut suffix_length: u32 = if total_coeff > 10 && trailing_ones < 3 {
1
} else {
0
};
for i in trailing_ones..total_coeff {
let is_first_after = i == trailing_ones && trailing_ones < 3;
let (lv, next_sl) = decode_level(r, suffix_length, is_first_after)?;
level_val[i as usize] = lv;
suffix_length = next_sl;
}
let tz_table = match max_num_coeff {
4 => TotalZerosTable::ChromaDc420,
8 => TotalZerosTable::ChromaDc422,
_ => TotalZerosTable::Luma,
};
let mut run_val = vec![0u32; total_coeff as usize];
let mut zeros_left: u32 = if total_coeff < max_num_coeff {
decode_total_zeros(r, total_coeff, tz_table)?
} else {
0
};
for i in 0..total_coeff.saturating_sub(1) {
let run_before = if zeros_left > 0 {
decode_run_before(r, zeros_left)?
} else {
0
};
run_val[i as usize] = run_before;
if run_before > zeros_left {
return Err(CavlcError::UnknownRunBefore { zl: zeros_left });
}
zeros_left -= run_before;
}
run_val[(total_coeff - 1) as usize] = zeros_left;
let mut coeff_num: i32 = -1;
for j in 0..total_coeff {
let i = (total_coeff - 1 - j) as usize;
coeff_num += (run_val[i] as i32) + 1;
let dst_idx = start_idx as i32 + coeff_num;
let rel = (dst_idx - start_idx as i32) as usize;
if rel >= span {
return Err(CavlcError::InvalidTotalCoeff {
tc: total_coeff,
max: max_num_coeff,
});
}
coeff_level[rel] = level_val[i];
}
Ok(coeff_level)
}
#[cfg(test)]
mod tests {
use super::*;
fn reader(bytes: &'static [u8]) -> BitReader<'static> {
BitReader::new(bytes)
}
fn pack_bits(bits: &[&str]) -> Vec<u8> {
let mut out = Vec::new();
let mut cur: u8 = 0;
let mut n: u8 = 0;
for s in bits {
for c in s.chars() {
let bit = match c {
'0' => 0,
'1' => 1,
' ' | '_' => continue, other => panic!("bad bit char: {other:?}"),
};
cur = (cur << 1) | bit;
n += 1;
if n == 8 {
out.push(cur);
cur = 0;
n = 0;
}
}
}
if n > 0 {
cur <<= 8 - n;
out.push(cur);
}
out
}
#[test]
fn pack_bits_matches_expected() {
assert_eq!(pack_bits(&["1010 0110"]), vec![0xA6]);
assert_eq!(pack_bits(&["1"]), vec![0x80]);
assert_eq!(pack_bits(&["01", "001"]), vec![0x48]);
}
#[test]
fn coeff_token_col0_nc_lt_2_samples() {
let cases: &[(&str, (u32, u32))] = &[
("1", (0, 0)),
("01", (1, 1)),
("001", (2, 2)),
("0001 1", (3, 3)),
("0000 0001 11", (4, 0)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(0)).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn coeff_token_col1_2_le_nc_lt_4_samples() {
let cases: &[(&str, (u32, u32))] = &[
("11", (0, 0)),
("10", (1, 1)),
("011", (2, 2)),
("0101", (3, 3)),
("0010 11", (1, 0)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(2)).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn coeff_token_col2_4_le_nc_lt_8_samples() {
let cases: &[(&str, (u32, u32))] = &[
("1111", (0, 0)),
("1110", (1, 1)),
("1101", (2, 2)),
("1100", (3, 3)),
("0111 0", (3, 2)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(5)).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn coeff_token_col3_8_le_nc_samples() {
let cases: &[(&str, (u32, u32))] = &[
("0000 11", (0, 0)), ("0000 00", (1, 0)), ("0000 01", (1, 1)), ("0001 00", (2, 0)), ("1111 11", (16, 3)), ];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(10)).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn coeff_token_col1_tc10_t1_2_from_spec() {
let bytes = pack_bits(&["0000 0000 1101"]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(3)).unwrap();
assert_eq!(got, (10, 2));
}
#[test]
fn coeff_token_col1_t1_3_tc_15_length_fix() {
let bytes = pack_bits(&["0000 0000 0000 1"]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(3)).unwrap();
assert_eq!(got, (15, 3));
let (b, bi) = r.position();
assert_eq!((b, bi), (1, 5));
}
#[test]
fn coeff_token_col1_11bit_length_fix() {
let cases: &[(&str, (u32, u32))] = &[
("0000 0001 111", (7, 0)), ("0000 0001 011", (8, 0)),
("0000 0001 110", (8, 1)),
("0000 0001 101", (8, 2)),
("0000 0001 010", (9, 1)),
("0000 0001 001", (9, 2)),
("0000 0001 100", (10, 3)),
("0000 0001 000", (11, 3)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(3)).unwrap();
assert_eq!(got, *want, "bits={bits}");
let (b, bi) = r.position();
let consumed = b * 8 + bi as usize;
assert_eq!(consumed, 11, "wrong bit count consumed for bits={bits}");
}
}
#[test]
fn coeff_token_col1_all_entries_roundtrip() {
for (bits, length, (tc, t1)) in TABLE_9_5_COL1.iter() {
let s: String = (0..*length)
.rev()
.map(|k| if (bits >> k) & 1 == 1 { '1' } else { '0' })
.collect();
let bytes = pack_bits(&[&s]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(2)).unwrap();
assert_eq!(got, (*tc, *t1), "entry bits={s:?}");
let (b, bi) = r.position();
let consumed = b * 8 + bi as usize;
assert_eq!(consumed, *length as usize, "wrong bit count for bits={s:?}");
}
}
#[test]
fn coeff_token_col3_bugfix_regression() {
let cases: &[(&str, (u32, u32))] = &[
("0000 00", (1, 0)), ("0001 00", (2, 0)), ("0010 00", (3, 0)), ("0010 01", (3, 1)), ("0011 00", (4, 0)),
("1000 00", (9, 0)),
("1111 00", (16, 0)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::Numeric(10)).unwrap();
assert_eq!(got, *want, "bits={bits}");
assert!(
got.1 <= got.0,
"invariant TrailingOnes<=TotalCoeff violated for bits={bits}"
);
}
}
#[test]
fn coeff_token_chroma_dc_420_samples() {
let cases: &[(&str, (u32, u32))] = &[
("01", (0, 0)),
("1", (1, 1)),
("001", (2, 2)),
("0001 11", (1, 0)),
("0000 10", (4, 0)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::ChromaDc420).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn coeff_token_chroma_dc_422_samples() {
let cases: &[(&str, (u32, u32))] = &[
("1", (0, 0)),
("01", (1, 1)),
("001", (2, 2)),
("0000 1", (3, 3)),
("0001 111", (1, 0)),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_coeff_token(&mut r, CoeffTokenContext::ChromaDc422).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn level_prefix_reads_leading_zeros_then_one() {
let cases: &[(&str, u32)] = &[
("1", 0),
("01", 1),
("001", 2),
("0001", 3),
("00000000 1", 8),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
assert_eq!(read_level_prefix(&mut r).unwrap(), *want, "bits={bits}");
}
}
#[test]
fn decode_level_basic_values() {
let bytes = pack_bits(&["01"]);
let mut r = BitReader::new(&bytes);
let (lv, _next) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, -1);
let bytes = pack_bits(&["01"]);
let mut r = BitReader::new(&bytes);
let (lv, _next) = decode_level(&mut r, 0, true).unwrap();
assert_eq!(lv, -2);
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 0, true).unwrap();
assert_eq!(lv, 2);
assert_eq!(next, 1);
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let (lv, _next) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, 1);
}
#[test]
fn decode_level_with_suffix_bits() {
let bytes = pack_bits(&["01", "1"]);
let mut r = BitReader::new(&bytes);
let (lv, _) = decode_level(&mut r, 1, false).unwrap();
assert_eq!(lv, -2);
let bytes = pack_bits(&["1", "0"]);
let mut r = BitReader::new(&bytes);
let (lv, _) = decode_level(&mut r, 1, false).unwrap();
assert_eq!(lv, 1);
}
#[test]
fn decode_level_prefix14_suffix0_reads_4_bit_suffix() {
let bits = "00000000000000 1 1010"; let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, 13);
assert_eq!(next, 2);
}
#[test]
fn decode_level_step10_triggers_suffix_length_bump() {
let bytes = pack_bits(&["1", "0"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 1, false).unwrap();
assert_eq!(lv, 1);
assert_eq!(next, 1);
let bytes = pack_bits(&["001", "0"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 1, false).unwrap();
assert_eq!(lv, 3);
assert_eq!(next, 1);
let bytes = pack_bits(&["001", "1"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 1, false).unwrap();
assert_eq!(lv, -3);
assert_eq!(next, 1);
let bytes = pack_bits(&["0001", "0"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 1, false).unwrap();
assert_eq!(lv, 4);
assert_eq!(next, 2);
}
#[test]
fn decode_level_step10_cap_at_6() {
let bytes = pack_bits(&["1", "00000"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 5, false).unwrap();
assert_eq!(lv, 1);
assert_eq!(next, 5);
let bytes = pack_bits(&["0001", "11111"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 5, false).unwrap();
assert_eq!(lv, -64);
assert_eq!(next, 6);
let bytes = pack_bits(&["0001", "111111"]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 6, false).unwrap();
assert_eq!(lv, -128);
assert_eq!(next, 6);
}
#[test]
fn decode_level_prefix15_suffix0_escape() {
let bits = "0000000000000001 000000000010";
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let (lv, next) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, 17);
assert_eq!(next, 2);
}
#[test]
fn decode_level_prefix16_step6_correction() {
let bits = "0000000000000000 1 0000000000000"; let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let (lv, _next) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, 2064);
}
#[test]
fn decode_level_step7_first_after_bias() {
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let (lv, _) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, 1);
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let (lv, _) = decode_level(&mut r, 0, true).unwrap();
assert_eq!(lv, 2);
let bytes = pack_bits(&["01"]);
let mut r = BitReader::new(&bytes);
let (lv, _) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(lv, -1);
let bytes = pack_bits(&["01"]);
let mut r = BitReader::new(&bytes);
let (lv, _) = decode_level(&mut r, 0, true).unwrap();
assert_eq!(lv, -2);
}
#[test]
fn decode_level_step9_always_promotes_0_to_1() {
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let (_, next) = decode_level(&mut r, 0, false).unwrap();
assert_eq!(next, 1);
}
#[test]
fn total_zeros_luma_tzvlc_1_samples() {
let cases: &[(&str, u32)] = &[("1", 0), ("011", 1), ("010", 2), ("0011", 3)];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_total_zeros(&mut r, 1, TotalZerosTable::Luma).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn total_zeros_luma_tzvlc_7_samples() {
let cases: &[(&str, u32)] = &[
("0000 01", 0),
("0000 1", 1),
("101", 2),
("11", 5),
("0000 00", 9),
];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_total_zeros(&mut r, 7, TotalZerosTable::Luma).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn total_zeros_luma_tzvlc_15_samples() {
let cases: &[(&str, u32)] = &[("0", 0), ("1", 1)];
for (bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_total_zeros(&mut r, 15, TotalZerosTable::Luma).unwrap();
assert_eq!(got, *want, "bits={bits}");
}
}
#[test]
fn total_zeros_chroma_420_samples() {
let cases: &[(u32, &str, u32)] = &[
(1, "1", 0),
(1, "01", 1),
(1, "001", 2),
(1, "000", 3),
(2, "1", 0),
(2, "00", 2),
(3, "1", 0),
(3, "0", 1),
];
for (tc, bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_total_zeros(&mut r, *tc, TotalZerosTable::ChromaDc420).unwrap();
assert_eq!(got, *want, "tc={tc} bits={bits}");
}
}
#[test]
fn total_zeros_chroma_422_samples() {
let cases: &[(u32, &str, u32)] = &[
(1, "1", 0),
(1, "010", 1),
(1, "0001", 5),
(4, "110", 0),
(7, "0", 0),
(7, "1", 1),
];
for (tc, bits, want) in cases {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_total_zeros(&mut r, *tc, TotalZerosTable::ChromaDc422).unwrap();
assert_eq!(got, *want, "tc={tc} bits={bits}");
}
}
fn check_rb(bits: &str, zeros_left: u32, want: u32) {
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let got = decode_run_before(&mut r, zeros_left)
.unwrap_or_else(|e| panic!("decode failed zl={zeros_left} bits={bits:?}: {e}"));
assert_eq!(
got, want,
"zl={zeros_left} bits={bits:?} expected run_before={want} got {got}"
);
}
#[test]
fn run_before_zl_1_all_entries() {
check_rb("1", 1, 0);
check_rb("0", 1, 1);
}
#[test]
fn run_before_zl_2_all_entries() {
check_rb("1", 2, 0);
check_rb("01", 2, 1);
check_rb("00", 2, 2);
}
#[test]
fn run_before_zl_3_all_entries() {
check_rb("11", 3, 0);
check_rb("10", 3, 1);
check_rb("01", 3, 2);
check_rb("00", 3, 3);
}
#[test]
fn run_before_zl_4_all_entries() {
check_rb("11", 4, 0);
check_rb("10", 4, 1);
check_rb("01", 4, 2);
check_rb("001", 4, 3);
check_rb("000", 4, 4);
}
#[test]
fn run_before_zl_5_all_entries() {
check_rb("11", 5, 0);
check_rb("10", 5, 1);
check_rb("011", 5, 2);
check_rb("010", 5, 3);
check_rb("001", 5, 4);
check_rb("000", 5, 5);
}
#[test]
fn run_before_zl_6_all_entries() {
check_rb("11", 6, 0);
check_rb("000", 6, 1);
check_rb("001", 6, 2);
check_rb("011", 6, 3);
check_rb("010", 6, 4);
check_rb("101", 6, 5);
check_rb("100", 6, 6);
}
#[test]
fn run_before_zl_7_decodes_every_long_form_row() {
check_rb("111", 7, 0);
check_rb("110", 7, 1);
check_rb("101", 7, 2);
check_rb("100", 7, 3);
check_rb("011", 7, 4);
check_rb("010", 7, 5);
check_rb("001", 7, 6);
check_rb("0001", 7, 7);
check_rb("00001", 7, 8);
check_rb("000001", 7, 9);
check_rb("0000001", 7, 10);
check_rb("00000001", 7, 11);
check_rb("000000001", 7, 12);
check_rb("0000000001", 7, 13);
check_rb("00000000001", 7, 14);
}
#[test]
fn run_before_long_form_shared_across_zl_7_to_14() {
for zl in 7u32..=14 {
check_rb("111", zl, 0); check_rb("0001", zl, 7); check_rb("00000001", zl, 11);
check_rb("00000000001", zl, 14); }
}
#[test]
fn run_before_endpoint_cases() {
check_rb("0001", 7, 7); check_rb("00000000001", 14, 14); }
#[test]
fn run_before_errors_on_exhausted_stream() {
let bytes: [u8; 1] = [0x00];
let mut r = BitReader::new(&bytes);
let err = decode_run_before(&mut r, 7).unwrap_err();
match err {
CavlcError::UnknownRunBefore { zl } => assert_eq!(zl, 7),
CavlcError::Bitstream(_) => {}
other => panic!("expected UnknownRunBefore or Bitstream, got {other:?}"),
}
}
#[test]
fn run_before_zl_zero_returns_zero_without_reading() {
let bytes: [u8; 1] = [0xFF];
let mut r = BitReader::new(&bytes);
assert_eq!(decode_run_before(&mut r, 0).unwrap(), 0);
assert_eq!(r.u(8).unwrap(), 0xFF);
}
#[test]
fn full_block_small_example() {
let bytes = pack_bits(&[
"0000101", "1", "0", "001", "110", "1", "1", ]);
let mut r = BitReader::new(&bytes);
let coeff =
parse_residual_block_cavlc(&mut r, CoeffTokenContext::Numeric(0), 0, 15, 16).unwrap();
let mut expected = vec![0i32; 16];
expected[2] = 3;
expected[3] = 1;
expected[4] = -1;
assert_eq!(coeff, expected);
}
#[test]
fn full_block_all_zero() {
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let coeff =
parse_residual_block_cavlc(&mut r, CoeffTokenContext::Numeric(0), 0, 15, 16).unwrap();
assert_eq!(coeff, vec![0; 16]);
}
#[test]
fn col1_11bit_codeword_full_parse_regression() {
let bytes = pack_bits(&[
"00000001111", "1", "1",
"0", "1",
"0", "1",
"0", "1",
"0", "1",
"0", "1",
"0", "000001", ]);
let mut r = BitReader::new(&bytes);
let coeff =
parse_residual_block_cavlc(&mut r, CoeffTokenContext::Numeric(3), 0, 15, 16).unwrap();
let mut expected = vec![0i32; 16];
expected[0] = 1;
expected[1] = 1;
expected[2] = 1;
expected[3] = 1;
expected[4] = 1;
expected[5] = 1;
expected[6] = 2;
assert_eq!(coeff, expected);
let (b, bi) = r.position();
assert_eq!(b * 8 + bi as usize, 30);
}
#[test]
fn full_block_single_trailing_one() {
let bytes = pack_bits(&["01", "1", "1"]);
let mut r = BitReader::new(&bytes);
let coeff =
parse_residual_block_cavlc(&mut r, CoeffTokenContext::Numeric(0), 0, 15, 16).unwrap();
let mut expected = vec![0i32; 16];
expected[0] = -1;
assert_eq!(coeff, expected);
}
#[test]
fn rejects_inverted_span() {
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let err = parse_residual_block_cavlc(&mut r, CoeffTokenContext::Numeric(0), 5, 3, 16)
.unwrap_err();
assert_eq!(
err,
CavlcError::InvalidBlockSpan {
start_idx: 5,
end_idx: 3
}
);
let (b, bi) = r.position();
assert_eq!((b, bi), (0, 0));
}
#[test]
fn rejects_invalid_max_num_coeff() {
for bad in [0u32, 1, 5, 9, 12, 17, 64, 0xffff_ffff] {
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let err = parse_residual_block_cavlc(
&mut r,
CoeffTokenContext::Numeric(0),
0,
bad.saturating_sub(1).min(15),
bad,
)
.unwrap_err();
assert_eq!(
err,
CavlcError::InvalidMaxNumCoeff { max: bad },
"max_num_coeff={bad} must be rejected"
);
let (b, bi) = r.position();
assert_eq!(
(b, bi),
(0, 0),
"max_num_coeff={bad} should not consume bits"
);
}
}
#[test]
fn accepts_all_four_legal_max_num_coeff() {
let cases: &[(u32, u32, CoeffTokenContext)] = &[
(4, 3, CoeffTokenContext::ChromaDc420),
(8, 7, CoeffTokenContext::ChromaDc422),
(15, 14, CoeffTokenContext::Numeric(0)),
(16, 15, CoeffTokenContext::Numeric(0)),
];
for (max, end, ctx) in cases {
let bits = match ctx {
CoeffTokenContext::Numeric(_) => "1",
CoeffTokenContext::ChromaDc420 => "01",
CoeffTokenContext::ChromaDc422 => "1",
};
let bytes = pack_bits(&[bits]);
let mut r = BitReader::new(&bytes);
let out =
parse_residual_block_cavlc(&mut r, *ctx, 0, *end, *max).expect("legal call ok");
assert_eq!(out.len() as u32, end + 1);
assert!(
out.iter().all(|c| *c == 0),
"TotalCoeff=0 should leave coeff_level all-zero"
);
}
}
#[test]
fn rejects_span_exceeding_max() {
let bytes = pack_bits(&["1"]);
let mut r = BitReader::new(&bytes);
let err = parse_residual_block_cavlc(&mut r, CoeffTokenContext::ChromaDc420, 0, 15, 4)
.unwrap_err();
assert_eq!(err, CavlcError::BlockSpanExceedsMax { span: 16, max: 4 });
let (b, bi) = r.position();
assert_eq!((b, bi), (0, 0));
}
}