Never crash: HEVC encoder without expect()/unreachable!()

Replaces the encoder's 8 expect()/unwrap()/unreachable!() calls with handling that can't
panic: coding-unit decisions without a candidate report an infinite cost, inter helpers
without a reference picture return a neutral result, residual_coding of an all-zero block
writes nothing, the impossible Intra arms are no-ops, and the edge padding of an empty row uses
0 instead of unwrapping. Denies the panic lints in the crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-04 13:23:03 -04:00
parent 10644a83aa
commit da3c7a842d
3 changed files with 19 additions and 8 deletions

4
clippy.toml Normal file
View File

@ -0,0 +1,4 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
allow-panic-in-tests = true
allow-indexing-slicing-in-tests = true

View File

@ -380,7 +380,9 @@ impl<'a> FrameCoder<'a> {
return cost;
}
}
let (cost, e, snap, mode) = best.expect("a coding unit decision");
// A coding unit always has at least one candidate mode (intra, or skip/merge in P
// slices); without one, report an infinite cost.
let Some((cost, e, snap, mode)) = best else { return f64::INFINITY };
self.restore(&snap);
*est = e;
decs.push(CuDec { x: x as u32, y: y as u32, log2, mode });
@ -452,7 +454,8 @@ impl<'a> FrameCoder<'a> {
let mv = match mode {
CuMode::Skip { idx } | CuMode::Merge { idx } => self.merge_list(x, y, n)[idx as usize],
CuMode::Amvp { mv, .. } => mv,
CuMode::Intra { .. } => unreachable!(),
// Handled by the arm above.
CuMode::Intra { .. } => [0, 0],
};
let amvp = if let CuMode::Amvp { .. } = mode { self.amvp(x, y, n) } else { [[0; 2]; 2] };
let skip = matches!(mode, CuMode::Skip { .. });
@ -503,7 +506,8 @@ impl<'a> FrameCoder<'a> {
self.write_tt(s, x, y, x, y, log2, 0, 0, [true, true], false, false, &mut ti);
}
}
CuMode::Intra { .. } => unreachable!(),
// Handled by the arm above.
CuMode::Intra { .. } => {}
}
(eff, self.cu_dist(x, y, n))
}
@ -864,7 +868,8 @@ impl<'a> FrameCoder<'a> {
}
fn predict_inter(&mut self, x: usize, y: usize, n: usize, mv: [i16; 2]) {
let refp = self.refp.expect("reference picture");
// Inter modes are only tried in P slices, which have a reference picture.
let Some(refp) = self.refp else { return };
for c in 0..3 {
let (px, py, pn, pw, ph) = if c == 0 { (x, y, n, self.w, self.h) } else { (x / 2, y / 2, n / 2, self.cw, self.ch) };
let pr = PlaneRef { data: &refp[c], w: pw, h: ph };
@ -882,7 +887,7 @@ impl<'a> FrameCoder<'a> {
/// Best merge index by SATD of the (approximate) luma prediction.
fn best_merge(&self, x: usize, y: usize, n: usize, list: &[[i16; 2]; MAX_MERGE_CAND]) -> (usize, u64) {
let me = self.me.expect("motion search reference");
let Some(me) = self.me else { return (0, u64::MAX / 4) };
let mut buf = vec![0u16; n * n];
let mut best = (0, u64::MAX);
for (i, &mv) in list.iter().enumerate() {
@ -901,7 +906,7 @@ impl<'a> FrameCoder<'a> {
/// Integer then fractional motion search. Returns (mv, mvp index, SATD cost).
fn motion_search(&self, x: usize, y: usize, n: usize, amvp: &[[i16; 2]; 2], merge: &[[i16; 2]; MAX_MERGE_CAND]) -> ([i16; 2], u8, u64) {
let me = self.me.expect("motion search reference");
let Some(me) = self.me else { return (amvp[0], 0, u64::MAX / 4) };
let (xi, yi) = (x as i32, y as i32);
let src = &self.src[0][y * self.w + x..];
let ls = self.lambda_sad;
@ -1168,7 +1173,8 @@ fn residual_coding<S: Sink>(s: &mut S, lv: &[i32], log2: u32, c: usize, scan_idx
}
}
}
let (last_sb, last_pos) = last.expect("residual_coding of an all-zero block");
// Callers only code blocks with a nonzero level (coded_block_flag = 1).
let Some((last_sb, last_pos)) = last else { return };
let lx = sb_scan[last_sb].0 as u32 * 4 + pos_scan[last_pos].0 as u32;
let ly = sb_scan[last_sb].1 as u32 * 4 + pos_scan[last_pos].1 as u32;
let (cx, cy) = if scan_idx == 2 { (ly, lx) } else { (lx, ly) };

View File

@ -14,6 +14,7 @@
//! let _hvcc = enc.hvcc();
//! ```
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
// Index loops over fixed-size blocks read more clearly than iterator chains in codec code.
#![allow(clippy::needless_range_loop, clippy::too_many_arguments)]
@ -287,7 +288,7 @@ impl Encoder {
for y in 0..ch {
let row = &src[y.min(h - 1) * stride..][..w];
plane.extend(row.iter().map(|&v| (v as u32).min(max) as u16));
let last = *plane.last().unwrap();
let last = plane.last().copied().unwrap_or(0);
plane.extend(std::iter::repeat_n(last, cw - w));
}
}