about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2019-03-25 21:12:53 -0700
committerJosh Stone <jistone@redhat.com>2019-03-26 10:13:48 -0700
commit01162d86c533cfb08221bb9475042ca94d1fd002 (patch)
treee6f0ab70e4169ddb797700643a2debfad846c44f
parentd91b32b4dc9db066ab7deedbc51dc62ba597524b (diff)
downloadrust-01162d86c533cfb08221bb9475042ca94d1fd002.tar.gz
rust-01162d86c533cfb08221bb9475042ca94d1fd002.zip
Implement useful steps_between for all integers
We can use `usize::try_from` to convert steps from any size of integer.
This enables a meaningful `size_hint()` for larger ranges, rather than
always just `(0, None)`. Now they return the true `(len, Some(len))`
when it fits, otherwise `(usize::MAX, None)` for overflow.
-rw-r--r--src/libcore/iter/range.rs57
-rw-r--r--src/libcore/tests/iter.rs49
2 files changed, 57 insertions, 49 deletions
diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs
index e7efd9728b9..145fde0350e 100644
--- a/src/libcore/iter/range.rs
+++ b/src/libcore/iter/range.rs
@@ -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)
                 }
@@ -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)
                 }
@@ -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)*) => ($(
@@ -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)
         }
     }
 
@@ -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),
         }
     }
 
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index a3f0b02e2fe..bcf0b7ec463 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -1,4 +1,5 @@
 use core::cell::Cell;
+use core::convert::TryFrom;
 use core::iter::*;
 use core::{i8, i16, isize};
 use core::usize;
@@ -1801,6 +1802,54 @@ fn test_range_inclusive_folds() {
 }
 
 #[test]
+fn test_range_size_hint() {
+    use core::usize::MAX as UMAX;
+    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..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!((-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!((-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!((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!((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!((-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!((-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);
     assert_eq!(it.next(), Some(42));