about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoshua Wong <joshuawong@anticentri.st>2024-05-12 19:56:03 -0500
committerJoshua Wong <joshuawong@anticentri.st>2024-05-18 18:30:20 -0500
commit9d6b93c3e6609aa104da23cb9091c6c2e277f71e (patch)
treea88298b12c4fdbaa92e85d0f0b7f9fd830eef0ba
parent6165dca6dbf66d45a373e08f16d1fc85f7be0ac7 (diff)
downloadrust-9d6b93c3e6609aa104da23cb9091c6c2e277f71e.tar.gz
rust-9d6b93c3e6609aa104da23cb9091c6c2e277f71e.zip
specialize `Iterator::fold` for `vec::IntoIter`
LLVM currently adds a redundant check for the returned option, in addition
to the `self.ptr != self.end` check when using the default
`Iterator::fold` method that calls `vec::IntoIter::next` in a loop.
-rw-r--r--library/alloc/src/vec/into_iter.rs29
1 files changed, 27 insertions, 2 deletions
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs
index 28082143402..c4798933770 100644
--- a/library/alloc/src/vec/into_iter.rs
+++ b/library/alloc/src/vec/into_iter.rs
@@ -289,13 +289,38 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
         };
     }
 
-    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
+    fn fold<B, F>(mut self, mut accum: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        if T::IS_ZST {
+            while self.ptr.as_ptr() != self.end.cast_mut() {
+                // SAFETY: we just checked that `self.ptr` is in bounds.
+                let tmp = unsafe { self.ptr.read() };
+                // See `next` for why we subtract from `end` here.
+                self.end = self.end.wrapping_byte_sub(1);
+                accum = f(accum, tmp);
+            }
+        } else {
+            // SAFETY: `self.end` can only be null if `T` is a ZST.
+            while self.ptr != non_null!(self.end, T) {
+                // SAFETY: we just checked that `self.ptr` is in bounds.
+                let tmp = unsafe { self.ptr.read() };
+                // SAFETY: the maximum this can be is `self.end`.
+                // Increment `self.ptr` first to avoid double dropping in the event of a panic.
+                self.ptr = unsafe { self.ptr.add(1) };
+                accum = f(accum, tmp);
+            }
+        }
+        accum
+    }
+
+    fn try_fold<B, F, R>(&mut self, mut accum: B, mut f: F) -> R
     where
         Self: Sized,
         F: FnMut(B, Self::Item) -> R,
         R: core::ops::Try<Output = B>,
     {
-        let mut accum = init;
         if T::IS_ZST {
             while self.ptr.as_ptr() != self.end.cast_mut() {
                 // SAFETY: we just checked that `self.ptr` is in bounds.