19
19
use iter:: * ;
20
20
use iter:: plumbing:: * ;
21
21
use std:: ops:: Range ;
22
+ use std:: usize;
22
23
23
24
/// Parallel iterator over a range, implemented for all integer types.
24
25
///
@@ -127,11 +128,15 @@ macro_rules! indexed_range_impl {
127
128
}
128
129
}
129
130
131
+ trait UnindexedRangeLen < L > {
132
+ fn len ( & self ) -> L ;
133
+ }
134
+
130
135
macro_rules! unindexed_range_impl {
131
136
( $t: ty, $len_t: ty ) => {
132
- impl IterProducer <$t> {
137
+ impl UnindexedRangeLen <$len_t> for Range <$t> {
133
138
fn len( & self ) -> $len_t {
134
- let Range { start, end } = self . range ;
139
+ let & Range { start, end } = self ;
135
140
if end > start {
136
141
end. wrapping_sub( start) as $len_t
137
142
} else {
@@ -146,15 +151,31 @@ macro_rules! unindexed_range_impl {
146
151
fn drive_unindexed<C >( self , consumer: C ) -> C :: Result
147
152
where C : UnindexedConsumer <Self :: Item >
148
153
{
149
- bridge_unindexed( IterProducer { range: self . range } , consumer)
154
+ if let Some ( len) = self . opt_len( ) {
155
+ // Drive this in indexed mode for better `collect`.
156
+ ( 0 ..len) . into_par_iter( )
157
+ . map( |i| self . range. start. wrapping_add( i as $t) )
158
+ . drive( consumer)
159
+ } else {
160
+ bridge_unindexed( IterProducer { range: self . range } , consumer)
161
+ }
162
+ }
163
+
164
+ fn opt_len( & self ) -> Option <usize > {
165
+ let len = self . range. len( ) ;
166
+ if len <= usize :: MAX as $len_t {
167
+ Some ( len as usize )
168
+ } else {
169
+ None
170
+ }
150
171
}
151
172
}
152
173
153
174
impl UnindexedProducer for IterProducer <$t> {
154
175
type Item = $t;
155
176
156
177
fn split( mut self ) -> ( Self , Option <Self >) {
157
- let index = self . len( ) / 2 ;
178
+ let index = self . range . len( ) / 2 ;
158
179
if index > 0 {
159
180
let mid = self . range. start. wrapping_add( index as $t) ;
160
181
let right = mid .. self . range. end;
@@ -191,7 +212,7 @@ unindexed_range_impl!{i64, u64}
191
212
#[ cfg( has_i128) ] unindexed_range_impl ! { i128 , u128 }
192
213
193
214
#[ test]
194
- pub fn check_range_split_at_overflow ( ) {
215
+ fn check_range_split_at_overflow ( ) {
195
216
// Note, this split index overflows i8!
196
217
let producer = IterProducer { range : -100i8 ..100 } ;
197
218
let ( left, right) = producer. split_at ( 150 ) ;
@@ -202,10 +223,54 @@ pub fn check_range_split_at_overflow() {
202
223
203
224
#[ cfg( has_i128) ]
204
225
#[ test]
205
- pub fn test_i128_len_doesnt_overflow ( ) {
226
+ fn test_i128_len_doesnt_overflow ( ) {
227
+ use std:: { i128, u128} ;
228
+
206
229
// Using parse because some versions of rust don't allow long literals
207
- let octillion = "1000000000000000000000000000" . parse :: < i128 > ( ) . unwrap ( ) ;
230
+ let octillion: i128 = "1000000000000000000000000000" . parse ( ) . unwrap ( ) ;
208
231
let producer = IterProducer { range : 0 ..octillion } ;
209
232
210
- assert_eq ! ( octillion as u128 , producer. len( ) ) ;
233
+ assert_eq ! ( octillion as u128 , producer. range. len( ) ) ;
234
+ assert_eq ! ( octillion as u128 , ( 0 ..octillion) . len( ) ) ;
235
+ assert_eq ! ( 2 * octillion as u128 , ( -octillion..octillion) . len( ) ) ;
236
+
237
+ assert_eq ! ( u128 :: MAX , ( i128 :: MIN ..i128 :: MAX ) . len( ) ) ;
238
+
239
+ }
240
+
241
+ #[ test]
242
+ fn test_u64_opt_len ( ) {
243
+ use std:: { u64, usize} ;
244
+ assert_eq ! ( Some ( 100 ) , ( 0 ..100u64 ) . into_par_iter( ) . opt_len( ) ) ;
245
+ assert_eq ! ( Some ( usize :: MAX ) , ( 0 ..usize :: MAX as u64 ) . into_par_iter( ) . opt_len( ) ) ;
246
+ if ( usize:: MAX as u64 ) < u64:: MAX {
247
+ assert_eq ! ( None , ( 0 ..1 + usize :: MAX as u64 ) . into_par_iter( ) . opt_len( ) ) ;
248
+ assert_eq ! ( None , ( 0 ..u64 :: MAX ) . into_par_iter( ) . opt_len( ) ) ;
249
+ }
250
+ }
251
+
252
+ #[ cfg( has_i128) ]
253
+ #[ test]
254
+ fn test_u128_opt_len ( ) {
255
+ use std:: { u128, usize} ;
256
+ assert_eq ! ( Some ( 100 ) , ( 0 ..100u128 ) . into_par_iter( ) . opt_len( ) ) ;
257
+ assert_eq ! ( Some ( usize :: MAX ) , ( 0 ..usize :: MAX as u128 ) . into_par_iter( ) . opt_len( ) ) ;
258
+ assert_eq ! ( None , ( 0 ..1 + usize :: MAX as u128 ) . into_par_iter( ) . opt_len( ) ) ;
259
+ assert_eq ! ( None , ( 0 ..u128 :: MAX ) . into_par_iter( ) . opt_len( ) ) ;
260
+ }
261
+
262
+ // `usize as i64` can overflow, so make sure to wrap it appropriately
263
+ // when using the `opt_len` "indexed" mode.
264
+ #[ test]
265
+ #[ cfg( target_pointer_width = "64" ) ]
266
+ fn test_usize_i64_overflow ( ) {
267
+ use std:: i64;
268
+ use ThreadPoolBuilder ;
269
+
270
+ let iter = ( -2 ..i64:: MAX ) . into_par_iter ( ) ;
271
+ assert_eq ! ( iter. opt_len( ) , Some ( i64 :: MAX as usize + 2 ) ) ;
272
+
273
+ // always run with multiple threads to split into, or this will take forever...
274
+ let pool = ThreadPoolBuilder :: new ( ) . num_threads ( 8 ) . build ( ) . unwrap ( ) ;
275
+ pool. install ( || assert_eq ! ( iter. find_last( |_| true ) , Some ( i64 :: MAX - 1 ) ) ) ;
211
276
}
0 commit comments