about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe8472 <git@infinite-source.de>2019-11-17 14:50:48 +0100
committerThe8472 <git@infinite-source.de>2020-09-03 20:59:11 +0200
commita4e385a0d0c1966870a18db5e138a55b8ebc7b04 (patch)
tree20159d1fc00d1a15d4c77becbba0639e25d638b6
parent631543dcb4e79d3c134a7dfc8e87b62287e96796 (diff)
downloadrust-a4e385a0d0c1966870a18db5e138a55b8ebc7b04.tar.gz
rust-a4e385a0d0c1966870a18db5e138a55b8ebc7b04.zip
use memmove instead of generic in-place iteration for IntoIter source
this is the original SpecExtend<_, IntoIter> logic except generalizing
the fast-path to include a memmove
-rw-r--r--library/alloc/src/vec.rs12
1 files changed, 10 insertions, 2 deletions
diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs
index 29878851da5..ce7ea2058b5 100644
--- a/library/alloc/src/vec.rs
+++ b/library/alloc/src/vec.rs
@@ -2216,14 +2216,22 @@ impl<T> SpecFrom<T, IntoIter<T>> for Vec<T> {
         // A common case is passing a vector into a function which immediately
         // re-collects into a vector. We can short circuit this if the IntoIter
         // has not been advanced at all.
-        if iterator.buf.as_ptr() as *const _ == iterator.ptr {
+        // We can also reuse the memory and move the data to the front if
+        // allocating a new vector and moving to it would result in the same capacity
+        let non_zero_offset = iterator.buf.as_ptr() as *const _ != iterator.ptr;
+        if !non_zero_offset || iterator.len() >= iterator.cap / 2 {
             unsafe {
                 let it = ManuallyDrop::new(iterator);
+                if non_zero_offset {
+                    ptr::copy(it.ptr, it.buf.as_ptr(), it.len());
+                }
                 return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap);
             }
         }
 
-        from_into_iter_source(iterator)
+        let mut vec = Vec::new();
+        vec.extend(iterator);
+        vec
     }
 }