diff options
| author | bors <bors@rust-lang.org> | 2015-04-29 00:15:22 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2015-04-29 00:15:22 +0000 |
| commit | c4b23aec4c5ddf32df1e0ba3cc23212327cd8b1f (patch) | |
| tree | 45cf093827a54610239c2fddd3760b8addf4444d /src/libcore | |
| parent | 8871c17b76a1e0ab36ce2bb51008b53f596e5b3c (diff) | |
| parent | 95be21df477655131627def1943de73922b1f6a4 (diff) | |
| download | rust-c4b23aec4c5ddf32df1e0ba3cc23212327cd8b1f.tar.gz rust-c4b23aec4c5ddf32df1e0ba3cc23212327cd8b1f.zip | |
Auto merge of #24865 - bluss:range-size, r=alexcrichton
core: Fix size_hint for signed integer `Range<T>` iterators There was an overflow bug in .size_hint() for signed iterators, which produced an hilariously incorrect size or an overflow panic. Incorrect size is a serious bug since the iterators are marked ExactSizeIterator. (And leads to abort() on (-1i8..127).collect() when the collection tries to preallocate too much). > (-1i8..127).size_hint() (18446744073709551488, Some(18446744073709551488)) Bug found using quickcheck. Fixes #24851
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/iter.rs | 39 |
1 files changed, 35 insertions, 4 deletions
diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 21510e1a83b..0fa27a4312d 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -2407,12 +2407,14 @@ pub trait Step: PartialOrd { /// `start` should always be less than `end`, so the result should never /// be negative. /// + /// `by` must be > 0. + /// /// Returns `None` if it is not possible to calculate steps_between /// without overflow. fn steps_between(start: &Self, end: &Self, by: &Self) -> Option<usize>; } -macro_rules! step_impl { +macro_rules! step_impl_unsigned { ($($t:ty)*) => ($( impl Step for $t { #[inline] @@ -2423,7 +2425,33 @@ macro_rules! step_impl { #[allow(trivial_numeric_casts)] fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> { if *start <= *end { - Some(((*end - *start) / *by) as usize) + // Note: We assume $t <= usize here + Some((*end - *start) as usize / (*by as usize)) + } else { + Some(0) + } + } + } + )*) +} +macro_rules! step_impl_signed { + ($($t:ty)*) => ($( + impl Step for $t { + #[inline] + fn step(&self, by: &$t) -> Option<$t> { + (*self).checked_add(*by) + } + #[inline] + #[allow(trivial_numeric_casts)] + fn steps_between(start: &$t, end: &$t, by: &$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 + / (*by as usize)) + ) } else { Some(0) } @@ -2447,9 +2475,12 @@ macro_rules! step_impl_no_between { )*) } -step_impl!(usize u8 u16 u32 isize i8 i16 i32); +step_impl_unsigned!(usize u8 u16 u32); +step_impl_signed!(isize i8 i16 i32); +#[cfg(target_pointer_width = "64")] +step_impl_unsigned!(u64); #[cfg(target_pointer_width = "64")] -step_impl!(u64 i64); +step_impl_signed!(i64); #[cfg(target_pointer_width = "32")] step_impl_no_between!(u64 i64); |
