mirror of
https://github.com/storytold/filmcraft.git
synced 2026-11-02 22:40:04 -06:00
Merge pull request #11 from storytold/pr/never-crash-vp9
vp9: never crash (no unwrap/expect/panic, deny them)
This commit is contained in:
@@ -29,8 +29,7 @@ impl<'a> BoolDecoder<'a> {
|
||||
fn fill(&mut self) {
|
||||
// Room for whole bytes below the top 8 bits and the `count` valid bits.
|
||||
let mut shift = 48 - self.count;
|
||||
if self.pos + 8 <= self.data.len() {
|
||||
let bytes: [u8; 8] = self.data[self.pos..self.pos + 8].try_into().expect("8 bytes");
|
||||
if let Some(&bytes) = self.data.get(self.pos..).and_then(|d| d.first_chunk::<8>()) {
|
||||
let v = u64::from_be_bytes(bytes);
|
||||
let n = ((shift + 8) / 8) as usize; // bytes that fit
|
||||
let n = n.min(7);
|
||||
|
||||
+26
-16
@@ -331,8 +331,10 @@ impl Decoder {
|
||||
let sz = if last {
|
||||
data.len() - pos
|
||||
} else {
|
||||
ensure!(pos + 4 <= data.len(), "tile size beyond frame data");
|
||||
let s = u32::from_be_bytes(data[pos..pos + 4].try_into().expect("4 bytes")) as usize;
|
||||
let Some(&b) = data.get(pos..).and_then(|d| d.first_chunk::<4>()) else {
|
||||
return Err(Error::Invalid("tile size beyond frame data".into()));
|
||||
};
|
||||
let s = u32::from_be_bytes(b) as usize;
|
||||
pos += 4;
|
||||
ensure!(s <= data.len() - pos, "tile size {s} beyond frame data");
|
||||
s
|
||||
@@ -456,7 +458,7 @@ impl Decoder {
|
||||
/// per band for the samples they read), else right here.
|
||||
fn run_post(&mut self, job: PostJob) {
|
||||
#[cfg(all(feature = "threads", not(target_arch = "wasm32")))]
|
||||
if self.frame_threads() {
|
||||
let job = if self.frame_threads() {
|
||||
// Bound the post stages in flight (each is one thread).
|
||||
while self.jobs.front().is_some_and(|j| j.is_finished()) {
|
||||
let _ = self.jobs.pop_front().map(|j| j.join());
|
||||
@@ -467,26 +469,34 @@ impl Decoder {
|
||||
}
|
||||
let shown = job.out.is_some();
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
// The job reaches the thread over a channel, so it is still here if no thread can
|
||||
// be spawned and then runs inline below.
|
||||
let (job_tx, job_rx) = std::sync::mpsc::channel::<PostJob>();
|
||||
let spawned = std::thread::Builder::new().name("vp9-post".into()).spawn(move || {
|
||||
if let Some(p) = job.run() {
|
||||
if let Ok(job) = job_rx.recv()
|
||||
&& let Some(p) = job.run()
|
||||
{
|
||||
let _ = tx.send(p);
|
||||
}
|
||||
});
|
||||
match spawned {
|
||||
Ok(handle) => {
|
||||
self.jobs.push_back(handle);
|
||||
if shown {
|
||||
self.pending.push_back(Pending::Job(rx));
|
||||
Ok(handle) => match job_tx.send(job) {
|
||||
Ok(()) => {
|
||||
self.jobs.push_back(handle);
|
||||
if shown {
|
||||
self.pending.push_back(Pending::Job(rx));
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
// No thread available: the job (moved into the failed closure) is gone,
|
||||
// which cannot happen in practice; report as a decoding failure.
|
||||
panic!("cannot spawn VP9 post-processing thread: {e}");
|
||||
}
|
||||
// The thread is gone already: take the job back and run it here.
|
||||
Err(std::sync::mpsc::SendError(back)) => back,
|
||||
},
|
||||
// No thread available: run it here.
|
||||
Err(_) => job,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
job
|
||||
};
|
||||
if let Some(p) = job.run() {
|
||||
self.pending.push_back(Pending::Ready(p));
|
||||
}
|
||||
|
||||
@@ -154,7 +154,9 @@ impl Frame {
|
||||
}
|
||||
#[cfg(not(feature = "threads"))]
|
||||
{
|
||||
self.bands[r].get().expect("reference band decoded before use")
|
||||
// Bands are decoded in order before use; an unpublished one reads as empty.
|
||||
static EMPTY: Band = Band { planes: [Vec::new(), Vec::new(), Vec::new()] };
|
||||
self.bands[r].get().unwrap_or(&EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +248,7 @@ impl<T: Clone> Pool<T> {
|
||||
/// `fill` only initialises fresh memory).
|
||||
pub fn take_any(&self, len: usize, fill: T) -> Vec<T> {
|
||||
let found = {
|
||||
let mut b = self.bufs.lock().expect("buffer pool");
|
||||
let mut b = self.bufs.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
b.iter().position(|v| v.len() >= len).map(|i| b.swap_remove(i))
|
||||
};
|
||||
match found {
|
||||
@@ -261,7 +263,7 @@ impl<T: Clone> Pool<T> {
|
||||
/// A buffer of `len` copies of `fill` (recycled memory when available).
|
||||
pub fn take(&self, len: usize, fill: T) -> Vec<T> {
|
||||
let found = {
|
||||
let mut b = self.bufs.lock().expect("buffer pool");
|
||||
let mut b = self.bufs.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
b.iter().position(|v| v.capacity() >= len).map(|i| b.swap_remove(i))
|
||||
};
|
||||
match found {
|
||||
@@ -278,7 +280,7 @@ impl<T: Clone> Pool<T> {
|
||||
if v.capacity() == 0 {
|
||||
return;
|
||||
}
|
||||
let mut b = self.bufs.lock().expect("buffer pool");
|
||||
let mut b = self.bufs.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
if b.len() < self.max {
|
||||
b.push(v);
|
||||
}
|
||||
|
||||
+18
-6
@@ -141,12 +141,16 @@ fn filter_h(s: &[u16], f: &[i16], max: i32, o: &mut [u16]) {
|
||||
|
||||
#[inline(always)]
|
||||
fn filter_h_n<const W: usize>(s: &[u16], f: &[i16], max: i32, o: &mut [u16]) {
|
||||
let o: &mut [u16; W] = (&mut o[..W]).try_into().expect("row");
|
||||
let Some(o) = o.first_chunk_mut::<W>() else {
|
||||
return;
|
||||
};
|
||||
if max == 255 {
|
||||
let mut acc = [64u16; W];
|
||||
for t in 0..8 {
|
||||
let c = f[t] as u16;
|
||||
let p: &[u16; W] = s[t..t + W].try_into().expect("row");
|
||||
let Some(p) = s.get(t..).and_then(|s| s.first_chunk::<W>()) else {
|
||||
return;
|
||||
};
|
||||
for j in 0..W {
|
||||
acc[j] = acc[j].wrapping_add(c.wrapping_mul(p[j]));
|
||||
}
|
||||
@@ -157,7 +161,9 @@ fn filter_h_n<const W: usize>(s: &[u16], f: &[i16], max: i32, o: &mut [u16]) {
|
||||
let mut acc = [64i32; W];
|
||||
for t in 0..8 {
|
||||
let c = f[t] as i32;
|
||||
let p: &[u16; W] = s[t..t + W].try_into().expect("row");
|
||||
let Some(p) = s.get(t..).and_then(|s| s.first_chunk::<W>()) else {
|
||||
return;
|
||||
};
|
||||
for j in 0..W {
|
||||
acc[j] += c * p[j] as i32;
|
||||
}
|
||||
@@ -199,12 +205,16 @@ fn filter_v(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) {
|
||||
|
||||
#[inline(always)]
|
||||
fn filter_v_n<const W: usize>(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut [u16]) {
|
||||
let o: &mut [u16; W] = (&mut o[..W]).try_into().expect("row");
|
||||
let Some(o) = o.first_chunk_mut::<W>() else {
|
||||
return;
|
||||
};
|
||||
if max == 255 {
|
||||
let mut acc = [64u16; W];
|
||||
for t in 0..8 {
|
||||
let c = f[t] as u16;
|
||||
let p: &[u16; W] = s[t * ss..t * ss + W].try_into().expect("row");
|
||||
let Some(p) = s.get(t * ss..).and_then(|s| s.first_chunk::<W>()) else {
|
||||
return;
|
||||
};
|
||||
for j in 0..W {
|
||||
acc[j] = acc[j].wrapping_add(c.wrapping_mul(p[j]));
|
||||
}
|
||||
@@ -215,7 +225,9 @@ fn filter_v_n<const W: usize>(s: &[u16], ss: usize, f: &[i16], max: i32, o: &mut
|
||||
let mut acc = [64i32; W];
|
||||
for t in 0..8 {
|
||||
let c = f[t] as i32;
|
||||
let p: &[u16; W] = s[t * ss..t * ss + W].try_into().expect("row");
|
||||
let Some(p) = s.get(t * ss..).and_then(|s| s.first_chunk::<W>()) else {
|
||||
return;
|
||||
};
|
||||
for j in 0..W {
|
||||
acc[j] += c * p[j] as i32;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//! ```
|
||||
|
||||
#![allow(clippy::needless_range_loop, clippy::too_many_arguments)]
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
|
||||
mod boolcoder;
|
||||
mod decoder;
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct FrameContext {
|
||||
}
|
||||
|
||||
fn arr<const N: usize>(s: &[u8]) -> [u8; N] {
|
||||
s.try_into().expect("table size")
|
||||
s.first_chunk::<N>().copied().unwrap_or([128; N])
|
||||
}
|
||||
|
||||
impl Default for FrameContext {
|
||||
|
||||
+51
-34
@@ -57,32 +57,39 @@ fn iwht4(x: [i32; 4], shift: u32) -> [i32; 4] {
|
||||
fn tx1d_narrow(buf: &mut [i32], n: usize, adst: bool) {
|
||||
match (n, adst) {
|
||||
(4, false) => {
|
||||
let r = narrow::idct4(buf[..4].try_into().unwrap());
|
||||
buf[..4].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<4>() {
|
||||
*a = narrow::idct4(*a);
|
||||
}
|
||||
}
|
||||
(4, true) => {
|
||||
let r = iadst4_narrow(buf[..4].try_into().unwrap());
|
||||
buf[..4].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<4>() {
|
||||
*a = iadst4_narrow(*a);
|
||||
}
|
||||
}
|
||||
(8, false) => {
|
||||
let r = narrow::idct8(buf[..8].try_into().unwrap());
|
||||
buf[..8].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<8>() {
|
||||
*a = narrow::idct8(*a);
|
||||
}
|
||||
}
|
||||
(8, true) => {
|
||||
let r = narrow::iadst8(buf[..8].try_into().unwrap());
|
||||
buf[..8].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<8>() {
|
||||
*a = narrow::iadst8(*a);
|
||||
}
|
||||
}
|
||||
(16, false) => {
|
||||
let r = narrow::idct16(buf[..16].try_into().unwrap());
|
||||
buf[..16].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<16>() {
|
||||
*a = narrow::idct16(*a);
|
||||
}
|
||||
}
|
||||
(16, true) => {
|
||||
let r = narrow::iadst16(buf[..16].try_into().unwrap());
|
||||
buf[..16].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<16>() {
|
||||
*a = narrow::iadst16(*a);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let r = narrow::idct32(buf[..32].try_into().unwrap());
|
||||
buf[..32].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<32>() {
|
||||
*a = narrow::idct32(*a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,32 +98,39 @@ fn tx1d_narrow(buf: &mut [i32], n: usize, adst: bool) {
|
||||
fn tx1d_wide(buf: &mut [i64], n: usize, adst: bool) {
|
||||
match (n, adst) {
|
||||
(4, false) => {
|
||||
let r = wide::idct4(buf[..4].try_into().unwrap());
|
||||
buf[..4].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<4>() {
|
||||
*a = wide::idct4(*a);
|
||||
}
|
||||
}
|
||||
(4, true) => {
|
||||
let r = iadst4_wide(buf[..4].try_into().unwrap());
|
||||
buf[..4].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<4>() {
|
||||
*a = iadst4_wide(*a);
|
||||
}
|
||||
}
|
||||
(8, false) => {
|
||||
let r = wide::idct8(buf[..8].try_into().unwrap());
|
||||
buf[..8].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<8>() {
|
||||
*a = wide::idct8(*a);
|
||||
}
|
||||
}
|
||||
(8, true) => {
|
||||
let r = wide::iadst8(buf[..8].try_into().unwrap());
|
||||
buf[..8].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<8>() {
|
||||
*a = wide::iadst8(*a);
|
||||
}
|
||||
}
|
||||
(16, false) => {
|
||||
let r = wide::idct16(buf[..16].try_into().unwrap());
|
||||
buf[..16].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<16>() {
|
||||
*a = wide::idct16(*a);
|
||||
}
|
||||
}
|
||||
(16, true) => {
|
||||
let r = wide::iadst16(buf[..16].try_into().unwrap());
|
||||
buf[..16].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<16>() {
|
||||
*a = wide::iadst16(*a);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let r = wide::idct32(buf[..32].try_into().unwrap());
|
||||
buf[..32].copy_from_slice(&r);
|
||||
if let Some(a) = buf.first_chunk_mut::<32>() {
|
||||
*a = wide::idct32(*a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +151,8 @@ pub fn inverse_transform_add(coefs: &[i32], tx_size: u8, tx_type: u8, lossless:
|
||||
if lossless {
|
||||
let mut t = [0i32; 16];
|
||||
for i in 0..4 {
|
||||
let r = iwht4(coefs[i * 4..i * 4 + 4].try_into().unwrap(), 2);
|
||||
let c = coefs.get(i * 4..).and_then(|c| c.first_chunk::<4>()).copied().unwrap_or_default();
|
||||
let r = iwht4(c, 2);
|
||||
t[i * 4..i * 4 + 4].copy_from_slice(&r);
|
||||
}
|
||||
for j in 0..4 {
|
||||
@@ -205,12 +220,12 @@ fn itx_narrow<const N: usize>(coefs: &[i32], p: &TxParams, dst: &mut [u16], stri
|
||||
// Column pass across 4 / 8 columns at once (the same butterflies on lane vectors), then
|
||||
// the rounding, residual add and clipping per row of lanes.
|
||||
if N == 4 {
|
||||
let x: [Cols<4>; N] = std::array::from_fn(|i| Cols(t[i][..4].try_into().expect("4 columns")));
|
||||
let x: [Cols<4>; N] = std::array::from_fn(|i| Cols(t[i].first_chunk::<4>().copied().unwrap_or_default()));
|
||||
let y = col_tx4(x, p.col_adst);
|
||||
add_cols(&y, 0, p, dst, stride);
|
||||
} else {
|
||||
for c0 in (0..N).step_by(8) {
|
||||
let x: [Cols<8>; N] = std::array::from_fn(|i| Cols(t[i][c0..c0 + 8].try_into().expect("8 columns")));
|
||||
let x: [Cols<8>; N] = std::array::from_fn(|i| Cols(t[i].get(c0..).and_then(|r| r.first_chunk::<8>()).copied().unwrap_or_default()));
|
||||
let y = col_tx8(x, p.col_adst);
|
||||
add_cols(&y, c0, p, dst, stride);
|
||||
}
|
||||
@@ -221,7 +236,9 @@ fn itx_narrow<const N: usize>(coefs: &[i32], p: &TxParams, dst: &mut [u16], stri
|
||||
#[inline(always)]
|
||||
fn add_cols<const N: usize, const W: usize>(y: &[Cols<W>; N], c0: usize, p: &TxParams, dst: &mut [u16], stride: usize) {
|
||||
for (i, v) in y.iter().enumerate() {
|
||||
let d: &mut [u16; W] = (&mut dst[i * stride + c0..i * stride + c0 + W]).try_into().expect("row");
|
||||
let Some(d) = dst.get_mut(i * stride + c0..).and_then(|d| d.first_chunk_mut::<W>()) else {
|
||||
return;
|
||||
};
|
||||
for j in 0..W {
|
||||
let r = (v.0[j].wrapping_add(p.round) >> p.shift).clamp(-(1 << 20), 1 << 20);
|
||||
d[j] = (d[j] as i32 + r).max(0).min(p.max) as u16;
|
||||
@@ -295,8 +312,8 @@ fn col_tx8<const N: usize>(x: [Cols<8>; N], adst: bool) -> [Cols<8>; N] {
|
||||
|
||||
/// A slice of known length as an array.
|
||||
#[inline(always)]
|
||||
fn fixed<T: Copy, const M: usize>(x: &[T]) -> [T; M] {
|
||||
x[..M].try_into().expect("transform size")
|
||||
fn fixed<T: Copy + From<i32>, const M: usize>(x: &[T]) -> [T; M] {
|
||||
x.first_chunk::<M>().copied().unwrap_or([T::from(0); M])
|
||||
}
|
||||
|
||||
/// ADST4 (8.7.1.6) on 4 columns.
|
||||
|
||||
Reference in New Issue
Block a user