From 76c978ae1fccd5ff49b8a2591b8e9aca3585816a Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Wed, 9 Sep 2020 00:46:05 +0200 Subject: [PATCH] Add ChunksFixed iterator --- src/lib.rs | 1 + src/utils.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/utils.rs diff --git a/src/lib.rs b/src/lib.rs index edd41a2..a2d70af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod app; mod canvas; mod controls; mod home; +mod utils; #[wasm_bindgen(start)] pub fn run_app() { diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..128695e --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,129 @@ +/// Utils: general utilities. + +/// An iterator over a slice in a fixed number of non-overlapping +/// chunks, starting at the beginning of the slice. +/// +/// When the slice len is not evenly divisible by the number of +/// chunks, the length of individual chunks may vary, with each chunk +/// being augmented by a single element until the remainder is +/// evenly distributed. +/// +/// TODO: +/// - implement size_hint, count, etc as appropriate +/// - improve test coverage +/// - extract to own crate +#[derive(Debug, Clone)] +pub struct ChunksFixed<'a, T: 'a> { + v: &'a [T], + size: usize, + rem: usize, +} + +pub fn chunks_fixed(slice: &[T], num_chunks: usize) -> ChunksFixed { + ChunksFixed { + v: slice, + size: slice.len() / num_chunks, + rem: slice.len() % num_chunks, + } +} + +impl<'a, T> Iterator for ChunksFixed<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.v.is_empty() { + return None; + } + + let s = if self.rem > 0 { + self.rem -= 1; + self.size + 1 + } else { + self.size + }; + + let (fst, snd) = self.v.split_at(s); + self.v = snd; + Some(fst) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_chunks_fixed_10_into_10() { + let v = vec![0; 10]; + let chunks = chunks_fixed(&v, 10).collect::>(); + + assert_eq!(chunks.len(), 10); + chunks.iter().for_each(|chunk| assert_eq!(chunk.len(), 1)); + } + + #[test] + fn test_chunks_fixed_100_into_10() { + let v = vec![0; 100]; + let chunks = chunks_fixed(&v, 10).collect::>(); + + assert_eq!(chunks.len(), 10); + chunks.iter().for_each(|chunk| assert_eq!(chunk.len(), 10)); + } + + #[test] + fn test_chunks_fixed_101_into_10() { + let v = vec![0; 101]; + let chunks = chunks_fixed(&v, 10).collect::>(); + + assert_eq!(chunks.len(), 10); + chunks.iter().enumerate().for_each(|(i, chunk)| { + if i == 0 { + assert_eq!(chunk.len(), 11); + } else { + assert_eq!(chunk.len(), 10); + } + }); + } + + #[test] + fn test_chunks_fixed_10399_into_800() { + let v = vec![0; 10399]; + let chunks = chunks_fixed(&v, 800).collect::>(); + + assert_eq!(chunks.len(), 800); + chunks.iter().enumerate().for_each(|(i, chunk)| { + if i < 799 { + assert_eq!(chunk.len(), 13); + } else { + assert_eq!(chunk.len(), 12); + } + }); + } + + #[test] + fn test_chunks_fixed_10400_into_800() { + let v = vec![0; 10400]; + let chunks = chunks_fixed(&v, 800).collect::>(); + + assert_eq!(chunks.len(), 800); + chunks.iter().for_each(|chunk| { + assert_eq!(chunk.len(), 13); + }); + } + + #[test] + fn test_chunks_fixed_10401_into_800() { + let v = vec![0; 10401]; + let chunks = chunks_fixed(&v, 800).collect::>(); + + assert_eq!(chunks.len(), 800); + chunks.iter().enumerate().for_each(|(i, chunk)| { + if i == 0 { + assert_eq!(chunk.len(), 14); + } else { + assert_eq!(chunk.len(), 13); + } + }); + } +}