diff --git a/.gitignore b/.gitignore index 274bf181a..ad2e819f1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ target *~ TAGS *.bk -.idea \ No newline at end of file +.idea +.devcontainer diff --git a/src/array.rs b/src/array.rs index 32a5fdd2f..a1078c009 100644 --- a/src/array.rs +++ b/src/array.rs @@ -18,6 +18,10 @@ impl<'data, T: Sync + 'data, const N: usize> IntoParallelIterator for &'data [T; fn into_par_iter(self) -> Self::Iter { <&[T]>::into_par_iter(self) } + + fn const_length() -> Option { + Some(N) + } } impl<'data, T: Send + 'data, const N: usize> IntoParallelIterator for &'data mut [T; N] { @@ -27,6 +31,10 @@ impl<'data, T: Send + 'data, const N: usize> IntoParallelIterator for &'data mut fn into_par_iter(self) -> Self::Iter { <&mut [T]>::into_par_iter(self) } + + fn const_length() -> Option { + Some(N) + } } impl IntoParallelIterator for [T; N] { @@ -36,6 +44,10 @@ impl IntoParallelIterator for [T; N] { fn into_par_iter(self) -> Self::Iter { IntoIter { array: self } } + + fn const_length() -> Option { + Some(N) + } } /// Parallel iterator that moves out of an array. @@ -57,6 +69,10 @@ impl ParallelIterator for IntoIter { fn opt_len(&self) -> Option { Some(N) } + + fn const_length() -> Option { + Some(N) + } } impl IndexedParallelIterator for IntoIter { diff --git a/src/iter/blocks.rs b/src/iter/blocks.rs index 1691e8b15..a0af2a97f 100644 --- a/src/iter/blocks.rs +++ b/src/iter/blocks.rs @@ -9,7 +9,7 @@ struct BlocksCallback { impl ProducerCallback for BlocksCallback where - C: UnindexedConsumer, + C: Consumer, S: Iterator, { type Output = C::Result; @@ -20,7 +20,7 @@ where // we need a local variable for the accumulated results // we call the reducer's identity by splitting at 0 - let (left_consumer, right_consumer, _) = consumer.split_at(0); + let (left_consumer, right_consumer, mut reducer) = consumer.split_at(0); let mut leftmost_res = left_consumer.into_folder().complete(); consumer = right_consumer; @@ -36,13 +36,14 @@ where producer = right_producer; // split the consumer - let (left_consumer, right_consumer, _) = consumer.split_at(capped_size); + let (left_consumer, right_consumer, next_reducer) = consumer.split_at(capped_size); consumer = right_consumer; - leftmost_res = consumer.to_reducer().reduce( + leftmost_res = reducer.reduce( leftmost_res, bridge_producer_consumer(capped_size, left_producer, left_consumer), ); + reducer = next_reducer; } leftmost_res } @@ -85,6 +86,10 @@ where }; self.base.with_producer(callback) } + + fn opt_len(&self) -> Option { + self.base.opt_len() + } } fn exponential_size(size: &usize) -> Option { @@ -128,4 +133,8 @@ where }; self.base.with_producer(callback) } + + fn opt_len(&self) -> Option { + self.base.opt_len() + } } diff --git a/src/iter/chain.rs b/src/iter/chain.rs index 25ee788c1..84f9aa1e2 100644 --- a/src/iter/chain.rs +++ b/src/iter/chain.rs @@ -61,6 +61,10 @@ where fn opt_len(&self) -> Option { self.a.opt_len()?.checked_add(self.b.opt_len()?) } + + fn const_length() -> Option { + A::const_length()?.checked_add(B::const_length()?) + } } impl IndexedParallelIterator for Chain diff --git a/src/iter/cloned.rs b/src/iter/cloned.rs index 8d5f420ef..e7478bdef 100644 --- a/src/iter/cloned.rs +++ b/src/iter/cloned.rs @@ -43,6 +43,10 @@ where fn opt_len(&self) -> Option { self.base.opt_len() } + + fn const_length() -> Option { + I::const_length() + } } impl<'a, T, I> IndexedParallelIterator for Cloned diff --git a/src/iter/copied.rs b/src/iter/copied.rs index 12c9c5b5b..131f439ae 100644 --- a/src/iter/copied.rs +++ b/src/iter/copied.rs @@ -43,6 +43,10 @@ where fn opt_len(&self) -> Option { self.base.opt_len() } + + fn const_length() -> Option { + I::const_length() + } } impl<'a, T, I> IndexedParallelIterator for Copied diff --git a/src/iter/empty.rs b/src/iter/empty.rs index 85a2e5fd9..4bcdbc31e 100644 --- a/src/iter/empty.rs +++ b/src/iter/empty.rs @@ -57,6 +57,10 @@ impl ParallelIterator for Empty { fn opt_len(&self) -> Option { Some(0) } + + fn const_length() -> Option { + Some(0) + } } impl IndexedParallelIterator for Empty { diff --git a/src/iter/enumerate.rs b/src/iter/enumerate.rs index 9d58b6ca0..8aa6b0962 100644 --- a/src/iter/enumerate.rs +++ b/src/iter/enumerate.rs @@ -40,6 +40,10 @@ where fn opt_len(&self) -> Option { Some(self.len()) } + + fn const_length() -> Option { + I::const_length() + } } impl IndexedParallelIterator for Enumerate diff --git a/src/iter/flat_map.rs b/src/iter/flat_map.rs index f264e1e14..e89015682 100644 --- a/src/iter/flat_map.rs +++ b/src/iter/flat_map.rs @@ -43,6 +43,20 @@ where let consumer = FlatMapConsumer::new(consumer, &self.map_op); self.base.drive_unindexed(consumer) } + + fn opt_len(&self) -> Option { + let base_len = self.base.opt_len()?; + let sub_len = PI::const_length()?; + + base_len.checked_mul(sub_len) + } + + fn const_length() -> Option { + let base_len = I::const_length()?; + let sub_len = PI::const_length()?; + + base_len.checked_mul(sub_len) + } } /// //////////////////////////////////////////////////////////////////////// @@ -70,7 +84,14 @@ where type Result = C::Result; fn split_at(self, index: usize) -> (Self, Self, C::Reducer) { - let (left, right, reducer) = self.base.split_at(index); + // FIXME: I have NO Idea if this is correct, it almost definately is NOT + // But the tests pass, and I no longer panic so that is a start + let (left, right, reducer) = if let Some(inner_len) = U::const_length() { + self.base.split_at(index * inner_len) + } else { + self.base.split_at(index) + }; + ( FlatMapConsumer::new(left, self.map_op), FlatMapConsumer::new(right, self.map_op), @@ -123,19 +144,27 @@ where fn consume(self, item: T) -> Self { let map_op = self.map_op; let par_iter = map_op(item).into_par_iter(); - let consumer = self.base.split_off_left(); + // TODO: This is a hack, to fake specialisation until it is in Rust proper + let (consumer, rest, reducer) = if let Some(inner_len) = U::const_length() { + // We can to use split_at + self.base.split_at(inner_len) + } else { + // Use the normal Unindexed version + let reducer = self.base.to_reducer(); + (self.base.split_off_left(), self.base, reducer) + }; + let result = par_iter.drive_unindexed(consumer); let previous = match self.previous { None => Some(result), Some(previous) => { - let reducer = self.base.to_reducer(); Some(reducer.reduce(previous, result)) } }; FlatMapFolder { - base: self.base, + base: rest, map_op, previous, } diff --git a/src/iter/flatten.rs b/src/iter/flatten.rs index 29d88f99e..6a7d75ced 100644 --- a/src/iter/flatten.rs +++ b/src/iter/flatten.rs @@ -37,6 +37,13 @@ where let consumer = FlattenConsumer::new(consumer); self.base.drive_unindexed(consumer) } + + fn const_length() -> Option { + let base_len = I::const_length()?; + let sub_len = I::Item::const_length()?; + + base_len.checked_mul(sub_len) + } } /// //////////////////////////////////////////////////////////////////////// diff --git a/src/iter/len.rs b/src/iter/len.rs index ba3cca8ba..0e11bd65f 100644 --- a/src/iter/len.rs +++ b/src/iter/len.rs @@ -39,6 +39,10 @@ where fn opt_len(&self) -> Option { Some(self.len()) } + + fn const_length() -> Option { + I::const_length() + } } impl IndexedParallelIterator for MinLen diff --git a/src/iter/map.rs b/src/iter/map.rs index da14d4082..7743e240c 100644 --- a/src/iter/map.rs +++ b/src/iter/map.rs @@ -52,6 +52,10 @@ where fn opt_len(&self) -> Option { self.base.opt_len() } + + fn const_length() -> Option { + I::const_length() + } } impl IndexedParallelIterator for Map diff --git a/src/iter/map_with.rs b/src/iter/map_with.rs index 10b1b4cac..6168e5b98 100644 --- a/src/iter/map_with.rs +++ b/src/iter/map_with.rs @@ -56,6 +56,10 @@ where fn opt_len(&self) -> Option { self.base.opt_len() } + + fn const_length() -> Option { + I::const_length() + } } impl IndexedParallelIterator for MapWith diff --git a/src/iter/mod.rs b/src/iter/mod.rs index c7e3a0d99..f4d825ba0 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -248,6 +248,11 @@ pub trait IntoParallelIterator { /// /// [`zip`]: trait.IndexedParallelIterator.html#method.zip fn into_par_iter(self) -> Self::Iter; + + /// Provides the length of the produced Iterator if known + fn const_length() -> Option { + None + } } /// `IntoParallelRefIterator` implements the conversion to a @@ -2430,6 +2435,14 @@ pub trait ParallelIterator: Sized + Send { fn opt_len(&self) -> Option { None } + + /// Internal method used to define the behavior of this parallel + /// iterator. You should not need to call this directly. + /// + /// Returns the constant length of Iterators of this type, if known + fn const_length() -> Option where Self: Sized { + None + } } impl IntoParallelIterator for T { @@ -2439,6 +2452,10 @@ impl IntoParallelIterator for T { fn into_par_iter(self) -> T { self } + + fn const_length() -> Option { + ::const_length() + } } /// An iterator that supports "random access" to its data, meaning diff --git a/src/iter/once.rs b/src/iter/once.rs index 5140b6b9f..75d599a5e 100644 --- a/src/iter/once.rs +++ b/src/iter/once.rs @@ -44,6 +44,10 @@ impl ParallelIterator for Once { fn opt_len(&self) -> Option { Some(1) } + + fn const_length() -> Option { + Some(1) + } } impl IndexedParallelIterator for Once { diff --git a/src/iter/panic_fuse.rs b/src/iter/panic_fuse.rs index 7487230e5..296048b8e 100644 --- a/src/iter/panic_fuse.rs +++ b/src/iter/panic_fuse.rs @@ -67,6 +67,10 @@ where fn opt_len(&self) -> Option { self.base.opt_len() } + + fn const_length() -> Option { + I::const_length() + } } impl IndexedParallelIterator for PanicFuse diff --git a/src/iter/rev.rs b/src/iter/rev.rs index a4c3b7c2e..190422939 100644 --- a/src/iter/rev.rs +++ b/src/iter/rev.rs @@ -39,6 +39,10 @@ where fn opt_len(&self) -> Option { Some(self.len()) } + + fn const_length() -> Option { + I::const_length() + } } impl IndexedParallelIterator for Rev diff --git a/src/iter/zip.rs b/src/iter/zip.rs index 74fc9774a..8c686be98 100644 --- a/src/iter/zip.rs +++ b/src/iter/zip.rs @@ -43,6 +43,10 @@ where fn opt_len(&self) -> Option { Some(self.len()) } + + fn const_length() -> Option { + Ord::min(A::const_length(), B::const_length()) + } } impl IndexedParallelIterator for Zip diff --git a/src/par_either.rs b/src/par_either.rs index a19ce5399..e98b324c3 100644 --- a/src/par_either.rs +++ b/src/par_either.rs @@ -23,6 +23,17 @@ where fn opt_len(&self) -> Option { self.as_ref().either(L::opt_len, R::opt_len) } + + fn const_length() -> Option { + let left_len = L::const_length()?; + let right_len = R::const_length()?; + + if left_len == right_len { + Some(left_len) + } else { + None + } + } } impl IndexedParallelIterator for Either