Skip to content

Commit 7b7dcc5

Browse files
committed
Specialisations for Range::{last,nth,count}
1 parent 748f826 commit 7b7dcc5

File tree

3 files changed

+200
-126
lines changed

3 files changed

+200
-126
lines changed

src/libcore/iter/range.rs

Lines changed: 118 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,15 @@ use super::{FusedIterator, TrustedLen};
2121
#[unstable(feature = "step_trait",
2222
reason = "likely to be replaced by finer-grained traits",
2323
issue = "42168")]
24-
pub trait Step: PartialOrd + Sized {
24+
pub trait Step: PartialOrd + Sized + Clone {
2525
/// Steps `self` if possible.
26-
fn step(&self, by: &Self) -> Option<Self>;
26+
fn step(&self, by: usize) -> Option<Self>;
2727

2828
/// Returns the number of steps between two step objects. The count is
2929
/// inclusive of `start` and exclusive of `end`.
3030
///
31-
/// Returns `None` if it is not possible to calculate `steps_between`
32-
/// without overflow.
33-
fn steps_between(start: &Self, end: &Self, by: &Self) -> Option<usize>;
31+
/// Returns `None` if the resultant number of steps overflows usize.
32+
fn steps_between(start: &Self, end: &Self, by: usize) -> Option<usize>;
3433

3534
/// Same as `steps_between`, but with a `by` of 1
3635
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize>;
@@ -58,25 +57,25 @@ macro_rules! step_impl_unsigned {
5857
issue = "42168")]
5958
impl Step for $t {
6059
#[inline]
61-
fn step(&self, by: &$t) -> Option<$t> {
62-
(*self).checked_add(*by)
60+
fn step(&self, by: usize) -> Option<$t> {
61+
// If casting usize to Self fails, this means overflow happened.
62+
Self::cast(by).ok().and_then(|by| (*self).checked_add(by))
6363
}
64+
6465
#[inline]
6566
#[allow(trivial_numeric_casts)]
66-
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
67-
if *by == 0 { return None; }
68-
if *start < *end {
69-
// Note: We assume $t <= usize here
70-
let diff = (*end - *start) as usize;
71-
let by = *by as usize;
72-
if diff % by > 0 {
73-
Some(diff / by + 1)
67+
fn steps_between(start: &$t, end: &$t, by: usize) -> Option<usize> {
68+
if by == 0 { return None; }
69+
Self::cast(by).ok().and_then(|by| if *start < *end {
70+
let diff = *end - *start;
71+
usize::cast(if diff % by > 0 {
72+
diff / by + 1
7473
} else {
75-
Some(diff / by)
76-
}
74+
diff / by
75+
}).ok()
7776
} else {
7877
Some(0)
79-
}
78+
})
8079
}
8180

8281
#[inline]
@@ -106,100 +105,39 @@ macro_rules! step_impl_unsigned {
106105

107106
#[inline]
108107
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
109-
Self::steps_between(start, end, &1)
108+
Self::steps_between(start, end, 1)
110109
}
111110
}
112111
)*)
113112
}
114113
macro_rules! step_impl_signed {
115-
($($t:ty)*) => ($(
114+
($($t:ty: $s:ty,)*) => ($(
116115
#[unstable(feature = "step_trait",
117116
reason = "likely to be replaced by finer-grained traits",
118117
issue = "42168")]
119118
impl Step for $t {
120119
#[inline]
121-
fn step(&self, by: &$t) -> Option<$t> {
122-
(*self).checked_add(*by)
120+
fn step(&self, by: usize) -> Option<$t> {
121+
Self::cast(by).ok().and_then(|by| (*self).checked_add(by))
123122
}
123+
124124
#[inline]
125125
#[allow(trivial_numeric_casts)]
126-
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
127-
if *by == 0 { return None; }
128-
let diff: usize;
129-
let by_u: usize;
130-
if *by > 0 {
131-
if *start >= *end {
132-
return Some(0);
133-
}
134-
// Note: We assume $t <= isize here
135-
// Use .wrapping_sub and cast to usize to compute the
136-
// difference that may not fit inside the range of isize.
137-
diff = (*end as isize).wrapping_sub(*start as isize) as usize;
138-
by_u = *by as usize;
139-
} else {
140-
if *start <= *end {
141-
return Some(0);
142-
}
143-
diff = (*start as isize).wrapping_sub(*end as isize) as usize;
144-
by_u = (*by as isize).wrapping_mul(-1) as usize;
145-
}
146-
if diff % by_u > 0 {
147-
Some(diff / by_u + 1)
126+
fn steps_between(start: &$t, end: &$t, by: usize) -> Option<usize> {
127+
if by == 0 { return None; }
128+
<$s>::cast(by).ok().and_then(|by| if *start < *end {
129+
let diff = end.wrapping_sub(*start) as $s;
130+
usize::cast(if diff % by > 0 {
131+
diff / by + 1
132+
} else {
133+
diff / by
134+
}).ok()
148135
} else {
149-
Some(diff / by_u)
150-
}
151-
}
152-
153-
#[inline]
154-
fn is_negative(&self) -> bool {
155-
*self < 0
156-
}
157-
158-
#[inline]
159-
fn replace_one(&mut self) -> Self {
160-
mem::replace(self, 1)
161-
}
162-
163-
#[inline]
164-
fn replace_zero(&mut self) -> Self {
165-
mem::replace(self, 0)
166-
}
167-
168-
#[inline]
169-
fn add_one(&self) -> Self {
170-
Add::add(*self, 1)
171-
}
172-
173-
#[inline]
174-
fn sub_one(&self) -> Self {
175-
Sub::sub(*self, 1)
176-
}
177-
178-
#[inline]
179-
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
180-
Self::steps_between(start, end, &1)
181-
}
182-
}
183-
)*)
184-
}
185-
186-
macro_rules! step_impl_no_between {
187-
($($t:ty)*) => ($(
188-
#[unstable(feature = "step_trait",
189-
reason = "likely to be replaced by finer-grained traits",
190-
issue = "42168")]
191-
impl Step for $t {
192-
#[inline]
193-
fn step(&self, by: &$t) -> Option<$t> {
194-
(*self).checked_add(*by)
195-
}
196-
#[inline]
197-
fn steps_between(_a: &$t, _b: &$t, _by: &$t) -> Option<usize> {
198-
None
136+
Some(0)
137+
})
199138
}
200139

201140
#[inline]
202-
#[allow(unused_comparisons)]
203141
fn is_negative(&self) -> bool {
204142
*self < 0
205143
}
@@ -226,23 +164,14 @@ macro_rules! step_impl_no_between {
226164

227165
#[inline]
228166
fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
229-
Self::steps_between(start, end, &1)
167+
Self::steps_between(start, end, 1)
230168
}
231169
}
232170
)*)
233171
}
234172

