| @@ -37,7 +37,7 @@ where | |||||
| operation: O, | operation: O, | ||||
| ) -> Self::Result | ) -> Self::Result | ||||
| where | where | ||||
| O: Fn(T2) -> T1 + Send + 'a; | |||||
| O: FnMut(T2) -> T1 + Send + 'a; | |||||
| } | } | ||||
| pub struct ExecutorCallback<E, C> { | pub struct ExecutorCallback<E, C> { | ||||
| @@ -0,0 +1,47 @@ | |||||
| use super::{Consumer, Reducer}; | |||||
| /// `ParallelExtend` extends an existing collection with items from a [`ParallelIterator`]. | |||||
| /// | |||||
| /// [`ParallelIterator`]: trait.ParallelIterator.html | |||||
| /// | |||||
| /// # Examples | |||||
| /// | |||||
| /// Implementing `ParallelExtend` for your type: | |||||
| /// | |||||
| /// ``` | |||||
| /// use rayon::prelude::*; | |||||
| /// use std::mem; | |||||
| /// | |||||
| /// struct BlackHole { | |||||
| /// mass: usize, | |||||
| /// } | |||||
| /// | |||||
| /// impl<T: Send> ParallelExtend<T> for BlackHole { | |||||
| /// fn par_extend<I>(&mut self, par_iter: I) | |||||
| /// where I: IntoParallelIterator<Item = T> | |||||
| /// { | |||||
| /// let par_iter = par_iter.into_par_iter(); | |||||
| /// self.mass += par_iter.count() * mem::size_of::<T>(); | |||||
| /// } | |||||
| /// } | |||||
| /// | |||||
| /// let mut bh = BlackHole { mass: 0 }; | |||||
| /// bh.par_extend(0i32..1000); | |||||
| /// assert_eq!(bh.mass, 4000); | |||||
| /// bh.par_extend(0i64..10); | |||||
| /// assert_eq!(bh.mass, 4080); | |||||
| /// ``` | |||||
| pub trait ParallelExtend<'a, I, T>: Send + Sized | |||||
| where | |||||
| I: Send + 'a, | |||||
| T: Send, | |||||
| <Self::Consumer as Consumer<I>>::Reducer: Reducer<T>, | |||||
| { | |||||
| type Consumer: Consumer<I, Result = T> + 'a; | |||||
| /// Creates a consumer that is used to handle the items from the iterator. | |||||
| fn into_consumer(self) -> Self::Consumer; | |||||
| /// Converts the result of the consumer into the final type | |||||
| fn map_result(inner: T) -> Self; | |||||
| } | |||||
| @@ -36,9 +36,9 @@ use super::{Executor, IntoParallelIterator}; | |||||
| /// let bh: BlackHole = (0i32..1000).into_par_iter().collect(); | /// let bh: BlackHole = (0i32..1000).into_par_iter().collect(); | ||||
| /// assert_eq!(bh.mass, 4000); | /// assert_eq!(bh.mass, 4000); | ||||
| /// ``` | /// ``` | ||||
| pub trait FromParallelIterator<T>: Send + Sized | |||||
| pub trait FromParallelIterator<'a, T>: Send + Sized | |||||
| where | where | ||||
| T: Send, | |||||
| T: Send + 'a, | |||||
| { | { | ||||
| /// Creates an instance of the collection from the parallel iterator `iterator`. | /// Creates an instance of the collection from the parallel iterator `iterator`. | ||||
| /// | /// | ||||
| @@ -55,15 +55,15 @@ where | |||||
| /// [`iterator.fold`]: trait.ParallelIterator.html#method.fold | /// [`iterator.fold`]: trait.ParallelIterator.html#method.fold | ||||
| /// [`iterator.fold_with`]: trait.ParallelIterator.html#method.fold_with | /// [`iterator.fold_with`]: trait.ParallelIterator.html#method.fold_with | ||||
| /// [`iterator.for_each`]: trait.ParallelIterator.html#method.for_each | /// [`iterator.for_each`]: trait.ParallelIterator.html#method.for_each | ||||
| fn from_par_iter<'a, E, X>(executor: E, iterator: X) -> E::Result | |||||
| fn from_par_iter<E, X>(executor: E, iterator: X) -> E::Result | |||||
| where | where | ||||
| E: Executor<'a, Self>, | E: Executor<'a, Self>, | ||||
| X: IntoParallelIterator<'a, Item = T>, | X: IntoParallelIterator<'a, Item = T>, | ||||
| T: 'a; | T: 'a; | ||||
| } | } | ||||
| impl FromParallelIterator<()> for () { | |||||
| fn from_par_iter<'a, E, X>(executor: E, iterator: X) -> E::Result | |||||
| impl<'a> FromParallelIterator<'a, ()> for () { | |||||
| fn from_par_iter<E, X>(executor: E, iterator: X) -> E::Result | |||||
| where | where | ||||
| E: Executor<'a, Self>, | E: Executor<'a, Self>, | ||||
| X: IntoParallelIterator<'a, Item = ()>, | X: IntoParallelIterator<'a, Item = ()>, | ||||
| @@ -32,6 +32,7 @@ use crate::{ | |||||
| try_fold::{TryFold, TryFoldWith}, | try_fold::{TryFold, TryFoldWith}, | ||||
| try_for_each::{TryForEach, TryForEachInit, TryForEachWith}, | try_for_each::{TryForEach, TryForEachInit, TryForEachWith}, | ||||
| try_reduce::{TryReduce, TryReduceWith}, | try_reduce::{TryReduce, TryReduceWith}, | ||||
| unzip::Unzip, | |||||
| update::Update, | update::Update, | ||||
| while_some::WhileSome, | while_some::WhileSome, | ||||
| }, | }, | ||||
| @@ -1590,10 +1591,48 @@ pub trait ParallelIterator<'a>: Sized + Send { | |||||
| /// ``` | /// ``` | ||||
| fn collect<T>(self) -> Collect<Self, T> | fn collect<T>(self) -> Collect<Self, T> | ||||
| where | where | ||||
| T: FromParallelIterator<Self::Item>, | |||||
| T: FromParallelIterator<'a, Self::Item>, | |||||
| { | { | ||||
| Collect::new(self) | Collect::new(self) | ||||
| } | } | ||||
| /// Unzips the items of a parallel iterator into a pair of arbitrary | |||||
| /// `ParallelExtend` containers. | |||||
| /// | |||||
| /// You may prefer to use `unzip_into_vecs()`, which allocates more | |||||
| /// efficiently with precise knowledge of how many elements the | |||||
| /// iterator contains, and even allows you to reuse existing | |||||
| /// vectors' backing stores rather than allocating fresh vectors. | |||||
| /// | |||||
| /// # Examples | |||||
| /// | |||||
| /// ``` | |||||
| /// use rayon::prelude::*; | |||||
| /// | |||||
| /// let a = [(0, 1), (1, 2), (2, 3), (3, 4)]; | |||||
| /// | |||||
| /// let (left, right): (Vec<_>, Vec<_>) = a.par_iter().cloned().unzip(); | |||||
| /// | |||||
| /// assert_eq!(left, [0, 1, 2, 3]); | |||||
| /// assert_eq!(right, [1, 2, 3, 4]); | |||||
| /// ``` | |||||
| /// | |||||
| /// Nested pairs can be unzipped too. | |||||
| /// | |||||
| /// ``` | |||||
| /// use rayon::prelude::*; | |||||
| /// | |||||
| /// let (values, squares, cubes): (Vec<_>, Vec<_>, Vec<_>) = (0..4).into_par_iter() | |||||
| /// .map(|i| (i, i * i, i * i * i)) | |||||
| /// .unzip(); | |||||
| /// | |||||
| /// assert_eq!(values, [0, 1, 2, 3]); | |||||
| /// assert_eq!(squares, [0, 1, 4, 9]); | |||||
| /// assert_eq!(cubes, [0, 1, 8, 27]); | |||||
| /// ``` | |||||
| fn unzip(self) -> Unzip<Self> { | |||||
| Unzip::new(self) | |||||
| } | |||||
| } | } | ||||
| /// An iterator that supports "random access" to its data, meaning | /// An iterator that supports "random access" to its data, meaning | ||||
| @@ -2,6 +2,7 @@ mod consumer; | |||||
| mod drain; | mod drain; | ||||
| mod driver; | mod driver; | ||||
| mod executor; | mod executor; | ||||
| mod extend; | |||||
| mod folder; | mod folder; | ||||
| mod from_iter; | mod from_iter; | ||||
| mod into_iter; | mod into_iter; | ||||
| @@ -13,6 +14,7 @@ pub use consumer::Consumer; | |||||
| pub use drain::{ParallelDrainFull, ParallelDrainRange}; | pub use drain::{ParallelDrainFull, ParallelDrainRange}; | ||||
| pub use driver::Driver; | pub use driver::Driver; | ||||
| pub use executor::{Executor, ExecutorCallback}; | pub use executor::{Executor, ExecutorCallback}; | ||||
| pub use extend::ParallelExtend; | |||||
| pub use folder::Folder; | pub use folder::Folder; | ||||
| pub use from_iter::FromParallelIterator; | pub use from_iter::FromParallelIterator; | ||||
| pub use into_iter::{IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator}; | pub use into_iter::{IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator}; | ||||
| @@ -55,10 +55,10 @@ where | |||||
| fn map<O>( | fn map<O>( | ||||
| inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | ||||
| operation: O, | |||||
| mut operation: O, | |||||
| ) -> Self::Result | ) -> Self::Result | ||||
| where | where | ||||
| O: Fn(T2) -> T1, | |||||
| O: FnMut(T2) -> T1, | |||||
| { | { | ||||
| operation(inner) | operation(inner) | ||||
| } | } | ||||
| @@ -96,10 +96,10 @@ where | |||||
| fn map<O>( | fn map<O>( | ||||
| inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | ||||
| operation: O, | |||||
| mut operation: O, | |||||
| ) -> Self::Result | ) -> Self::Result | ||||
| where | where | ||||
| O: Fn(T2) -> T1 + Send + 'a, | |||||
| O: FnMut(T2) -> T1 + Send + 'a, | |||||
| { | { | ||||
| async move { | async move { | ||||
| let value = inner.await; | let value = inner.await; | ||||
| @@ -22,7 +22,7 @@ impl<X, T> Collect<X, T> { | |||||
| impl<'a, X, T> Driver<'a, T> for Collect<X, T> | impl<'a, X, T> Driver<'a, T> for Collect<X, T> | ||||
| where | where | ||||
| X: ParallelIterator<'a>, | X: ParallelIterator<'a>, | ||||
| T: FromParallelIterator<X::Item> + Send + 'a, | |||||
| T: FromParallelIterator<'a, X::Item> + Send + 'a, | |||||
| { | { | ||||
| fn exec_with<E>(self, executor: E) -> E::Result | fn exec_with<E>(self, executor: E) -> E::Result | ||||
| where | where | ||||
| @@ -23,6 +23,7 @@ pub mod sum; | |||||
| pub mod try_fold; | pub mod try_fold; | ||||
| pub mod try_for_each; | pub mod try_for_each; | ||||
| pub mod try_reduce; | pub mod try_reduce; | ||||
| pub mod unzip; | |||||
| pub mod update; | pub mod update; | ||||
| pub mod while_some; | pub mod while_some; | ||||
| @@ -49,7 +50,7 @@ mod tests { | |||||
| vec![11usize, 12usize], | vec![11usize, 12usize], | ||||
| ]; | ]; | ||||
| let x = a | |||||
| let (x, y, z): (Vec<_>, Vec<_>, Vec<_>) = a | |||||
| .par_iter() | .par_iter() | ||||
| .cloned() | .cloned() | ||||
| .chain(b) | .chain(b) | ||||
| @@ -60,22 +61,19 @@ mod tests { | |||||
| .while_some() | .while_some() | ||||
| .map_init( | .map_init( | ||||
| move || i.fetch_add(1, Ordering::Relaxed), | move || i.fetch_add(1, Ordering::Relaxed), | ||||
| |init, item| Some((item, *init)), | |||||
| |init, item| (*init, item), | |||||
| ) | ) | ||||
| .try_fold_with(String::new(), |s, item| -> Result<String, ()> { | |||||
| Ok(format!("{} + {:?}", s, item)) | |||||
| }) | |||||
| .try_for_each_init( | |||||
| move || j.fetch_add(1, Ordering::Relaxed), | |||||
| |init, item| -> Result<(), ()> { | |||||
| println!("{:?} - {:?}", init, item); | |||||
| Ok(()) | |||||
| }, | |||||
| .map_init( | |||||
| move || j.fetch_add(2, Ordering::Relaxed), | |||||
| |init, (init2, item)| (*init, init2, item), | |||||
| ) | ) | ||||
| .unzip() | |||||
| .exec() | .exec() | ||||
| .await; | .await; | ||||
| dbg!(&x); | dbg!(&x); | ||||
| dbg!(&y); | |||||
| dbg!(&z); | |||||
| } | } | ||||
| #[tokio::test] | #[tokio::test] | ||||
| @@ -0,0 +1,180 @@ | |||||
| use crate::{core::Driver, Consumer, Executor, Folder, ParallelExtend, ParallelIterator, Reducer}; | |||||
| /* Unzip */ | |||||
| pub struct Unzip<X> { | |||||
| iterator: X, | |||||
| } | |||||
| impl<X> Unzip<X> { | |||||
| pub fn new(iterator: X) -> Self { | |||||
| Self { iterator } | |||||
| } | |||||
| } | |||||
| impl<'a, X, P, T> Driver<'a, P, T> for Unzip<X> | |||||
| where | |||||
| X: ParallelIterator<'a>, | |||||
| P: Default + Send + 'a, | |||||
| UnzipExtend<P>: ParallelExtend<'a, X::Item, T>, | |||||
| <<UnzipExtend<P> as ParallelExtend<'a, X::Item, T>>::Consumer as Consumer<X::Item>>::Reducer: | |||||
| Reducer<T> + Send, | |||||
| T: Send + 'a, | |||||
| { | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | |||||
| where | |||||
| E: Executor<'a, P, T>, | |||||
| { | |||||
| let executor = executor.into_inner(); | |||||
| let consumer = UnzipExtend(P::default()).into_consumer(); | |||||
| let inner = self.iterator.drive(executor, consumer); | |||||
| E::map(inner, |inner| { | |||||
| let UnzipExtend(ret) = UnzipExtend::map_result(inner); | |||||
| ret | |||||
| }) | |||||
| } | |||||
| } | |||||
| pub struct UnzipExtend<T>(T); | |||||
| pub struct UnzipConsumer<T>(T); | |||||
| pub struct UnzipFolder<T>(T); | |||||
| pub struct UnzipReducer<T>(T); | |||||
| pub struct UnzipResult<T>(T); | |||||
| macro_rules! parallel_extend_tuple { | |||||
| (($($E:ident),*), ($($I:ident),*), ($($T:ident),*), ($($C:ident),*), ($($F:ident),*), ($($R:ident),*)) => { | |||||
| #[allow(non_snake_case)] | |||||
| impl<'a, $($E,)+ $($I,)+ $($T,)+> ParallelExtend<'a, ($($I,)+), UnzipResult<($($T,)+)>> for UnzipExtend<($($E,)+)> | |||||
| where | |||||
| $($E: ParallelExtend<'a, $I, $T> + Send,)+ | |||||
| $(<$E::Consumer as Consumer<$I>>::Reducer: Reducer<$T>,)+ | |||||
| $($I: Send + 'a,)+ | |||||
| $($T: Send,)+ | |||||
| { | |||||
| type Consumer = UnzipConsumer<($($E::Consumer,)+)>; | |||||
| fn into_consumer(self) -> Self::Consumer { | |||||
| let UnzipExtend(($($E,)+)) = self; | |||||
| UnzipConsumer(($($E.into_consumer(),)+)) | |||||
| } | |||||
| fn map_result(inner: UnzipResult<($($T,)+)>) -> Self { | |||||
| let UnzipResult(($($T,)+)) = inner; | |||||
| UnzipExtend(($($E::map_result($T),)+)) | |||||
| } | |||||
| } | |||||
| #[allow(non_snake_case)] | |||||
| impl<$($C,)+ $($I,)+> Consumer<($($I,)+)> for UnzipConsumer<($($C,)+)> | |||||
| where | |||||
| $($C: Consumer<$I>,)+ | |||||
| { | |||||
| type Folder = UnzipFolder<($($C::Folder,)+)>; | |||||
| type Reducer = UnzipReducer<($($C::Reducer,)+)>; | |||||
| type Result = UnzipResult<($($C::Result,)+)>; | |||||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||||
| let UnzipConsumer(($($C,)+)) = self; | |||||
| let ($($C,)+) = ($($C.split(),)+); | |||||
| ( | |||||
| UnzipConsumer(($($C.0,)+)), UnzipConsumer(($($C.1,)+)), UnzipReducer(($($C.2,)+)) | |||||
| ) | |||||
| } | |||||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||||
| let UnzipConsumer(($($C,)+)) = self; | |||||
| let ($($C,)+) = ($($C.split_at(index),)+); | |||||
| ( | |||||
| UnzipConsumer(($($C.0,)+)), UnzipConsumer(($($C.1,)+)), UnzipReducer(($($C.2,)+)) | |||||
| ) | |||||
| } | |||||
| fn into_folder(self) -> Self::Folder { | |||||
| let UnzipConsumer(($($C,)+)) = self; | |||||
| UnzipFolder(($($C.into_folder(),)+)) | |||||
| } | |||||
| fn is_full(&self) -> bool { | |||||
| let UnzipConsumer(($($C,)+)) = self; | |||||
| true $(&& $C.is_full())+ | |||||
| } | |||||
| } | |||||
| #[allow(non_snake_case)] | |||||
| impl<$($F,)+ $($I,)+> Folder<($($I,)+)> for UnzipFolder<($($F,)+)> | |||||
| where | |||||
| $($F: Folder<$I>,)+ | |||||
| { | |||||
| type Result = UnzipResult<($($F::Result,)+)>; | |||||
| fn consume(self, item: ($($I,)+)) -> Self { | |||||
| let UnzipFolder(($($F,)+)) = self; | |||||
| let ($($I,)+) = item; | |||||
| UnzipFolder(($($F.consume($I),)+)) | |||||
| } | |||||
| fn complete(self) -> Self::Result { | |||||
| let UnzipFolder(($($F,)+)) = self; | |||||
| UnzipResult(($($F.complete(),)+)) | |||||
| } | |||||
| fn is_full(&self) -> bool { | |||||
| let UnzipFolder(($($F,)+)) = self; | |||||
| $($F.is_full() &&)+ true | |||||
| } | |||||
| } | |||||
| #[allow(non_snake_case)] | |||||
| impl<$($R,)+ $($T,)+> Reducer<UnzipResult<($($T,)+)>> for UnzipReducer<($($R,)+)> | |||||
| where | |||||
| $($R: Reducer<$T>,)+ | |||||
| { | |||||
| fn reduce(self, left: UnzipResult<($($T,)+)>, right: UnzipResult<($($T,)+)>) -> UnzipResult<($($T,)+)> { | |||||
| let UnzipReducer(($($R,)+)) = self; | |||||
| let UnzipResult(($($T,)+)) = left; | |||||
| let UnzipResult(($($E,)+)) = right; | |||||
| UnzipResult(($($R.reduce($T, $E),)+)) | |||||
| } | |||||
| } | |||||
| }; | |||||
| } | |||||
| parallel_extend_tuple!((E1, E2), (I1, I2), (T1, T2), (C1, C2), (F1, F2), (R1, R2)); | |||||
| parallel_extend_tuple!( | |||||
| (E1, E2, E3), | |||||
| (I1, I2, I3), | |||||
| (T1, T2, T3), | |||||
| (C1, C2, C3), | |||||
| (F1, F2, F3), | |||||
| (R1, R2, R3) | |||||
| ); | |||||
| parallel_extend_tuple!( | |||||
| (E1, E2, E3, E4), | |||||
| (I1, I2, I3, I4), | |||||
| (T1, T2, T3, T4), | |||||
| (C1, C2, C3, C4), | |||||
| (F1, F2, F3, F4), | |||||
| (R1, R2, R3, R4) | |||||
| ); | |||||
| parallel_extend_tuple!( | |||||
| (E1, E2, E3, E4, E5), | |||||
| (I1, I2, I3, I4, I5), | |||||
| (T1, T2, T3, T4, T5), | |||||
| (C1, C2, C3, C4, C5), | |||||
| (F1, F2, F3, F4, F5), | |||||
| (R1, R2, R3, R4, R5) | |||||
| ); | |||||
| @@ -7,7 +7,7 @@ mod std; | |||||
| pub use self::core::{ | pub use self::core::{ | ||||
| Consumer, Driver, Executor, ExecutorCallback, Folder, IndexedParallelIterator, IndexedProducer, | Consumer, Driver, Executor, ExecutorCallback, Folder, IndexedParallelIterator, IndexedProducer, | ||||
| IndexedProducerCallback, IntoParallelIterator, IntoParallelRefIterator, | IndexedProducerCallback, IntoParallelIterator, IntoParallelRefIterator, | ||||
| IntoParallelRefMutIterator, ParallelDrainFull, ParallelDrainRange, ParallelIterator, Producer, | |||||
| ProducerCallback, Reducer, | |||||
| IntoParallelRefMutIterator, ParallelDrainFull, ParallelDrainRange, ParallelExtend, | |||||
| ParallelIterator, Producer, ProducerCallback, Reducer, | |||||
| }; | }; | ||||
| pub use self::executor::{DefaultExecutor, SequentialExecutor}; | pub use self::executor::{DefaultExecutor, SequentialExecutor}; | ||||
| @@ -1,3 +1,4 @@ | |||||
| use std::collections::LinkedList; | |||||
| use std::iter::FusedIterator; | use std::iter::FusedIterator; | ||||
| use std::mem::replace; | use std::mem::replace; | ||||
| use std::ops::{Range, RangeBounds}; | use std::ops::{Range, RangeBounds}; | ||||
| @@ -6,9 +7,9 @@ use std::slice::{from_raw_parts_mut, IterMut}; | |||||
| use std::sync::Arc; | use std::sync::Arc; | ||||
| use crate::{ | use crate::{ | ||||
| misc::simplify_range, Consumer, Executor, ExecutorCallback, IndexedParallelIterator, | |||||
| misc::simplify_range, Consumer, Executor, ExecutorCallback, Folder, IndexedParallelIterator, | |||||
| IndexedProducer, IndexedProducerCallback, IntoParallelIterator, ParallelDrainRange, | IndexedProducer, IndexedProducerCallback, IntoParallelIterator, ParallelDrainRange, | ||||
| ParallelIterator, Producer, ProducerCallback, Reducer, | |||||
| ParallelExtend, ParallelIterator, Producer, ProducerCallback, Reducer, | |||||
| }; | }; | ||||
| /// Parallel iterator that moves out of a vector. | /// Parallel iterator that moves out of a vector. | ||||
| @@ -455,3 +456,126 @@ impl<'a, T, C> ExactSizeIterator for SliceIter<'a, T, C> { | |||||
| } | } | ||||
| impl<'a, T, C> FusedIterator for SliceIter<'a, T, C> {} | impl<'a, T, C> FusedIterator for SliceIter<'a, T, C> {} | ||||
| /* ParallelExtend */ | |||||
| impl<'a, I> ParallelExtend<'a, I, VecExtendResult<I>> for Vec<I> | |||||
| where | |||||
| I: Send + 'a, | |||||
| { | |||||
| type Consumer = VecConsumer<I>; | |||||
| fn into_consumer(self) -> Self::Consumer { | |||||
| VecConsumer { vec: Some(self) } | |||||
| } | |||||
| fn map_result(inner: VecExtendResult<I>) -> Self { | |||||
| let mut vec = inner.vec.unwrap(); | |||||
| for mut items in inner.items { | |||||
| vec.append(&mut items); | |||||
| } | |||||
| vec | |||||
| } | |||||
| } | |||||
| /* VecConsumer */ | |||||
| pub struct VecConsumer<T> { | |||||
| vec: Option<Vec<T>>, | |||||
| } | |||||
| impl<T> Consumer<T> for VecConsumer<T> | |||||
| where | |||||
| T: Send, | |||||
| { | |||||
| type Folder = VecFolder<T>; | |||||
| type Reducer = VecReducer; | |||||
| type Result = VecExtendResult<T>; | |||||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||||
| let left = VecConsumer { vec: self.vec }; | |||||
| let right = VecConsumer { vec: None }; | |||||
| let reducer = VecReducer; | |||||
| (left, right, reducer) | |||||
| } | |||||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||||
| self.split() | |||||
| } | |||||
| fn into_folder(self) -> Self::Folder { | |||||
| VecFolder { | |||||
| vec: self.vec, | |||||
| items: Vec::new(), | |||||
| } | |||||
| } | |||||
| fn is_full(&self) -> bool { | |||||
| false | |||||
| } | |||||
| } | |||||
| /* VecFolder */ | |||||
| pub struct VecFolder<T> { | |||||
| vec: Option<Vec<T>>, | |||||
| items: Vec<T>, | |||||
| } | |||||
| impl<T> Folder<T> for VecFolder<T> { | |||||
| type Result = VecExtendResult<T>; | |||||
| fn consume(mut self, item: T) -> Self { | |||||
| self.items.push(item); | |||||
| self | |||||
| } | |||||
| fn consume_iter<X>(mut self, iter: X) -> Self | |||||
| where | |||||
| X: IntoIterator<Item = T>, | |||||
| { | |||||
| self.items.extend(iter); | |||||
| self | |||||
| } | |||||
| fn complete(self) -> Self::Result { | |||||
| let mut items = LinkedList::new(); | |||||
| items.push_back(self.items); | |||||
| VecExtendResult { | |||||
| vec: self.vec, | |||||
| items, | |||||
| } | |||||
| } | |||||
| fn is_full(&self) -> bool { | |||||
| false | |||||
| } | |||||
| } | |||||
| /* VecReducer */ | |||||
| pub struct VecReducer; | |||||
| impl<T> Reducer<VecExtendResult<T>> for VecReducer { | |||||
| fn reduce(self, left: VecExtendResult<T>, mut right: VecExtendResult<T>) -> VecExtendResult<T> { | |||||
| let mut items = left.items; | |||||
| items.append(&mut right.items); | |||||
| let vec = left.vec.or(right.vec); | |||||
| VecExtendResult { vec, items } | |||||
| } | |||||
| } | |||||
| /* VecExtendResult */ | |||||
| pub struct VecExtendResult<T> { | |||||
| vec: Option<Vec<T>>, | |||||
| items: LinkedList<Vec<T>>, | |||||
| } | |||||