From 2a8fc9b02cb77664551712ecc5ac491c91238e99 Mon Sep 17 00:00:00 2001 From: critiqjo Date: Fri, 1 May 2015 00:47:15 +0530 Subject: [PATCH] iterator: Add `StepBy::size_hint` method Fixes `Step::steps_between` implementations by integer types to correctly handle `by != 1`. --- src/libcore/iter.rs | 56 ++++++++++++++++++++++++++++++----------- src/libcoretest/iter.rs | 14 ++++++++++- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 0fa27a4312d..20720b93641 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -2402,14 +2402,10 @@ pub trait Step: PartialOrd { /// Steps `self` if possible. fn step(&self, by: &Self) -> Option; - /// Returns the number of steps between two step objects. + /// Returns the number of steps between two step objects. The count is + /// inclusive of `start` and exclusive of `end`. /// - /// `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 + /// Returns `None` if it is not possible to calculate `steps_between` /// without overflow. fn steps_between(start: &Self, end: &Self, by: &Self) -> Option; } @@ -2424,9 +2420,16 @@ fn step(&self, by: &$t) -> Option<$t> { #[inline] #[allow(trivial_numeric_casts)] fn steps_between(start: &$t, end: &$t, by: &$t) -> Option { - if *start <= *end { + if *by == 0 { return None; } + if *start < *end { // Note: We assume $t <= usize here - Some((*end - *start) as usize / (*by as usize)) + let diff = (*end - *start) as usize; + let by = *by as usize; + if diff % by > 0 { + Some(diff / by + 1) + } else { + Some(diff / by) + } } else { Some(0) } @@ -2444,16 +2447,29 @@ fn step(&self, by: &$t) -> Option<$t> { #[inline] #[allow(trivial_numeric_casts)] fn steps_between(start: &$t, end: &$t, by: &$t) -> Option { - if *start <= *end { + if *by == 0 { return None; } + let mut diff: usize; + let mut by_u: usize; + if *by > 0 { + if *start >= *end { + return Some(0); + } // 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)) - ) + diff = (*end as isize).wrapping_sub(*start as isize) as usize; + by_u = *by as usize; } else { - Some(0) + if *start <= *end { + return Some(0); + } + diff = (*start as isize).wrapping_sub(*end as isize) as usize; + by_u = (*by as isize).wrapping_mul(-1) as usize; + } + if diff % by_u > 0 { + Some(diff / by_u + 1) + } else { + Some(diff / by_u) } } } @@ -2675,6 +2691,16 @@ fn next(&mut self) -> Option { None } } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + match Step::steps_between(&self.range.start, + &self.range.end, + &self.step_by) { + Some(hint) => (hint, Some(hint)), + None => (0, None) + } + } } macro_rules! range_exact_iter_impl { diff --git a/src/libcoretest/iter.rs b/src/libcoretest/iter.rs index bb36630f168..95a6236e9c3 100644 --- a/src/libcoretest/iter.rs +++ b/src/libcoretest/iter.rs @@ -11,7 +11,7 @@ use core::iter::*; use core::iter::order::*; use core::iter::MinMaxResult::*; -use core::isize; +use core::{i8, i16, isize}; use core::usize; use core::cmp; @@ -786,6 +786,18 @@ fn test_range_step() { assert_eq!((200..255).step_by(50).collect::>(), [200, 250]); assert_eq!((200..-5).step_by(1).collect::>(), []); assert_eq!((200..200).step_by(1).collect::>(), []); + + assert_eq!((0..20).step_by(1).size_hint(), (20, Some(20))); + assert_eq!((0..20).step_by(21).size_hint(), (1, Some(1))); + assert_eq!((0..20).step_by(5).size_hint(), (4, Some(4))); + assert_eq!((20..0).step_by(-5).size_hint(), (4, Some(4))); + assert_eq!((20..0).step_by(-6).size_hint(), (4, Some(4))); + assert_eq!((20..-5).step_by(1).size_hint(), (0, Some(0))); + assert_eq!((20..20).step_by(1).size_hint(), (0, Some(0))); + assert_eq!((0..1).step_by(0).size_hint(), (0, None)); + assert_eq!((i8::MAX..i8::MIN).step_by(i8::MIN).size_hint(), (2, Some(2))); + assert_eq!((i16::MIN..i16::MAX).step_by(i16::MAX).size_hint(), (3, Some(3))); + assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX))); } #[test] -- GitLab