#![forbid(unsafe_code)]
use thiserror::Error;
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Av1RefSlotsError {
#[error("AV1 DPB slot {index} still has an outstanding Zero-Copy handle")]
SlotOutstanding {
index: usize,
},
#[error("no free AV1 DPB slot available (capacity {capacity})")]
NoFreeSlot {
capacity: usize,
},
#[error("AV1 DPB slot index {index} out of range (capacity {capacity})")]
InvalidSlotIndex {
index: usize,
capacity: usize,
},
}
pub(crate) struct Av1RefSlots {
occupied: Vec<bool>,
outstanding: Vec<bool>,
}
impl Av1RefSlots {
#[must_use]
pub(crate) fn new(capacity: usize) -> Self {
let capacity = capacity.max(1);
Self {
occupied: vec![false; capacity],
outstanding: vec![false; capacity],
}
}
const fn check_index(&self, index: usize) -> Result<(), Av1RefSlotsError> {
if index >= self.occupied.len() {
Err(Av1RefSlotsError::InvalidSlotIndex {
index,
capacity: self.occupied.len(),
})
} else {
Ok(())
}
}
pub(crate) fn mark_outstanding(&mut self, index: usize) -> Result<(), Av1RefSlotsError> {
self.check_index(index)?;
self.outstanding[index] = true;
Ok(())
}
pub(crate) fn clear_all(&mut self) -> Result<(), Av1RefSlotsError> {
for index in 0..self.occupied.len() {
if self.occupied[index] {
if self.outstanding[index] {
return Err(Av1RefSlotsError::SlotOutstanding { index });
}
self.occupied[index] = false;
}
}
Ok(())
}
pub(crate) fn allocate_slot(&mut self) -> Result<usize, Av1RefSlotsError> {
let index = self.occupied.iter().position(|&occupied| !occupied).ok_or(
Av1RefSlotsError::NoFreeSlot {
capacity: self.occupied.len(),
},
)?;
self.occupied[index] = true;
Ok(index)
}
}
#[cfg(test)]
#[path = "av1_refs_tests.rs"]
mod tests;