Skip to content

Commit 31e49f0

Browse files
committed
Test and fix size_hint for slice's [r]split* iterators
Adds extensive test for all the [r]split* iterators. Fixes size_hint upper bound for split_inclusive* iterators which was one higher than necessary for non-empty slices. Fixes size_hint lower bound for [r]splitn* iterators when n==0, which was one too high.
1 parent 6bed1f0 commit 31e49f0

File tree

2 files changed

+99
-8
lines changed

2 files changed

+99
-8
lines changed

library/alloc/tests/slice.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,80 @@ fn test_rsplitnator() {
993993
assert!(xs.rsplitn(0, |x| *x % 2 == 0).next().is_none());
994994
}
995995

996+
#[test]
997+
fn test_split_iterators_size_hint() {
998+
for len in 0..=2 {
999+
let mut v: Vec<u8> = (0..len).collect();
1000+
fn verify_descending(sequence: &[usize], context: &str) {
1001+
let len = sequence.len();
1002+
let target: Vec<usize> = (0..len).rev().collect();
1003+
assert_eq!(sequence, target, "while testing: {}", context);
1004+
}
1005+
1006+
macro_rules! test_size_hint {
1007+
($create_iterator:expr) => {{
1008+
// with a predicate always returning false, the split*-iterators
1009+
// become maximally short, so the size_hint lower bounds are correct
1010+
1011+
macro_rules! p {
1012+
() => {
1013+
|_| false
1014+
};
1015+
}
1016+
let mut short_iterator = $create_iterator;
1017+
let mut lower_bounds = vec![short_iterator.size_hint().0];
1018+
while let Some(_) = short_iterator.next() {
1019+
lower_bounds.push(short_iterator.size_hint().0);
1020+
}
1021+
verify_descending(&lower_bounds, stringify!($create_iterator));
1022+
}
1023+
{
1024+
// with a predicate always returning true, the split*-iterators
1025+
// become maximally long, so the size_hint upper bounds are correct
1026+
1027+
macro_rules! p {
1028+
() => {
1029+
|_| true
1030+
};
1031+
}
1032+
let mut long_iterator = $create_iterator;
1033+
let mut upper_bounds = vec![
1034+
long_iterator.size_hint().1.expect("split*-methods have known upper bound"),
1035+
];
1036+
while let Some(_) = long_iterator.next() {
1037+
upper_bounds.push(
1038+
long_iterator.size_hint().1.expect("split*-methods have known upper bound"),
1039+
);
1040+
}
1041+
verify_descending(&upper_bounds, stringify!($create_iterator));
1042+
}};
1043+
}
1044+
1045+
test_size_hint!(v.split(p!()));
1046+
test_size_hint!(v.split_mut(p!()));
1047+
test_size_hint!(v.splitn(0, p!()));
1048+
test_size_hint!(v.splitn(1, p!()));
1049+
test_size_hint!(v.splitn(2, p!()));
1050+
test_size_hint!(v.splitn(3, p!()));
1051+
test_size_hint!(v.splitn_mut(0, p!()));
1052+
test_size_hint!(v.splitn_mut(1, p!()));
1053+
test_size_hint!(v.splitn_mut(2, p!()));
1054+
test_size_hint!(v.splitn_mut(3, p!()));
1055+
test_size_hint!(v.split_inclusive(p!()));
1056+
test_size_hint!(v.split_inclusive_mut(p!()));
1057+
test_size_hint!(v.rsplit(p!()));
1058+
test_size_hint!(v.rsplit_mut(p!()));
1059+
test_size_hint!(v.rsplitn(0, p!()));
1060+
test_size_hint!(v.rsplitn(1, p!()));
1061+
test_size_hint!(v.rsplitn(2, p!()));
1062+
test_size_hint!(v.rsplitn(3, p!()));
1063+
test_size_hint!(v.rsplitn_mut(0, p!()));
1064+
test_size_hint!(v.rsplitn_mut(1, p!()));
1065+
test_size_hint!(v.rsplitn_mut(2, p!()));
1066+
test_size_hint!(v.rsplitn_mut(3, p!()));
1067+
}
1068+
}
1069+
9961070
#[test]
9971071
fn test_windowsator() {
9981072
let v = &[1, 2, 3, 4];

library/core/src/slice/iter.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,13 @@ where
400400

401401
#[inline]
402402
fn size_hint(&self) -> (usize, Option<usize>) {
403-
if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) }
403+
if self.finished {
404+
(0, Some(0))
405+
} else {
406+
// If the predicate doesn't match anything, we yield one slice.
407+
// If it matches every element, we yield `len() + 1` empty slices.
408+
(1, Some(self.v.len() + 1))
409+
}
404410
}
405411
}
406412

@@ -525,7 +531,14 @@ where
525531

526532
#[inline]
527533
fn size_hint(&self) -> (usize, Option<usize>) {
528-
if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) }
534+
if self.finished {
535+
(0, Some(0))
536+
} else {
537+
// If the predicate doesn't match anything, we yield one slice.
538+
// If it matches every element, we yield `len()` one-element slices,
539+
// or a single empty slice.
540+
(1, Some(cmp::max(1, self.v.len())))
541+
}
529542
}
530543
}
531544

@@ -647,8 +660,8 @@ where
647660
if self.finished {
648661
(0, Some(0))
649662
} else {
650-
// if the predicate doesn't match anything, we yield one slice
651-
// if it matches every element, we yield len+1 empty slices.
663+
// If the predicate doesn't match anything, we yield one slice.
664+
// If it matches every element, we yield `len() + 1` empty slices.
652665
(1, Some(self.v.len() + 1))
653666
}
654667
}
@@ -763,9 +776,10 @@ where
763776
if self.finished {
764777
(0, Some(0))
765778
} else {
766-
// if the predicate doesn't match anything, we yield one slice
767-
// if it matches every element, we yield len+1 empty slices.
768-
(1, Some(self.v.len() + 1))
779+
// If the predicate doesn't match anything, we yield one slice.
780+
// If it matches every element, we yield `len()` one-element slices,
781+
// or a single empty slice.
782+
(1, Some(cmp::max(1, self.v.len())))
769783
}
770784
}
771785
}
@@ -1008,7 +1022,10 @@ impl<T, I: SplitIter<Item = T>> Iterator for GenericSplitN<I> {
10081022
#[inline]
10091023
fn size_hint(&self) -> (usize, Option<usize>) {
10101024
let (lower, upper_opt) = self.iter.size_hint();
1011-
(lower, upper_opt.map(|upper| cmp::min(self.count, upper)))
1025+
(
1026+
cmp::min(self.count, lower),
1027+
Some(upper_opt.map_or(self.count, |upper| cmp::min(self.count, upper))),
1028+
)
10121029
}
10131030
}
10141031

0 commit comments

Comments
 (0)