Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement useful steps_between for all integers #59444

Merged
merged 3 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 10 additions & 51 deletions src/libcore/iter/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,9 @@ macro_rules! step_impl_unsigned {
issue = "42168")]
impl Step for $t {
#[inline]
#[allow(trivial_numeric_casts)]
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
if *start < *end {
// Note: We assume $t <= usize here
Some((*end - *start) as usize)
usize::try_from(*end - *start).ok()
} else {
Some(0)
}
Expand All @@ -98,13 +96,11 @@ macro_rules! step_impl_signed {
issue = "42168")]
impl Step for $t {
#[inline]
#[allow(trivial_numeric_casts)]
fn steps_between(start: &$t, end: &$t) -> Option<usize> {
if *start < *end {
// Note: We assume $t <= isize here
// Use .wrapping_sub and cast to usize to compute the
// difference that may not fit inside the range of isize.
Some((*end as isize).wrapping_sub(*start as isize) as usize)
// Use .wrapping_sub and cast to unsigned to compute the
// difference that may not fit inside the range of $t.
usize::try_from(end.wrapping_sub(*start) as $unsigned).ok()
} else {
Some(0)
}
Expand Down Expand Up @@ -134,46 +130,9 @@ macro_rules! step_impl_signed {
)*)
}

macro_rules! step_impl_no_between {
($($t:ty)*) => ($(
#[unstable(feature = "step_trait",
reason = "likely to be replaced by finer-grained traits",
issue = "42168")]
impl Step for $t {
#[inline]
fn steps_between(_start: &Self, _end: &Self) -> Option<usize> {
None
}

#[inline]
fn add_usize(&self, n: usize) -> Option<Self> {
self.checked_add(n as $t)
}

step_identical_methods!();
}
)*)
}

step_impl_unsigned!(usize u8 u16);
#[cfg(not(target_pointer_width = "16"))]
step_impl_unsigned!(u32);
#[cfg(target_pointer_width = "16")]
step_impl_no_between!(u32);
step_impl_unsigned!(usize u8 u16 u32 u64 u128);
step_impl_signed!([isize: usize] [i8: u8] [i16: u16]);
#[cfg(not(target_pointer_width = "16"))]
step_impl_signed!([i32: u32]);
#[cfg(target_pointer_width = "16")]
step_impl_no_between!(i32);
#[cfg(target_pointer_width = "64")]
step_impl_unsigned!(u64);
#[cfg(target_pointer_width = "64")]
step_impl_signed!([i64: u64]);
// If the target pointer width is not 64-bits, we
// assume here that it is less than 64-bits.
#[cfg(not(target_pointer_width = "64"))]
step_impl_no_between!(u64 i64);
step_impl_no_between!(u128 i128);
step_impl_signed!([i32: u32] [i64: u64] [i128: u128]);

macro_rules! range_exact_iter_impl {
($($t:ty)*) => ($(
Expand Down Expand Up @@ -229,7 +188,7 @@ impl<A: Step> Iterator for ops::Range<A> {
fn size_hint(&self) -> (usize, Option<usize>) {
match Step::steps_between(&self.start, &self.end) {
Some(hint) => (hint, Some(hint)),
None => (0, None)
None => (usize::MAX, None)
}
}

Expand Down Expand Up @@ -273,8 +232,8 @@ range_incl_exact_iter_impl!(u8 u16 i8 i16);
//
// They need to guarantee that .size_hint() is either exact, or that
// the upper bound is None when it does not fit the type limits.
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 i64 u64);
range_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);
range_incl_trusted_len_impl!(usize isize u8 i8 u16 i16 u32 i32 u64 i64 u128 i128);

#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step> DoubleEndedIterator for ops::Range<A> {
Expand Down Expand Up @@ -350,7 +309,7 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {

match Step::steps_between(&self.start, &self.end) {
Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
None => (0, None),
None => (usize::MAX, None),
}
}

Expand Down
61 changes: 61 additions & 0 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::cell::Cell;
use core::convert::TryFrom;
use core::iter::*;
use core::{i8, i16, isize};
use core::usize;
Expand Down Expand Up @@ -1800,6 +1801,66 @@ fn test_range_inclusive_folds() {
assert!(it.is_empty());
}

#[test]
fn test_range_size_hint() {
use core::usize::MAX as UMAX;
assert_eq!((0..0usize).size_hint(), (0, Some(0)));
assert_eq!((0..100usize).size_hint(), (100, Some(100)));
assert_eq!((0..UMAX).size_hint(), (UMAX, Some(UMAX)));

let umax = u128::try_from(UMAX).unwrap();
assert_eq!((0..0u128).size_hint(), (0, Some(0)));
assert_eq!((0..100u128).size_hint(), (100, Some(100)));
assert_eq!((0..umax).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((0..umax + 1).size_hint(), (UMAX, None));

use core::isize::{MAX as IMAX, MIN as IMIN};
assert_eq!((0..0isize).size_hint(), (0, Some(0)));
assert_eq!((-100..100isize).size_hint(), (200, Some(200)));
assert_eq!((IMIN..IMAX).size_hint(), (UMAX, Some(UMAX)));

let imin = i128::try_from(IMIN).unwrap();
let imax = i128::try_from(IMAX).unwrap();
assert_eq!((0..0i128).size_hint(), (0, Some(0)));
assert_eq!((-100..100i128).size_hint(), (200, Some(200)));
assert_eq!((imin..imax).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((imin..imax + 1).size_hint(), (UMAX, None));
}

#[test]
fn test_range_inclusive_size_hint() {
use core::usize::MAX as UMAX;
assert_eq!((1..=0usize).size_hint(), (0, Some(0)));
assert_eq!((0..=0usize).size_hint(), (1, Some(1)));
assert_eq!((0..=100usize).size_hint(), (101, Some(101)));
assert_eq!((0..=UMAX - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((0..=UMAX).size_hint(), (UMAX, None));

let umax = u128::try_from(UMAX).unwrap();
assert_eq!((1..=0u128).size_hint(), (0, Some(0)));
assert_eq!((0..=0u128).size_hint(), (1, Some(1)));
assert_eq!((0..=100u128).size_hint(), (101, Some(101)));
assert_eq!((0..=umax - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((0..=umax).size_hint(), (UMAX, None));
assert_eq!((0..=umax + 1).size_hint(), (UMAX, None));

use core::isize::{MAX as IMAX, MIN as IMIN};
assert_eq!((0..=-1isize).size_hint(), (0, Some(0)));
assert_eq!((0..=0isize).size_hint(), (1, Some(1)));
assert_eq!((-100..=100isize).size_hint(), (201, Some(201)));
assert_eq!((IMIN..=IMAX - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((IMIN..=IMAX).size_hint(), (UMAX, None));

let imin = i128::try_from(IMIN).unwrap();
let imax = i128::try_from(IMAX).unwrap();
assert_eq!((0..=-1i128).size_hint(), (0, Some(0)));
assert_eq!((0..=0i128).size_hint(), (1, Some(1)));
assert_eq!((-100..=100i128).size_hint(), (201, Some(201)));
assert_eq!((imin..=imax - 1).size_hint(), (UMAX, Some(UMAX)));
assert_eq!((imin..=imax).size_hint(), (UMAX, None));
assert_eq!((imin..=imax + 1).size_hint(), (UMAX, None));
}

#[test]
fn test_repeat() {
let mut it = repeat(42);
Expand Down