235-
step_impl_unsigned!(usize u8 u16 u32);
236-
step_impl_signed!(isize i8 i16 i32);
237-
#[cfg(target_pointer_width = "64")]
238-
step_impl_unsigned!(u64);
239-
#[cfg(target_pointer_width = "64")]
240-
step_impl_signed!(i64);
241-
// If the target pointer width is not 64-bits, we
242-
// assume here that it is less than 64-bits.
243-
#[cfg(not(target_pointer_width = "64"))]
244-
step_impl_no_between!(u64 i64);
245-
step_impl_no_between!(u128 i128);
173+
step_impl_unsigned!(usize u8 u16 u32 u64 u128);
174+
step_impl_signed!(isize: usize, i8: u8, i16: u16, i32: u32, i64: u64, i128: u128,);
246175

247176
macro_rules! range_exact_iter_impl {
248177
($($t:ty)*) => ($(
@@ -300,6 +229,28 @@ impl<A: Step> Iterator for ops::Range<A> where
300229
None => (0, None)
301230
}
302231
}
232+
233+
#[inline]
234+
fn count(self) -> usize {
235+
if let Some(x) = Step::steps_between_by_one(&self.start, &self.end) {
236+
x
237+
} else {
238+
panic!("accumulator overflowed while counting the elements")
239+
}
240+
}
241+
242+
#[inline]
243+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
244+
Step::step(&self.start, n).and_then(|next| if next < self.end {
245+
self.start = next.add_one();
246+
Some(next)
247+
} else {
248+
None
249+
})
250+
}
251+
252+
// TODO: last specialisation if `A: Sub` and/or Range<A> is a DoubleEndedIterator.
253+
// so self.end.sub_one() or self.next_back()
303254
}
304255

305256
// These macros generate `ExactSizeIterator` impls for various range types.
@@ -313,11 +264,11 @@ range_incl_exact_iter_impl!(u8 u16 i8 i16);
313264
//
314265
// They need to guarantee that .size_hint() is either exact, or that
315266
// the upper bound is None when it does not fit the type limits.
316-
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
317-
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
267+
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64 i128 u128);
268+
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64 i128 u128);
318269

319270
#[stable(feature = "rust1", since = "1.0.0")]
320-
impl<A: Step + Clone> DoubleEndedIterator for ops::Range<A> where
271+
impl<A: Step> DoubleEndedIterator for ops::Range<A> where
321272
for<'a> &'a A: Add<&'a A, Output = A>,
322273
for<'a> &'a A: Sub<&'a A, Output = A>
323274
{
@@ -353,6 +304,23 @@ impl<A: Step> Iterator for ops::RangeFrom<A> where
353304
fn size_hint(&self) -> (usize, Option<usize>) {
354305
(usize::MAX, None)
355306
}
307+
308+
#[inline]
309+
fn count(self) -> usize {
310+
usize::MAX
311+
}
312+
313+
#[inline]
314+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
315+
let next = Step::step(&self.start, n).unwrap();
316+
self.start = next.add_one();
317+
Some(next)
318+
}
319+
320+
#[cold]
321+
fn last(self) -> Option<Self::Item> {
322+
panic!("Iterator::last on a `x..` cannot work")
323+
}
356324
}
357325

358326
#[unstable(feature = "fused", issue = "35602")]
@@ -394,6 +362,46 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> where
394362
None => (0, None),
395363
}
396364
}
365+
366+
#[inline]
367+
fn count(self) -> usize {
368+
if self.start > self.end {
369+
0
370+
} else {
371+
if let Some(x) = Step::steps_between_by_one(&self.start, &self.end) {
372+
x.add_one()
373+
} else {
374+
panic!("accumulator overflowed while counting the elements")
375+
}
376+
}
377+
}
378+
379+
#[inline]
380+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
381+
use cmp::Ordering::*;
382+
Step::step(&self.start, n).and_then(|next| match next.partial_cmp(&self.end) {
383+
Some(Less) => {
384+
self.start = next.add_one();
385+
Some(next)
386+
},
387+
Some(Equal) => {
388+
// Avoid overflow in the case `self.end` is a `max_value()`
389+
self.end.replace_zero();
390+
self.start.replace_one();
391+
Some(next)
392+
},
393+
_ => None
394+
})
395+
}
396+
397+
#[inline]
398+
fn last(self) -> Option<Self::Item> {
399+
if self.start <= self.end {
400+
Some(self.end)
401+
} else {
402+
None
403+
}
404+
}
397405
}
398406

399407
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]

0 commit comments

Comments
 (0)