1use alloc::vec::Vec;
4use core::num::NonZeroUsize;
5
6use crate::{scale::Downscale, types::Rect};
7
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
10pub struct TileBatchOptions {
11 pub workers: Option<NonZeroUsize>,
13}
14
15impl TileBatchOptions {
16 pub const fn new(workers: Option<NonZeroUsize>) -> Self {
18 Self { workers }
19 }
20}
21
22pub type IndexedBatchResult<T, E> = (usize, Result<T, E>);
24
25pub struct TileDecodeJob<'i, 'o> {
27 pub input: &'i [u8],
29 pub out: &'o mut [u8],
31 pub stride: usize,
33}
34
35pub struct TileRegionDecodeJob<'i, 'o> {
37 pub input: &'i [u8],
39 pub out: &'o mut [u8],
41 pub stride: usize,
43 pub roi: Rect,
45}
46
47pub struct TileScaledDecodeJob<'i, 'o> {
49 pub input: &'i [u8],
51 pub out: &'o mut [u8],
53 pub stride: usize,
55 pub scale: Downscale,
57}
58
59pub struct TileRegionScaledDecodeJob<'i, 'o> {
61 pub input: &'i [u8],
63 pub out: &'o mut [u8],
65 pub stride: usize,
67 pub roi: Rect,
69 pub scale: Downscale,
71}
72
73#[derive(Debug)]
75pub struct TileBatchError<E> {
76 pub index: usize,
78 pub source: E,
80}
81
82impl<E: core::fmt::Display> core::fmt::Display for TileBatchError<E> {
83 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84 write!(f, "tile {} decode failed: {}", self.index, self.source)
85 }
86}
87
88impl<E: core::error::Error + 'static> core::error::Error for TileBatchError<E> {
89 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
90 Some(&self.source)
91 }
92}
93
94pub fn tile_batch_worker_count(
99 batch_size: usize,
100 options: TileBatchOptions,
101 available_workers: usize,
102) -> usize {
103 if batch_size <= 1 {
104 return 1;
105 }
106 let workers = options.workers.map_or(available_workers, NonZeroUsize::get);
107 workers.max(1).min(batch_size)
108}
109
110pub fn collect_indexed_batch_results<T, E, B, F>(
120 job_count: usize,
121 results: Vec<IndexedBatchResult<T, E>>,
122 make_error: F,
123) -> Result<Vec<T>, B>
124where
125 F: FnOnce(usize, E) -> B,
126{
127 let mut outcomes = Vec::with_capacity(job_count);
128 outcomes.resize_with(job_count, || None);
129 let mut first_error = None::<(usize, E)>;
130 for (index, result) in results {
131 assert!(
132 index < job_count,
133 "indexed batch result index {index} outside job count {job_count}"
134 );
135 match result {
136 Ok(outcome) => outcomes[index] = Some(outcome),
137 Err(source) => {
138 if first_error
139 .as_ref()
140 .is_none_or(|(current, _)| index < *current)
141 {
142 first_error = Some((index, source));
143 }
144 }
145 }
146 }
147
148 if let Some((index, source)) = first_error {
149 return Err(make_error(index, source));
150 }
151
152 Ok(outcomes
153 .into_iter()
154 .map(|outcome| outcome.expect("successful batch stores one outcome per tile"))
155 .collect())
156}