use super::{mod_ceil, ChunkConfig, ChunkWindow};
use std::{iter::*, ops::Range};
impl<'a> IntoIterator for &'a ChunkConfig {
type Item = ChunkWindow<'a>;
type IntoIter = Map<Range<usize>, Box<dyn Fn(usize) -> ChunkWindow<'a> + 'a>>;
fn into_iter(self) -> Self::IntoIter {
let (count, func) = self.iter_mapper();
(0..count).map(Box::new(func))
}
}
impl ChunkConfig {
fn check_preconditions(&self) {
debug_assert!(
self.block_size > 0
&& self.start >= self.padding
&& self.end <= self.height
&& self.data_height % self.block_size == 0,
"ChunkConfig preconditions failed"
);
}
fn calc_initial_chunk(&self) -> [usize; 3] {
if self.start >= self.end {
return [0, 0, 0];
}
let mut data_end = (self.start + self.data_height).min(self.end);
debug_assert!(data_end > self.start);
let mut load_end = mod_ceil(data_end + self.padding, self.block_size).min(self.height);
debug_assert!(load_end > self.start);
data_end = (load_end - self.padding).max(self.start);
if data_end > self.end {
data_end = self.end;
load_end = data_end + self.padding;
}
let count = {
let dcount = mod_ceil(self.end - data_end, self.data_height) / self.data_height;
let lcount = mod_ceil(self.height - load_end, self.data_height) / self.data_height;
dcount.min(lcount)
} + 1;
debug_assert!(count == 1 || load_end % self.block_size == 0);
[count, data_end, load_end]
}
pub(super) fn iter_mapper<'a>(&'a self) -> (usize, impl Fn(usize) -> ChunkWindow<'a> + 'a) {
self.check_preconditions();
let [count, initial_data_end, initial_load_end] = self.calc_initial_chunk();
(count, move |i| {
let (data_start, _, load_end) = if i == 0 {
(self.start, initial_data_end, initial_load_end)
} else if i < count - 1 {
let data_start = initial_data_end + (i - 1) * self.data_height;
let data_end = data_start + self.data_height;
let load_end = data_end + self.padding;
(data_start, data_end, load_end)
} else {
let data_start = initial_data_end + (i - 1) * self.data_height;
let data_end = (data_start + self.data_height).min(self.end);
let load_end = (data_end + self.padding).min(self.height);
let data_end = load_end - self.padding;
(data_start, data_end, load_end)
};
let load_start = data_start - self.padding;
(self, load_start, (load_end - load_start) as usize)
})
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = ChunkWindow> + '_ {
let (count, func) = self.iter_mapper();
(0..count).map(func)
}
}