about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/alloc/tests/slice.rs74
-rw-r--r--library/core/src/slice/iter.rs33
2 files changed, 99 insertions, 8 deletions
diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs
index 1fb4a51acfd..fe3d7183ee2 100644
--- a/library/alloc/tests/slice.rs
+++ b/library/alloc/tests/slice.rs
@@ -994,6 +994,80 @@ fn test_rsplitnator() {
 }
 
 #[test]
+fn test_split_iterators_size_hint() {
+    for len in 0..=2 {
+        let mut v: Vec<u8> = (0..len).collect();
+        fn verify_descending(sequence: &[usize], context: &str) {
+            let len = sequence.len();
+            let target: Vec<usize> = (0..len).rev().collect();
+            assert_eq!(sequence, target, "while testing: {}", context);
+        }
+
+        macro_rules! test_size_hint {
+            ($create_iterator:expr) => {{
+                // with a predicate always returning false, the split*-iterators
+                // become maximally short, so the size_hint lower bounds are correct
+
+                macro_rules! p {
+                    () => {
+                        |_| false
+                    };
+                }
+                let mut short_iterator = $create_iterator;
+                let mut lower_bounds = vec![short_iterator.size_hint().0];
+                while let Some(_) = short_iterator.next() {
+                    lower_bounds.push(short_iterator.size_hint().0);
+                }
+                verify_descending(&lower_bounds, stringify!($create_iterator));
+            }
+            {
+                // with a predicate always returning true, the split*-iterators
+                // become maximally long, so the size_hint upper bounds are correct
+
+                macro_rules! p {
+                    () => {
+                        |_| true
+                    };
+                }
+                let mut long_iterator = $create_iterator;
+                let mut upper_bounds = vec![
+                    long_iterator.size_hint().1.expect("split*-methods have known upper bound"),
+                ];
+                while let Some(_) = long_iterator.next() {
+                    upper_bounds.push(
+                        long_iterator.size_hint().1.expect("split*-methods have known upper bound"),
+                    );
+                }
+                verify_descending(&upper_bounds, stringify!($create_iterator));
+            }};
+        }
+
+        test_size_hint!(v.split(p!()));
+        test_size_hint!(v.split_mut(p!()));
+        test_size_hint!(v.splitn(0, p!()));
+        test_size_hint!(v.splitn(1, p!()));
+        test_size_hint!(v.splitn(2, p!()));
+        test_size_hint!(v.splitn(3, p!()));
+        test_size_hint!(v.splitn_mut(0, p!()));
+        test_size_hint!(v.splitn_mut(1, p!()));
+        test_size_hint!(v.splitn_mut(2, p!()));
+        test_size_hint!(v.splitn_mut(3, p!()));
+        test_size_hint!(v.split_inclusive(p!()));
+        test_size_hint!(v.split_inclusive_mut(p!()));
+        test_size_hint!(v.rsplit(p!()));
+        test_size_hint!(v.rsplit_mut(p!()));
+        test_size_hint!(v.rsplitn(0, p!()));
+        test_size_hint!(v.rsplitn(1, p!()));
+        test_size_hint!(v.rsplitn(2, p!()));
+        test_size_hint!(v.rsplitn(3, p!()));
+        test_size_hint!(v.rsplitn_mut(0, p!()));
+        test_size_hint!(v.rsplitn_mut(1, p!()));
+        test_size_hint!(v.rsplitn_mut(2, p!()));
+        test_size_hint!(v.rsplitn_mut(3, p!()));
+    }
+}
+
+#[test]
 fn test_windowsator() {
     let v = &[1, 2, 3, 4];
 
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index d67af9cf668..c0dfba490ec 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -400,7 +400,13 @@ where
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) }
+        if self.finished {
+            (0, Some(0))
+        } else {
+            // If the predicate doesn't match anything, we yield one slice.
+            // If it matches every element, we yield `len() + 1` empty slices.
+            (1, Some(self.v.len() + 1))
+        }
     }
 }
 
@@ -525,7 +531,14 @@ where
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        if self.finished { (0, Some(0)) } else { (1, Some(self.v.len() + 1)) }
+        if self.finished {
+            (0, Some(0))
+        } else {
+            // If the predicate doesn't match anything, we yield one slice.
+            // If it matches every element, we yield `len()` one-element slices,
+            // or a single empty slice.
+            (1, Some(cmp::max(1, self.v.len())))
+        }
     }
 }
 
@@ -647,8 +660,8 @@ where
         if self.finished {
             (0, Some(0))
         } else {
-            // if the predicate doesn't match anything, we yield one slice
-            // if it matches every element, we yield len+1 empty slices.
+            // If the predicate doesn't match anything, we yield one slice.
+            // If it matches every element, we yield `len() + 1` empty slices.
             (1, Some(self.v.len() + 1))
         }
     }
@@ -763,9 +776,10 @@ where
         if self.finished {
             (0, Some(0))
         } else {
-            // if the predicate doesn't match anything, we yield one slice
-            // if it matches every element, we yield len+1 empty slices.
-            (1, Some(self.v.len() + 1))
+            // If the predicate doesn't match anything, we yield one slice.
+            // If it matches every element, we yield `len()` one-element slices,
+            // or a single empty slice.
+            (1, Some(cmp::max(1, self.v.len())))
         }
     }
 }
@@ -1008,7 +1022,10 @@ impl<T, I: SplitIter<Item = T>> Iterator for GenericSplitN<I> {
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
         let (lower, upper_opt) = self.iter.size_hint();
-        (lower, upper_opt.map(|upper| cmp::min(self.count, upper)))
+        (
+            cmp::min(self.count, lower),
+            Some(upper_opt.map_or(self.count, |upper| cmp::min(self.count, upper))),
+        )
     }
 }