use alloc::vec::Vec;
use core::num::NonZeroUsize;
use crate::{scale::Downscale, types::Rect};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct TileBatchOptions {
pub workers: Option<NonZeroUsize>,
}
impl TileBatchOptions {
pub const fn new(workers: Option<NonZeroUsize>) -> Self {
Self { workers }
}
}
pub type IndexedBatchResult<T, E> = (usize, Result<T, E>);
pub struct TileDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
}
pub struct TileRegionDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
pub roi: Rect,
}
pub struct TileScaledDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
pub scale: Downscale,
}
pub struct TileRegionScaledDecodeJob<'i, 'o> {
pub input: &'i [u8],
pub out: &'o mut [u8],
pub stride: usize,
pub roi: Rect,
pub scale: Downscale,
}
#[derive(Debug)]
pub struct TileBatchError<E> {
pub index: usize,
pub source: E,
}
impl<E: core::fmt::Display> core::fmt::Display for TileBatchError<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "tile {} decode failed: {}", self.index, self.source)
}
}
impl<E: core::error::Error + 'static> core::error::Error for TileBatchError<E> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(&self.source)
}
}
pub fn tile_batch_worker_count(
batch_size: usize,
options: TileBatchOptions,
available_workers: usize,
) -> usize {
if batch_size <= 1 {
return 1;
}
let workers = options.workers.map_or(available_workers, NonZeroUsize::get);
workers.max(1).min(batch_size)
}
pub fn collect_indexed_batch_results<T, E, B, F>(
job_count: usize,
results: Vec<IndexedBatchResult<T, E>>,
make_error: F,
) -> Result<Vec<T>, B>
where
F: FnOnce(usize, E) -> B,
{
let mut outcomes = Vec::with_capacity(job_count);
outcomes.resize_with(job_count, || None);
let mut first_error = None::<(usize, E)>;
for (index, result) in results {
assert!(
index < job_count,
"indexed batch result index {index} outside job count {job_count}"
);
match result {
Ok(outcome) => outcomes[index] = Some(outcome),
Err(source) => {
if first_error
.as_ref()
.is_none_or(|(current, _)| index < *current)
{
first_error = Some((index, source));
}
}
}
}
if let Some((index, source)) = first_error {
return Err(make_error(index, source));
}
Ok(outcomes
.into_iter()
.map(|outcome| outcome.expect("successful batch stores one outcome per tile"))
.collect())
}