| @@ -1,30 +0,0 @@ | |||||
| use crate::{ | |||||
| Consumer, DefaultExecutor, Executor, IndexedConsumer, IndexedExecutor, IndexedParallelIterator, | |||||
| ParallelIterator, | |||||
| }; | |||||
| pub trait Collector: Sized { | |||||
| type Iterator: ParallelIterator; | |||||
| type Consumer: Consumer<<Self::Iterator as ParallelIterator>::Item>; | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | |||||
| where | |||||
| E: Executor<Self::Iterator, Self::Consumer>; | |||||
| fn exec(self) -> <DefaultExecutor as Executor<Self::Iterator, Self::Consumer>>::Result { | |||||
| self.exec_with(DefaultExecutor::default()) | |||||
| } | |||||
| } | |||||
| pub trait IndexedCollector: Sized { | |||||
| type Iterator: IndexedParallelIterator; | |||||
| type Consumer: IndexedConsumer<<Self::Iterator as ParallelIterator>::Item>; | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | |||||
| where | |||||
| E: IndexedExecutor<Self::Iterator, Self::Consumer>; | |||||
| fn exec(self) -> <DefaultExecutor as Executor<Self::Iterator, Self::Consumer>>::Result { | |||||
| self.exec_with(DefaultExecutor::default()) | |||||
| } | |||||
| } | |||||
| @@ -14,9 +14,9 @@ use super::{Folder, Reducer}; | |||||
| /// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold | /// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold | ||||
| /// [`Folder`]: trait.Folder.html | /// [`Folder`]: trait.Folder.html | ||||
| /// [`Producer`]: trait.Producer.html | /// [`Producer`]: trait.Producer.html | ||||
| pub trait Consumer<Item>: Send + Sized { | |||||
| pub trait Consumer<I>: Send + Sized { | |||||
| /// The type of folder that this consumer can be converted into. | /// The type of folder that this consumer can be converted into. | ||||
| type Folder: Folder<Item, Result = Self::Result>; | |||||
| type Folder: Folder<I, Result = Self::Result>; | |||||
| /// The type of reducer that is produced if this consumer is split. | /// The type of reducer that is produced if this consumer is split. | ||||
| type Reducer: Reducer<Self::Result>; | type Reducer: Reducer<Self::Result>; | ||||
| @@ -47,7 +47,7 @@ pub trait Consumer<Item>: Send + Sized { | |||||
| /// A stateless consumer can be freely copied. These consumers can be | /// A stateless consumer can be freely copied. These consumers can be | ||||
| /// used like regular consumers, but they also support a | /// used like regular consumers, but they also support a | ||||
| /// `split_at` method that does take an index to split. | /// `split_at` method that does take an index to split. | ||||
| pub trait IndexedConsumer<Item>: Consumer<Item> { | |||||
| pub trait IndexedConsumer<I>: Consumer<I> { | |||||
| /// Divide the consumer into two consumers, one processing items | /// Divide the consumer into two consumers, one processing items | ||||
| /// `0..index` and one processing items from `index..`. Also | /// `0..index` and one processing items from `index..`. Also | ||||
| /// produces a reducer that can be used to reduce the results at | /// produces a reducer that can be used to reduce the results at | ||||
| @@ -0,0 +1,11 @@ | |||||
| use crate::{Executor, DefaultExecutor}; | |||||
| pub trait Driver<D>: Sized | |||||
| where D: Send, | |||||
| { | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | |||||
| where E: Executor<D>; | |||||
| fn exec(self) -> <DefaultExecutor as Executor<D>>::Result | |||||
| { self.exec_with(DefaultExecutor::default()) } | |||||
| } | |||||
| @@ -1,21 +1,69 @@ | |||||
| use super::{Consumer, IndexedConsumer, IndexedParallelIterator, ParallelIterator}; | |||||
| use super::{ | |||||
| Consumer, IndexedConsumer, Producer, IndexedProducer, Reducer, ProducerCallback, IndexedProducerCallback, | |||||
| }; | |||||
| pub trait Executor<I, C> | |||||
| pub trait Executor<D> | |||||
| where D: Send, | |||||
| { | |||||
| type Result: Send; | |||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||||
| where | |||||
| P: Producer, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>; | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||||
| where | |||||
| P: IndexedProducer, | |||||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>; | |||||
| } | |||||
| pub struct ExecutorCallback<E, C> { | |||||
| executor: E, | |||||
| consumer: C, | |||||
| } | |||||
| impl<E, C> ExecutorCallback<E, C> { | |||||
| pub fn new(executor: E, consumer: C) -> Self { | |||||
| Self { | |||||
| executor, | |||||
| consumer, | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<E, D, C, I, R> ProducerCallback<I> for ExecutorCallback<E, C> | |||||
| where | where | ||||
| I: ParallelIterator, | |||||
| C: Consumer<I::Item>, | |||||
| E: Executor<D>, | |||||
| D: Send, | |||||
| C: Consumer<I, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| { | { | ||||
| type Result; | |||||
| type Output = E::Result; | |||||
| fn exec(self, iterator: I, consumer: C) -> Self::Result; | |||||
| fn callback<P>(self, producer: P) -> Self::Output | |||||
| where | |||||
| P: Producer<Item = I> | |||||
| { | |||||
| self.executor.exec(producer, self.consumer) | |||||
| } | |||||
| } | } | ||||
| pub trait IndexedExecutor<I, C> | |||||
| impl<E, D, C, I, R> IndexedProducerCallback<I> for ExecutorCallback<E, C> | |||||
| where | where | ||||
| I: IndexedParallelIterator, | |||||
| C: IndexedConsumer<I::Item>, | |||||
| E: Executor<D>, | |||||
| D: Send, | |||||
| C: IndexedConsumer<I, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| { | { | ||||
| type Result; | |||||
| type Output = E::Result; | |||||
| fn exec_indexed(self, iterator: I, consumer: C) -> Self::Result; | |||||
| fn callback<P>(self, producer: P) -> Self::Output | |||||
| where | |||||
| P: IndexedProducer<Item = I> | |||||
| { | |||||
| self.executor.exec_indexed(producer, self.consumer) | |||||
| } | |||||
| } | } | ||||
| @@ -4,12 +4,12 @@ | |||||
| /// be converted (using `complete`) into a final value. | /// be converted (using `complete`) into a final value. | ||||
| /// | /// | ||||
| /// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold | /// [fold]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold | ||||
| pub trait Folder<Item>: Sized { | |||||
| pub trait Folder<I>: Sized { | |||||
| /// The type of result that will ultimately be produced by the folder. | /// The type of result that will ultimately be produced by the folder. | ||||
| type Result; | type Result; | ||||
| /// Consume next item and return new sequential state. | /// Consume next item and return new sequential state. | ||||
| fn consume(self, item: Item) -> Self; | |||||
| fn consume(self, item: I) -> Self; | |||||
| /// Consume items from the iterator until full, and return new sequential state. | /// Consume items from the iterator until full, and return new sequential state. | ||||
| /// | /// | ||||
| @@ -19,16 +19,18 @@ pub trait Folder<Item>: Sized { | |||||
| /// | /// | ||||
| /// The main reason to override it is if you can provide a more | /// The main reason to override it is if you can provide a more | ||||
| /// specialized, efficient implementation. | /// specialized, efficient implementation. | ||||
| fn consume_iter<I>(mut self, iter: I) -> Self | |||||
| fn consume_iter<X>(mut self, iter: X) -> Self | |||||
| where | where | ||||
| I: IntoIterator<Item = Item>, | |||||
| X: IntoIterator<Item = I>, | |||||
| { | { | ||||
| for item in iter { | for item in iter { | ||||
| self = self.consume(item); | self = self.consume(item); | ||||
| if self.is_full() { | if self.is_full() { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| self | self | ||||
| } | } | ||||
| @@ -1,6 +1,6 @@ | |||||
| use super::{Consumer, Executor, IndexedConsumer, IndexedProducerCallback, ProducerCallback}; | |||||
| use super::{Consumer, IndexedConsumer, IndexedProducerCallback, ProducerCallback, Executor, Reducer}; | |||||
| use crate::inner::for_each::ForEach; | |||||
| use crate::inner::{for_each::ForEach, map::Map}; | |||||
| /// Parallel version of the standard iterator trait. | /// Parallel version of the standard iterator trait. | ||||
| /// | /// | ||||
| @@ -36,10 +36,12 @@ pub trait ParallelIterator: Sized + Send { | |||||
| /// iterators. | /// iterators. | ||||
| /// | /// | ||||
| /// [README]: README.md | /// [README]: README.md | ||||
| fn drive<E, C>(self, executor: E, consumer: C) -> E::Result | |||||
| fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||||
| where | where | ||||
| E: Executor<Self, C>, | |||||
| C: Consumer<Self::Item>; | |||||
| E: Executor<D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R>, | |||||
| D: Send, | |||||
| R: Reducer<D>; | |||||
| /// Internal method used to define the behavior of this parallel | /// Internal method used to define the behavior of this parallel | ||||
| /// iterator. You should not need to call this directly. | /// iterator. You should not need to call this directly. | ||||
| @@ -87,36 +89,42 @@ pub trait ParallelIterator: Sized + Send { | |||||
| /// | /// | ||||
| /// (0..100).into_par_iter().for_each(|x| println!("{:?}", x)); | /// (0..100).into_par_iter().for_each(|x| println!("{:?}", x)); | ||||
| /// ``` | /// ``` | ||||
| fn for_each<F>(self, operation: F) -> ForEach<Self, F> | |||||
| fn for_each<O>(self, operation: O) -> ForEach<Self, O> | |||||
| where | where | ||||
| F: Fn(Self::Item) + Sync + Send, | |||||
| O: Fn(Self::Item), | |||||
| { | { | ||||
| ForEach::new(self, operation) | ForEach::new(self, operation) | ||||
| } | } | ||||
| } | |||||
| /// An iterator that supports "random access" to its data, meaning | |||||
| /// that you can split it at arbitrary indices and draw data from | |||||
| /// those points. | |||||
| /// | |||||
| /// **Note:** Not implemented for `u64`, `i64`, `u128`, or `i128` ranges | |||||
| pub trait IndexedParallelIterator: ParallelIterator { | |||||
| /// Produces an exact count of how many items this iterator will | |||||
| /// produce, presuming no panic occurs. | |||||
| /// Applies `map_op` to each item of this iterator, producing a new | |||||
| /// iterator with the results. | |||||
| /// | /// | ||||
| /// # Examples | /// # Examples | ||||
| /// | /// | ||||
| /// ``` | /// ``` | ||||
| /// use asparit::*; | |||||
| /// use rayon::prelude::*; | |||||
| /// | /// | ||||
| /// let par_iter = (0..100).into_par_iter().zip(vec![0; 10]); | |||||
| /// assert_eq!(par_iter.len(), 10); | |||||
| /// let mut par_iter = (0..5).into_par_iter().map(|x| x * 2); | |||||
| /// | /// | ||||
| /// let vec: Vec<_> = par_iter.collect(); | |||||
| /// assert_eq!(vec.len(), 10); | |||||
| /// let doubles: Vec<_> = par_iter.collect(); | |||||
| /// | |||||
| /// assert_eq!(&doubles[..], &[0, 2, 4, 6, 8]); | |||||
| /// ``` | /// ``` | ||||
| fn len_hint(&self) -> usize; | |||||
| fn map<O, T>(self, operation: O) -> Map<Self, O> | |||||
| where | |||||
| O: Fn(Self::Item) -> T + Sync + Send, | |||||
| T: Send, | |||||
| { | |||||
| Map::new(self, operation) | |||||
| } | |||||
| } | |||||
| /// An iterator that supports "random access" to its data, meaning | |||||
| /// that you can split it at arbitrary indices and draw data from | |||||
| /// those points. | |||||
| /// | |||||
| /// **Note:** Not implemented for `u64`, `i64`, `u128`, or `i128` ranges | |||||
| pub trait IndexedParallelIterator: ParallelIterator { | |||||
| /// Internal method used to define the behavior of this parallel | /// Internal method used to define the behavior of this parallel | ||||
| /// iterator. You should not need to call this directly. | /// iterator. You should not need to call this directly. | ||||
| /// | /// | ||||
| @@ -131,9 +139,12 @@ pub trait IndexedParallelIterator: ParallelIterator { | |||||
| /// iterators. | /// iterators. | ||||
| /// | /// | ||||
| /// [README]: README.md | /// [README]: README.md | ||||
| fn drive_indexed<C>(self, consumer: C) -> C::Result | |||||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||||
| where | where | ||||
| C: IndexedConsumer<Self::Item>; | |||||
| E: Executor<D>, | |||||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R>, | |||||
| D: Send, | |||||
| R: Reducer<D>; | |||||
| /// Internal method used to define the behavior of this parallel | /// Internal method used to define the behavior of this parallel | ||||
| /// iterator. You should not need to call this directly. | /// iterator. You should not need to call this directly. | ||||
| @@ -153,4 +164,20 @@ pub trait IndexedParallelIterator: ParallelIterator { | |||||
| fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | ||||
| where | where | ||||
| CB: IndexedProducerCallback<Self::Item>; | CB: IndexedProducerCallback<Self::Item>; | ||||
| /// Produces an exact count of how many items this iterator will | |||||
| /// produce, presuming no panic occurs. | |||||
| /// | |||||
| /// # Examples | |||||
| /// | |||||
| /// ``` | |||||
| /// use asparit::*; | |||||
| /// | |||||
| /// let par_iter = (0..100).into_par_iter().zip(vec![0; 10]); | |||||
| /// assert_eq!(par_iter.len(), 10); | |||||
| /// | |||||
| /// let vec: Vec<_> = par_iter.collect(); | |||||
| /// assert_eq!(vec.len(), 10); | |||||
| /// ``` | |||||
| fn len_hint(&self) -> usize; | |||||
| } | } | ||||
| @@ -1,5 +1,5 @@ | |||||
| mod collector; | |||||
| mod consumer; | mod consumer; | ||||
| mod driver; | |||||
| mod executor; | mod executor; | ||||
| mod folder; | mod folder; | ||||
| mod into_iter; | mod into_iter; | ||||
| @@ -7,9 +7,9 @@ mod iterator; | |||||
| mod producer; | mod producer; | ||||
| mod reducer; | mod reducer; | ||||
| pub use collector::Collector; | |||||
| pub use consumer::{Consumer, IndexedConsumer}; | pub use consumer::{Consumer, IndexedConsumer}; | ||||
| pub use executor::{Executor, IndexedExecutor}; | |||||
| pub use driver::Driver; | |||||
| pub use executor::{Executor, ExecutorCallback}; | |||||
| pub use folder::Folder; | pub use folder::Folder; | ||||
| pub use into_iter::{IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator}; | pub use into_iter::{IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator}; | ||||
| pub use iterator::{IndexedParallelIterator, ParallelIterator}; | pub use iterator::{IndexedParallelIterator, ParallelIterator}; | ||||
| @@ -12,14 +12,26 @@ pub trait Producer: Send + Sized { | |||||
| /// The type of item returned by this producer. | /// The type of item returned by this producer. | ||||
| type Item; | type Item; | ||||
| /// The type of iterator we will become. | |||||
| type IntoIter: Iterator<Item = Self::Item>; | |||||
| /// Convert `self` into an iterator; at this point, no more parallel splits | |||||
| /// are possible. | |||||
| fn into_iter(self) -> Self::IntoIter; | |||||
| /// Split midway into a new producer if possible, otherwise return `None`. | /// Split midway into a new producer if possible, otherwise return `None`. | ||||
| fn split(self) -> (Self, Option<Self>); | fn split(self) -> (Self, Option<Self>); | ||||
| /// Iterate the producer, feeding each element to `folder`, and | /// Iterate the producer, feeding each element to `folder`, and | ||||
| /// stop when the folder is full (or all elements have been consumed). | /// stop when the folder is full (or all elements have been consumed). | ||||
| /// | |||||
| /// The provided implementation is sufficient for most iterables. | |||||
| fn fold_with<F>(self, folder: F) -> F | fn fold_with<F>(self, folder: F) -> F | ||||
| where | where | ||||
| F: Folder<Self::Item>; | |||||
| F: Folder<Self::Item>, | |||||
| { | |||||
| folder.consume_iter(self.into_iter()) | |||||
| } | |||||
| } | } | ||||
| /// A `Producer` is effectively a "splittable `IntoIterator`". That | /// A `Producer` is effectively a "splittable `IntoIterator`". That | ||||
| @@ -107,7 +119,7 @@ pub trait IndexedProducer: Send + Sized { | |||||
| /// | /// | ||||
| /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | ||||
| /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | ||||
| pub trait ProducerCallback<T> { | |||||
| pub trait ProducerCallback<I> { | |||||
| /// The type of value returned by this callback. Analogous to | /// The type of value returned by this callback. Analogous to | ||||
| /// [`Output` from the `FnOnce` trait][Output]. | /// [`Output` from the `FnOnce` trait][Output]. | ||||
| /// | /// | ||||
| @@ -119,7 +131,7 @@ pub trait ProducerCallback<T> { | |||||
| /// `P`, and hence implementors must be defined for any producer. | /// `P`, and hence implementors must be defined for any producer. | ||||
| fn callback<P>(self, producer: P) -> Self::Output | fn callback<P>(self, producer: P) -> Self::Output | ||||
| where | where | ||||
| P: Producer<Item = T>; | |||||
| P: Producer<Item = I>; | |||||
| } | } | ||||
| /// The `IndexedProducerCallback` trait is a kind of generic closure, | /// The `IndexedProducerCallback` trait is a kind of generic closure, | ||||
| @@ -128,7 +140,7 @@ pub trait ProducerCallback<T> { | |||||
| /// | /// | ||||
| /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | /// [r]: https://github.com/rayon-rs/rayon/blob/master/src/iter/plumbing/README.md#producer-callback | ||||
| /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | /// [FnOnce]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html | ||||
| pub trait IndexedProducerCallback<T> { | |||||
| pub trait IndexedProducerCallback<I> { | |||||
| /// The type of value returned by this callback. Analogous to | /// The type of value returned by this callback. Analogous to | ||||
| /// [`Output` from the `FnOnce` trait][Output]. | /// [`Output` from the `FnOnce` trait][Output]. | ||||
| /// | /// | ||||
| @@ -140,5 +152,5 @@ pub trait IndexedProducerCallback<T> { | |||||
| /// `P`, and hence implementors must be defined for any producer. | /// `P`, and hence implementors must be defined for any producer. | ||||
| fn callback<P>(self, producer: P) -> Self::Output | fn callback<P>(self, producer: P) -> Self::Output | ||||
| where | where | ||||
| P: IndexedProducer<Item = T>; | |||||
| P: IndexedProducer<Item = I>; | |||||
| } | } | ||||
| @@ -1,70 +1,39 @@ | |||||
| use crate::core::{ | use crate::core::{ | ||||
| Consumer, Executor, Folder, IndexedConsumer, IndexedExecutor, IndexedParallelIterator, | |||||
| IndexedProducer, IndexedProducerCallback, ParallelIterator, Producer, ProducerCallback, | |||||
| Consumer, Executor, Folder, IndexedConsumer, IndexedProducer, | |||||
| Producer, Reducer, | |||||
| }; | }; | ||||
| #[derive(Default)] | #[derive(Default)] | ||||
| pub struct Sequential; | pub struct Sequential; | ||||
| struct Callback<C> { | |||||
| consumer: C, | |||||
| } | |||||
| struct IndexedCallback<C> { | |||||
| consumer: C, | |||||
| } | |||||
| impl<I, C> Executor<I, C> for Sequential | |||||
| where | |||||
| I: ParallelIterator, | |||||
| C: Consumer<I::Item>, | |||||
| { | |||||
| type Result = C::Result; | |||||
| fn exec(self, iterator: I, consumer: C) -> Self::Result { | |||||
| iterator.with_producer(Callback { consumer }) | |||||
| } | |||||
| } | |||||
| impl<I, C> IndexedExecutor<I, C> for Sequential | |||||
| where | |||||
| I: IndexedParallelIterator, | |||||
| C: IndexedConsumer<I::Item>, | |||||
| impl<D> Executor<D> for Sequential | |||||
| where D: Send, | |||||
| { | { | ||||
| type Result = C::Result; | |||||
| type Result = D; | |||||
| fn exec_indexed(self, iterator: I, consumer: C) -> Self::Result { | |||||
| iterator.with_producer_indexed(IndexedCallback { consumer }) | |||||
| } | |||||
| } | |||||
| impl<C, T> ProducerCallback<T> for Callback<C> | |||||
| where | |||||
| C: Consumer<T>, | |||||
| { | |||||
| type Output = C::Result; | |||||
| fn callback<P>(self, producer: P) -> C::Result | |||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||||
| where | where | ||||
| P: Producer<Item = T>, | |||||
| P: Producer, | |||||
| C: Consumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| { | { | ||||
| if self.consumer.is_full() { | |||||
| self.consumer.into_folder().complete() | |||||
| if consumer.is_full() { | |||||
| consumer.into_folder().complete() | |||||
| } else { | } else { | ||||
| producer.fold_with(self.consumer.into_folder()).complete() | |||||
| producer.fold_with(consumer.into_folder()).complete() | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| impl<C, T> IndexedProducerCallback<T> for IndexedCallback<C> | |||||
| where | |||||
| C: IndexedConsumer<T>, | |||||
| { | |||||
| type Output = C::Result; | |||||
| fn callback<P>(self, _producer: P) -> C::Result | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||||
| where | where | ||||
| P: IndexedProducer<Item = T>, | |||||
| P: IndexedProducer, | |||||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| { | { | ||||
| self.consumer.into_folder().complete() | |||||
| if consumer.is_full() { | |||||
| consumer.into_folder().complete() | |||||
| } else { | |||||
| producer.fold_with(consumer.into_folder()).complete() | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,17 +1,15 @@ | |||||
| use crate::{ | |||||
| core::{Collector, Executor}, | |||||
| Consumer, Folder, ParallelIterator, | |||||
| }; | |||||
| use crate::{core::Driver, Consumer, Folder, ParallelIterator, Executor}; | |||||
| use super::noop::NoOpReducer; | use super::noop::NoOpReducer; | ||||
| pub struct ForEach<I, F> { | |||||
| iterator: I, | |||||
| operation: F, | |||||
| pub struct ForEach<X, O> { | |||||
| iterator: X, | |||||
| operation: O, | |||||
| } | } | ||||
| impl<I, F> ForEach<I, F> { | |||||
| pub fn new(iterator: I, operation: F) -> Self { | |||||
| impl<X, O> ForEach<X, O> | |||||
| { | |||||
| pub fn new(iterator: X, operation: O) -> Self { | |||||
| Self { | Self { | ||||
| iterator, | iterator, | ||||
| operation, | operation, | ||||
| @@ -19,17 +17,13 @@ impl<I, F> ForEach<I, F> { | |||||
| } | } | ||||
| } | } | ||||
| impl<I, F> Collector for ForEach<I, F> | |||||
| impl<X, O> Driver<()> for ForEach<X, O> | |||||
| where | where | ||||
| I: ParallelIterator, | |||||
| F: Fn(I::Item) + Sync + Send + Copy, | |||||
| X: ParallelIterator, | |||||
| O: Fn(X::Item) + Clone + Send, | |||||
| { | { | ||||
| type Iterator = I; | |||||
| type Consumer = ForEachConsumer<F>; | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | fn exec_with<E>(self, executor: E) -> E::Result | ||||
| where | |||||
| E: Executor<Self::Iterator, Self::Consumer>, | |||||
| where E: Executor<()> | |||||
| { | { | ||||
| let iterator = self.iterator; | let iterator = self.iterator; | ||||
| let operation = self.operation; | let operation = self.operation; | ||||
| @@ -40,22 +34,22 @@ where | |||||
| } | } | ||||
| } | } | ||||
| pub struct ForEachConsumer<F> { | |||||
| operation: F, | |||||
| pub struct ForEachConsumer<O> { | |||||
| operation: O, | |||||
| } | } | ||||
| impl<F, T> Consumer<T> for ForEachConsumer<F> | |||||
| impl<O, I> Consumer<I> for ForEachConsumer<O> | |||||
| where | where | ||||
| F: Fn(T) + Sync + Send + Copy, | |||||
| O: Fn(I) + Clone + Send, | |||||
| { | { | ||||
| type Folder = ForEachConsumer<F>; | |||||
| type Folder = ForEachConsumer<O>; | |||||
| type Reducer = NoOpReducer; | type Reducer = NoOpReducer; | ||||
| type Result = (); | type Result = (); | ||||
| fn split_off_left(&self) -> (Self, NoOpReducer) { | fn split_off_left(&self) -> (Self, NoOpReducer) { | ||||
| ( | ( | ||||
| ForEachConsumer { | ForEachConsumer { | ||||
| operation: self.operation, | |||||
| operation: self.operation.clone(), | |||||
| }, | }, | ||||
| NoOpReducer, | NoOpReducer, | ||||
| ) | ) | ||||
| @@ -66,23 +60,23 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<F, T> Folder<T> for ForEachConsumer<F> | |||||
| impl<O, I> Folder<I> for ForEachConsumer<O> | |||||
| where | where | ||||
| F: Fn(T) + Sync + Send + Copy, | |||||
| O: Fn(I), | |||||
| { | { | ||||
| type Result = (); | type Result = (); | ||||
| fn consume(self, item: T) -> Self { | |||||
| fn consume(self, item: I) -> Self { | |||||
| (self.operation)(item); | (self.operation)(item); | ||||
| self | self | ||||
| } | } | ||||
| fn consume_iter<I>(self, iter: I) -> Self | |||||
| fn consume_iter<X>(self, iter: X) -> Self | |||||
| where | where | ||||
| I: IntoIterator<Item = T>, | |||||
| X: IntoIterator<Item = I>, | |||||
| { | { | ||||
| iter.into_iter().for_each(self.operation); | |||||
| iter.into_iter().for_each(&self.operation); | |||||
| self | self | ||||
| } | } | ||||
| @@ -97,11 +91,14 @@ mod tests { | |||||
| #[test] | #[test] | ||||
| fn test_for_each() { | fn test_for_each() { | ||||
| (0..10usize) | |||||
| let x = (0..10usize) | |||||
| .into_par_iter() | .into_par_iter() | ||||
| .for_each(&|j| { | |||||
| println!("{}", j); | |||||
| .map(Some) | |||||
| .for_each(|j| { | |||||
| println!("{:?}", j); | |||||
| }) | }) | ||||
| .exec(); | .exec(); | ||||
| dbg!(x); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,332 @@ | |||||
| use crate::{ | |||||
| Consumer, Folder, IndexedConsumer, IndexedParallelIterator, IndexedProducer, Reducer, | |||||
| IndexedProducerCallback, ParallelIterator, Producer, ProducerCallback, Executor, | |||||
| }; | |||||
| /* Map */ | |||||
| pub struct Map<X, O> { | |||||
| base: X, | |||||
| operation: O, | |||||
| } | |||||
| impl<X, O> Map<X, O> { | |||||
| pub fn new(base: X, operation: O) -> Self { | |||||
| Self { base, operation } | |||||
| } | |||||
| } | |||||
| impl<X, O, T> ParallelIterator for Map<X, O> | |||||
| where | |||||
| X: ParallelIterator, | |||||
| O: Fn(X::Item) -> T + Sync + Send + Copy, | |||||
| T: Send, | |||||
| { | |||||
| type Item = O::Output; | |||||
| fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||||
| where | |||||
| E: Executor<D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R>, | |||||
| D: Send, | |||||
| R: Reducer<D> | |||||
| { | |||||
| let consumer = MapConsumer::new(consumer, self.operation); | |||||
| self.base.drive(executor, consumer) | |||||
| } | |||||
| fn with_producer<CB>(self, callback: CB) -> CB::Output | |||||
| where | |||||
| CB: ProducerCallback<Self::Item>, | |||||
| { | |||||
| self.base.with_producer(MapCallback { | |||||
| callback, | |||||
| operation: self.operation, | |||||
| }) | |||||
| } | |||||
| fn len_hint_opt(&self) -> Option<usize> { | |||||
| self.base.len_hint_opt() | |||||
| } | |||||
| } | |||||
| impl<X, O, T> IndexedParallelIterator for Map<X, O> | |||||
| where | |||||
| X: IndexedParallelIterator, | |||||
| O: Fn(X::Item) -> T + Sync + Send + Copy, | |||||
| T: Send, | |||||
| { | |||||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||||
| where | |||||
| E: Executor<D>, | |||||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R>, | |||||
| D: Send, | |||||
| R: Reducer<D> | |||||
| { | |||||
| let consumer = MapConsumer::new(consumer, self.operation); | |||||
| self.base.drive_indexed(executor, consumer) | |||||
| } | |||||
| fn with_producer_indexed<CB>(self, callback: CB) -> CB::Output | |||||
| where | |||||
| CB: IndexedProducerCallback<Self::Item>, | |||||
| { | |||||
| self.base.with_producer_indexed(MapCallback { | |||||
| callback, | |||||
| operation: self.operation, | |||||
| }) | |||||
| } | |||||
| fn len_hint(&self) -> usize { | |||||
| self.base.len_hint() | |||||
| } | |||||
| } | |||||
| /* MapCallback */ | |||||
| struct MapCallback<CB, O> { | |||||
| callback: CB, | |||||
| operation: O, | |||||
| } | |||||
| impl<I, O, T, CB> ProducerCallback<I> for MapCallback<CB, O> | |||||
| where | |||||
| CB: ProducerCallback<T>, | |||||
| O: Fn(I) -> T + Sync + Send + Copy, | |||||
| T: Send, | |||||
| { | |||||
| type Output = CB::Output; | |||||
| fn callback<P>(self, base: P) -> CB::Output | |||||
| where | |||||
| P: Producer<Item = I>, | |||||
| { | |||||
| let producer = MapProducer { | |||||
| base, | |||||
| operation: self.operation, | |||||
| }; | |||||
| self.callback.callback(producer) | |||||
| } | |||||
| } | |||||
| impl<I, O, T, CB> IndexedProducerCallback<I> for MapCallback<CB, O> | |||||
| where | |||||
| CB: IndexedProducerCallback<T>, | |||||
| O: Fn(I) -> T + Sync + Send + Copy, | |||||
| T: Send, | |||||
| { | |||||
| type Output = CB::Output; | |||||
| fn callback<P>(self, base: P) -> CB::Output | |||||
| where | |||||
| P: IndexedProducer<Item = I>, | |||||
| { | |||||
| let producer = MapProducer { | |||||
| base, | |||||
| operation: self.operation, | |||||
| }; | |||||
| self.callback.callback(producer) | |||||
| } | |||||
| } | |||||
| /* MapProducer */ | |||||
| struct MapProducer<P, O> { | |||||
| base: P, | |||||
| operation: O, | |||||
| } | |||||
| impl<P, O, T> Producer for MapProducer<P, O> | |||||
| where | |||||
| P: Producer, | |||||
| O: Fn(P::Item) -> T + Sync + Send + Copy, | |||||
| T: Send, | |||||
| { | |||||
| type Item = O::Output; | |||||
| type IntoIter = std::iter::Map<P::IntoIter, O>; | |||||
| fn into_iter(self) -> Self::IntoIter { | |||||
| // self.base.into_iter().map(self.operation) | |||||
| unimplemented!() | |||||
| } | |||||
| fn split(self) -> (Self, Option<Self>) { | |||||
| let operation = self.operation; | |||||
| let (left, right) = self.base.split(); | |||||
| ( | |||||
| MapProducer { | |||||
| base: left, | |||||
| operation, | |||||
| }, | |||||
| right.map(|right| MapProducer { | |||||
| base: right, | |||||
| operation, | |||||
| }), | |||||
| ) | |||||
| } | |||||
| fn fold_with<G>(self, folder: G) -> G | |||||
| where | |||||
| G: Folder<Self::Item>, | |||||
| { | |||||
| let folder = MapFolder { | |||||
| base: folder, | |||||
| operation: self.operation, | |||||
| }; | |||||
| self.base.fold_with(folder).base | |||||
| } | |||||
| } | |||||
| impl<P, O, T> IndexedProducer for MapProducer<P, O> | |||||
| where | |||||
| P: IndexedProducer, | |||||
| O: Fn(P::Item) -> T + Sync + Send + Copy, | |||||
| T: Send, | |||||
| { | |||||
| type Item = O::Output; | |||||
| type IntoIter = std::iter::Map<P::IntoIter, O>; | |||||
| fn into_iter(self) -> Self::IntoIter { | |||||
| self.base.into_iter().map(self.operation) | |||||
| } | |||||
| fn min_len(&self) -> usize { | |||||
| self.base.min_len() | |||||
| } | |||||
| fn max_len(&self) -> usize { | |||||
| self.base.max_len() | |||||
| } | |||||
| fn split_at(self, index: usize) -> (Self, Self) { | |||||
| let operation = self.operation; | |||||
| let (left, right) = self.base.split_at(index); | |||||
| ( | |||||
| MapProducer { | |||||
| base: left, | |||||
| operation, | |||||
| }, | |||||
| MapProducer { | |||||
| base: right, | |||||
| operation, | |||||
| }, | |||||
| ) | |||||
| } | |||||
| fn fold_with<G>(self, folder: G) -> G | |||||
| where | |||||
| G: Folder<Self::Item>, | |||||
| { | |||||
| let folder = MapFolder { | |||||
| base: folder, | |||||
| operation: self.operation, | |||||
| }; | |||||
| self.base.fold_with(folder).base | |||||
| } | |||||
| } | |||||
| /* MapConsumer */ | |||||
| struct MapConsumer<C, O> { | |||||
| base: C, | |||||
| operation: O, | |||||
| } | |||||
| impl<C, O> MapConsumer<C, O> { | |||||
| fn new(base: C, operation: O) -> Self { | |||||
| Self { base, operation } | |||||
| } | |||||
| } | |||||
| impl<I, T, C, O> Consumer<I> for MapConsumer<C, O> | |||||
| where | |||||
| C: Consumer<O::Output>, | |||||
| O: Fn(I) -> T + Send + Sync + Copy, | |||||
| T: Send, | |||||
| { | |||||
| type Folder = MapFolder<C::Folder, O>; | |||||
| type Reducer = C::Reducer; | |||||
| type Result = C::Result; | |||||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||||
| let (left, reducer) = self.base.split_off_left(); | |||||
| (MapConsumer::new(left, self.operation), reducer) | |||||
| } | |||||
| fn into_folder(self) -> Self::Folder { | |||||
| MapFolder { | |||||
| base: self.base.into_folder(), | |||||
| operation: self.operation, | |||||
| } | |||||
| } | |||||
| fn is_full(&self) -> bool { | |||||
| self.base.is_full() | |||||
| } | |||||
| } | |||||
| impl<I, T, C, O> IndexedConsumer<I> for MapConsumer<C, O> | |||||
| where | |||||
| C: IndexedConsumer<O::Output>, | |||||
| O: Fn(I) -> T + Send + Sync + Copy, | |||||
| T: Send, | |||||
| { | |||||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||||
| let (left, right, reducer) = self.base.split_at(index); | |||||
| ( | |||||
| MapConsumer::new(left, self.operation), | |||||
| MapConsumer::new(right, self.operation), | |||||
| reducer, | |||||
| ) | |||||
| } | |||||
| } | |||||
| /* MapFolder */ | |||||
| struct MapFolder<F, O> { | |||||
| base: F, | |||||
| operation: O, | |||||
| } | |||||
| impl<I, T, F, O> Folder<I> for MapFolder<F, O> | |||||
| where | |||||
| F: Folder<O::Output>, | |||||
| O: Fn(I) -> T + Copy, | |||||
| { | |||||
| type Result = F::Result; | |||||
| fn consume(self, item: I) -> Self { | |||||
| let mapped_item = (self.operation)(item); | |||||
| MapFolder { | |||||
| base: self.base.consume(mapped_item), | |||||
| operation: self.operation, | |||||
| } | |||||
| } | |||||
| fn consume_iter<X>(mut self, iter: X) -> Self | |||||
| where | |||||
| X: IntoIterator<Item = I>, | |||||
| { | |||||
| self.base = self.base.consume_iter(iter.into_iter().map(self.operation)); | |||||
| self | |||||
| } | |||||
| fn complete(self) -> F::Result { | |||||
| self.base.complete() | |||||
| } | |||||
| fn is_full(&self) -> bool { | |||||
| self.base.is_full() | |||||
| } | |||||
| } | |||||
| @@ -1,2 +1,3 @@ | |||||
| pub mod for_each; | pub mod for_each; | ||||
| pub mod map; | |||||
| pub mod noop; | pub mod noop; | ||||
| @@ -4,8 +4,8 @@ mod inner; | |||||
| mod std; | mod std; | ||||
| pub use self::core::{ | pub use self::core::{ | ||||
| Consumer, Executor, Folder, IndexedConsumer, IndexedExecutor, IndexedParallelIterator, | |||||
| IndexedProducer, IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, | |||||
| ParallelIterator, Producer, ProducerCallback, Reducer, | |||||
| Consumer, Executor, Folder, IndexedConsumer, IndexedParallelIterator, IndexedProducer, | |||||
| IndexedProducerCallback, IntoParallelIterator, IntoParallelRefIterator, ExecutorCallback, | |||||
| IntoParallelRefMutIterator, ParallelIterator, Producer, ProducerCallback, Reducer, | |||||
| }; | }; | ||||
| pub use self::executor::{DefaultExecutor, SequentialExecutor}; | pub use self::executor::{DefaultExecutor, SequentialExecutor}; | ||||
| @@ -1,8 +1,6 @@ | |||||
| use std::ops::Range; | use std::ops::Range; | ||||
| use crate::{ | |||||
| Consumer, Executor, Folder, IntoParallelIterator, ParallelIterator, Producer, ProducerCallback, | |||||
| }; | |||||
| use crate::{Consumer, Folder, IntoParallelIterator, ParallelIterator, Producer, ProducerCallback, Executor, Reducer, ExecutorCallback}; | |||||
| pub struct Iter { | pub struct Iter { | ||||
| range: Range<usize>, | range: Range<usize>, | ||||
| @@ -24,12 +22,14 @@ impl IntoParallelIterator for Range<usize> { | |||||
| impl ParallelIterator for Iter { | impl ParallelIterator for Iter { | ||||
| type Item = usize; | type Item = usize; | ||||
| fn drive<E, C>(self, executor: E, consumer: C) -> E::Result | |||||
| fn drive<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||||
| where | where | ||||
| E: Executor<Self, C>, | |||||
| C: Consumer<Self::Item>, | |||||
| E: Executor<D>, | |||||
| C: Consumer<Self::Item, Result = D, Reducer = R>, | |||||
| D: Send, | |||||
| R: Reducer<D> | |||||
| { | { | ||||
| executor.exec(self, consumer) | |||||
| self.with_producer(ExecutorCallback::new(executor, consumer)) | |||||
| } | } | ||||
| fn len_hint_opt(&self) -> Option<usize> { | fn len_hint_opt(&self) -> Option<usize> { | ||||
| @@ -46,6 +46,11 @@ impl ParallelIterator for Iter { | |||||
| impl Producer for IterProducer { | impl Producer for IterProducer { | ||||
| type Item = usize; | type Item = usize; | ||||
| type IntoIter = Range<usize>; | |||||
| fn into_iter(self) -> Self::IntoIter { | |||||
| self.range | |||||
| } | |||||
| fn split(mut self) -> (Self, Option<Self>) { | fn split(mut self) -> (Self, Option<Self>) { | ||||
| let index = self.range.len() / 2; | let index = self.range.len() / 2; | ||||