diff --git a/src/slice/mod.rs b/src/slice/mod.rs index bcc112fa5..b53defad4 100644 --- a/src/slice/mod.rs +++ b/src/slice/mod.rs @@ -9,6 +9,7 @@ mod chunk_by; mod chunks; mod rchunks; mod sort; +mod windows; mod test; @@ -24,6 +25,7 @@ use std::fmt::{self, Debug}; pub use self::chunk_by::{ChunkBy, ChunkByMut}; pub use self::chunks::{Chunks, ChunksExact, ChunksExactMut, ChunksMut}; pub use self::rchunks::{RChunks, RChunksExact, RChunksExactMut, RChunksMut}; +pub use self::windows::{ArrayWindows, Windows}; /// Parallel extensions for slices. pub trait ParallelSlice { @@ -88,10 +90,21 @@ pub trait ParallelSlice { /// assert_eq!(vec![[1, 2], [2, 3]], windows); /// ``` fn par_windows(&self, window_size: usize) -> Windows<'_, T> { - Windows { - window_size, - slice: self.as_parallel_slice(), - } + Windows::new(window_size, self.as_parallel_slice()) + } + + /// Returns a parallel iterator over all contiguous array windows of + /// length `N`. The windows overlap. + /// + /// # Examples + /// + /// ``` + /// use rayon::prelude::*; + /// let windows: Vec<_> = [1, 2, 3].par_array_windows().collect(); + /// assert_eq!(vec![&[1, 2], &[2, 3]], windows); + /// ``` + fn par_array_windows(&self) -> ArrayWindows<'_, T, N> { + ArrayWindows::new(self.as_parallel_slice()) } /// Returns a parallel iterator over at most `chunk_size` elements of @@ -861,88 +874,6 @@ impl<'data, T: 'data + Sync> Producer for IterProducer<'data, T> { } } -/// Parallel iterator over immutable overlapping windows of a slice -#[derive(Debug)] -pub struct Windows<'data, T> { - window_size: usize, - slice: &'data [T], -} - -impl Clone for Windows<'_, T> { - fn clone(&self) -> Self { - Windows { ..*self } - } -} - -impl<'data, T: Sync> ParallelIterator for Windows<'data, T> { - type Item = &'data [T]; - - fn drive_unindexed(self, consumer: C) -> C::Result - where - C: UnindexedConsumer, - { - bridge(self, consumer) - } - - fn opt_len(&self) -> Option { - Some(self.len()) - } -} - -impl IndexedParallelIterator for Windows<'_, T> { - fn drive(self, consumer: C) -> C::Result - where - C: Consumer, - { - bridge(self, consumer) - } - - fn len(&self) -> usize { - assert!(self.window_size >= 1); - self.slice.len().saturating_sub(self.window_size - 1) - } - - fn with_producer(self, callback: CB) -> CB::Output - where - CB: ProducerCallback, - { - callback.callback(WindowsProducer { - window_size: self.window_size, - slice: self.slice, - }) - } -} - -struct WindowsProducer<'data, T: Sync> { - window_size: usize, - slice: &'data [T], -} - -impl<'data, T: 'data + Sync> Producer for WindowsProducer<'data, T> { - type Item = &'data [T]; - type IntoIter = ::std::slice::Windows<'data, T>; - - fn into_iter(self) -> Self::IntoIter { - self.slice.windows(self.window_size) - } - - fn split_at(self, index: usize) -> (Self, Self) { - let left_index = Ord::min(self.slice.len(), index + (self.window_size - 1)); - let left = &self.slice[..left_index]; - let right = &self.slice[index..]; - ( - WindowsProducer { - window_size: self.window_size, - slice: left, - }, - WindowsProducer { - window_size: self.window_size, - slice: right, - }, - ) - } -} - /// Parallel iterator over mutable items in a slice #[derive(Debug)] pub struct IterMut<'data, T> { diff --git a/src/slice/windows.rs b/src/slice/windows.rs new file mode 100644 index 000000000..ff2633f63 --- /dev/null +++ b/src/slice/windows.rs @@ -0,0 +1,147 @@ +use crate::iter::plumbing::*; +use crate::iter::*; + +/// Parallel iterator over immutable overlapping windows of a slice +#[derive(Debug)] +pub struct Windows<'data, T> { + window_size: usize, + slice: &'data [T], +} + +impl<'data, T> Windows<'data, T> { + pub(super) fn new(window_size: usize, slice: &'data [T]) -> Self { + Self { window_size, slice } + } +} + +impl Clone for Windows<'_, T> { + fn clone(&self) -> Self { + Windows { ..*self } + } +} + +impl<'data, T: Sync> ParallelIterator for Windows<'data, T> { + type Item = &'data [T]; + + fn drive_unindexed(self, consumer: C) -> C::Result + where + C: UnindexedConsumer, + { + bridge(self, consumer) + } + + fn opt_len(&self) -> Option { + Some(self.len()) + } +} + +impl IndexedParallelIterator for Windows<'_, T> { + fn drive(self, consumer: C) -> C::Result + where + C: Consumer, + { + bridge(self, consumer) + } + + fn len(&self) -> usize { + assert!(self.window_size >= 1); + self.slice.len().saturating_sub(self.window_size - 1) + } + + fn with_producer(self, callback: CB) -> CB::Output + where + CB: ProducerCallback, + { + callback.callback(WindowsProducer { + window_size: self.window_size, + slice: self.slice, + }) + } +} + +struct WindowsProducer<'data, T: Sync> { + window_size: usize, + slice: &'data [T], +} + +impl<'data, T: Sync> Producer for WindowsProducer<'data, T> { + type Item = &'data [T]; + type IntoIter = ::std::slice::Windows<'data, T>; + + fn into_iter(self) -> Self::IntoIter { + self.slice.windows(self.window_size) + } + + fn split_at(self, index: usize) -> (Self, Self) { + let left_index = Ord::min(self.slice.len(), index + (self.window_size - 1)); + let left = &self.slice[..left_index]; + let right = &self.slice[index..]; + ( + WindowsProducer { + window_size: self.window_size, + slice: left, + }, + WindowsProducer { + window_size: self.window_size, + slice: right, + }, + ) + } +} + +/// Parallel iterator over immutable overlapping windows of a slice +#[derive(Debug)] +pub struct ArrayWindows<'data, T: Sync, const N: usize> { + slice: &'data [T], +} + +impl<'data, T: Sync, const N: usize> ArrayWindows<'data, T, N> { + pub(super) fn new(slice: &'data [T]) -> Self { + ArrayWindows { slice } + } +} + +impl Clone for ArrayWindows<'_, T, N> { + fn clone(&self) -> Self { + ArrayWindows { ..*self } + } +} + +impl<'data, T: Sync, const N: usize> ParallelIterator for ArrayWindows<'data, T, N> { + type Item = &'data [T; N]; + + fn drive_unindexed(self, consumer: C) -> C::Result + where + C: UnindexedConsumer, + { + bridge(self, consumer) + } + + fn opt_len(&self) -> Option { + Some(self.len()) + } +} + +impl IndexedParallelIterator for ArrayWindows<'_, T, N> { + fn drive(self, consumer: C) -> C::Result + where + C: Consumer, + { + bridge(self, consumer) + } + + fn len(&self) -> usize { + assert!(N >= 1); + self.slice.len().saturating_sub(const { N - 1 }) + } + + fn with_producer(self, callback: CB) -> CB::Output + where + CB: ProducerCallback, + { + // TODO (MSRV 1.94): use our own producer and the standard `array_windows` + Windows::new(N, self.slice) + .map(|slice| slice.try_into().unwrap()) + .with_producer(callback) + } +} diff --git a/tests/clones.rs b/tests/clones.rs index 510f13375..127e47e79 100644 --- a/tests/clones.rs +++ b/tests/clones.rs @@ -115,6 +115,7 @@ fn clone_vec() { check(v.par_rchunks(42)); check(v.par_rchunks_exact(42)); check(v.par_windows(42)); + check(v.par_array_windows::<42>()); check(v.par_split(|x| x % 3 == 0)); check(v.par_split_inclusive(|x| x % 3 == 0)); check(v.into_par_iter()); diff --git a/tests/debug.rs b/tests/debug.rs index 4ab24a96b..21bafd4bc 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -132,6 +132,7 @@ fn debug_vec() { check(v.par_rchunks_mut(42)); check(v.par_rchunks_exact_mut(42)); check(v.par_windows(42)); + check(v.par_array_windows::<42>()); check(v.par_split(|x| x % 3 == 0)); check(v.par_split_inclusive(|x| x % 3 == 0)); check(v.par_split_mut(|x| x % 3 == 0)); diff --git a/tests/producer_split_at.rs b/tests/producer_split_at.rs index 1be04d4c1..4571a6a3b 100644 --- a/tests/producer_split_at.rs +++ b/tests/producer_split_at.rs @@ -256,6 +256,13 @@ fn slice_windows() { check(&v, || s.par_windows(2)); } +#[test] +fn slice_array_windows() { + let s: Vec<_> = (0..10).collect(); + let v: Vec<&[_; 2]> = s.array_windows().collect(); + check(&v, || s.par_array_windows::<2>()); +} + #[test] fn vec() { let v: Vec<_> = (0..10).collect();