use super::cleanup::CleanupCoefficientSource;
const ROW_PAIR_COLUMNS: usize = 1024 + 4;
const QUADS: usize = ROW_PAIR_COLUMNS / 2;
pub(super) struct RowPair {
exponents: [[u8; ROW_PAIR_COLUMNS]; 2],
values: [[u32; ROW_PAIR_COLUMNS]; 2],
rho: [u8; QUADS],
e_qmax: [u8; QUADS],
reach: [u8; QUADS],
above_context: [u8; QUADS],
above_exponent: [u8; QUADS],
index: [u16; QUADS],
u_q: [u8; QUADS],
bound: [u8; QUADS],
}
impl RowPair {
pub(super) fn new() -> Self {
Self {
exponents: [[0; ROW_PAIR_COLUMNS]; 2],
values: [[0; ROW_PAIR_COLUMNS]; 2],
rho: [0; QUADS],
e_qmax: [0; QUADS],
reach: [0; QUADS],
above_context: [0; QUADS],
above_exponent: [0; QUADS],
index: [0; QUADS],
u_q: [0; QUADS],
bound: [0; QUADS],
}
}
pub(super) fn fill<C: CleanupCoefficientSource + ?Sized>(
&mut self,
coefficients: &C,
y: usize,
width: usize,
height: usize,
p: u32,
) -> Result<(), &'static str> {
let quads = width.div_ceil(2);
if y > 0 {
self.fill_above(quads);
}
let rows = self.exponents.iter_mut().zip(&mut self.values);
for (row, (exponents, values)) in rows.enumerate() {
let filled = if y + row < height {
let start = (y + row) * width;
coefficients.fill_row(start, p, &mut exponents[..width], &mut values[..width])?;
width
} else {
0
};
exponents[filled..width + 4].fill(0);
}
let [upper, lower] = &self.exponents;
for quad in 0..width / 2 + 2 {
let (left, right) = (2 * quad, 2 * quad + 1);
let samples = [upper[left], lower[left], upper[right], lower[right]];
self.rho[quad] = u8::from(samples[0] != 0)
| u8::from(samples[1] != 0) << 1
| u8::from(samples[2] != 0) << 2
| u8::from(samples[3] != 0) << 3;
let e_qmax = samples[0].max(samples[1]).max(samples[2].max(samples[3]));
self.e_qmax[quad] = e_qmax;
self.reach[quad] = u8::from(samples[0] == e_qmax)
| u8::from(samples[1] == e_qmax) << 1
| u8::from(samples[2] == e_qmax) << 2
| u8::from(samples[3] == e_qmax) << 3;
}
self.fill_coding(quads, y == 0);
Ok(())
}
fn fill_above(&mut self, quads: usize) {
let mut pair_max = [0_u8; QUADS + 1];
let above = &self.exponents[1][..=2 * quads];
pair_max[0] = above[0];
let pairs = above[1..].as_chunks::<2>().0;
for (max, pair) in pair_max[1..=quads].iter_mut().zip(pairs) {
*max = pair[0].max(pair[1]);
}
let (left, right) = (&pair_max[..quads], &pair_max[1..=quads]);
let targets = self.above_exponent[..quads].iter_mut();
let contexts = self.above_context[..quads].iter_mut();
for (((exponent, context), &left), &right) in targets.zip(contexts).zip(left).zip(right) {
*exponent = left.max(right);
*context = u8::from(left != 0) | u8::from(right != 0) << 2;
}
}
fn fill_coding(&mut self, quads: usize, initial: bool) {
let mut left_rho = [0_u8; QUADS];
left_rho[1..quads].copy_from_slice(&self.rho[..quads - 1]);
let fields = self.rho[..quads]
.iter()
.zip(&left_rho[..quads])
.zip(&self.e_qmax[..quads])
.zip(&self.reach[..quads])
.zip(&self.above_context[..quads])
.zip(&self.above_exponent[..quads]);
let outputs = self.index[..quads]
.iter_mut()
.zip(&mut self.u_q[..quads])
.zip(&mut self.bound[..quads]);
for ((((((&rho, &left), &e_qmax), &reach), &above), &above_e), ((index, u_q), bound)) in
fields.zip(outputs)
{
let (context, kappa) = if initial {
((left >> 1) | (left & 1), 1)
} else {
let multiple = u8::from(rho & rho.wrapping_sub(1) != 0);
let kappa = (multiple * above_e.saturating_sub(1)).max(1);
(above | (((left >> 2) | (left >> 3)) & 1) << 1, kappa)
};
*bound = e_qmax.max(kappa);
*u_q = *bound - kappa;
let eps = reach & (u8::from(*u_q > 0) * 0xF);
*index = u16::from(context) << 8 | u16::from(rho) << 4 | u16::from(eps);
}
}
}
impl RowPair {
#[expect(
clippy::inline_always,
reason = "tested once per quad pair in the cleanup hot path"
)]
#[inline(always)]
pub(super) fn pair_is_zero(&self, x: usize, width: usize) -> bool {
let quad = x / 2;
self.index[quad] == 0 && (x + 2 >= width || self.index[quad + 1] == 0)
}
#[expect(
clippy::inline_always,
reason = "quad loads are fused into the per-quad cleanup hot path"
)]
#[inline(always)]
fn coded_quad(&self, quad: usize) -> CodedQuad {
let (column, [upper, lower]) = (2 * quad, &self.values);
CodedQuad {
index: self.index[quad],
e_qmax: self.e_qmax[quad],
u_q: self.u_q[quad],
bound: self.bound[quad],
values: [
upper[column],
lower[column],
upper[column + 1],
lower[column + 1],
],
}
}
}
#[derive(Clone, Copy)]
pub(super) struct CodedQuad {
pub(super) index: u16,
pub(super) e_qmax: u8,
pub(super) u_q: u8,
pub(super) bound: u8,
pub(super) values: [u32; 4],
}
impl CodedQuad {
pub(super) fn context(self) -> u16 {
self.index >> 8
}
#[expect(
clippy::cast_possible_truncation,
reason = "rho is the four-bit field of the table index"
)]
pub(super) fn rho(self) -> u8 {
(self.index >> 4) as u8 & 0xF
}
}
pub(super) trait QuadSink {
type Error;
fn quad<const INITIAL: bool>(&mut self, quad: CodedQuad) -> Result<(), Self::Error>;
fn initial_uvlc_pair(&mut self, u_q0: i32, u_q1: i32) -> Result<(), Self::Error>;
fn initial_uvlc_lone(&mut self, u_q0: i32) -> Result<(), Self::Error>;
fn non_initial_uvlc(&mut self, u_q0: i32, u_q1: i32) -> Result<(), Self::Error>;
}
#[expect(
clippy::inline_always,
reason = "this normative quad walk stays fused per sink"
)]
#[inline(always)]
pub(super) fn quad_pair<const INITIAL: bool, S: QuadSink>(
rows: &RowPair,
width: usize,
x: usize,
sink: &mut S,
) -> Result<(), S::Error> {
let quad = x / 2;
sink.quad::<INITIAL>(rows.coded_quad(quad))?;
let u_q0 = i32::from(rows.u_q[quad]);
if x + 2 < width {
sink.quad::<INITIAL>(rows.coded_quad(quad + 1))?;
let u_q1 = i32::from(rows.u_q[quad + 1]);
if INITIAL {
sink.initial_uvlc_pair(u_q0, u_q1)
} else {
sink.non_initial_uvlc(u_q0, u_q1)
}
} else if INITIAL {
sink.initial_uvlc_lone(u_q0)
} else {
sink.non_initial_uvlc(u_q0, 0)
}
}