diff options
Diffstat (limited to 'rust/alloc/vec/drain.rs')
| -rw-r--r-- | rust/alloc/vec/drain.rs | 81 | 
1 files changed, 76 insertions, 5 deletions
diff --git a/rust/alloc/vec/drain.rs b/rust/alloc/vec/drain.rs index b6a5f98e4fcd..d503d2f478ce 100644 --- a/rust/alloc/vec/drain.rs +++ b/rust/alloc/vec/drain.rs @@ -3,7 +3,7 @@  use crate::alloc::{Allocator, Global};  use core::fmt;  use core::iter::{FusedIterator, TrustedLen}; -use core::mem; +use core::mem::{self, ManuallyDrop, SizedTypeProperties};  use core::ptr::{self, NonNull};  use core::slice::{self}; @@ -67,6 +67,77 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {      pub fn allocator(&self) -> &A {          unsafe { self.vec.as_ref().allocator() }      } + +    /// Keep unyielded elements in the source `Vec`. +    /// +    /// # Examples +    /// +    /// ``` +    /// #![feature(drain_keep_rest)] +    /// +    /// let mut vec = vec!['a', 'b', 'c']; +    /// let mut drain = vec.drain(..); +    /// +    /// assert_eq!(drain.next().unwrap(), 'a'); +    /// +    /// // This call keeps 'b' and 'c' in the vec. +    /// drain.keep_rest(); +    /// +    /// // If we wouldn't call `keep_rest()`, +    /// // `vec` would be empty. +    /// assert_eq!(vec, ['b', 'c']); +    /// ``` +    #[unstable(feature = "drain_keep_rest", issue = "101122")] +    pub fn keep_rest(self) { +        // At this moment layout looks like this: +        // +        // [head] [yielded by next] [unyielded] [yielded by next_back] [tail] +        //        ^-- start         \_________/-- unyielded_len        \____/-- self.tail_len +        //                          ^-- unyielded_ptr                  ^-- tail +        // +        // Normally `Drop` impl would drop [unyielded] and then move [tail] to the `start`. +        // Here we want to +        // 1. Move [unyielded] to `start` +        // 2. Move [tail] to a new start at `start + len(unyielded)` +        // 3. Update length of the original vec to `len(head) + len(unyielded) + len(tail)` +        //    a. In case of ZST, this is the only thing we want to do +        // 4. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do +        let mut this = ManuallyDrop::new(self); + +        unsafe { +            let source_vec = this.vec.as_mut(); + +            let start = source_vec.len(); +            let tail = this.tail_start; + +            let unyielded_len = this.iter.len(); +            let unyielded_ptr = this.iter.as_slice().as_ptr(); + +            // ZSTs have no identity, so we don't need to move them around. +            let needs_move = mem::size_of::<T>() != 0; + +            if needs_move { +                let start_ptr = source_vec.as_mut_ptr().add(start); + +                // memmove back unyielded elements +                if unyielded_ptr != start_ptr { +                    let src = unyielded_ptr; +                    let dst = start_ptr; + +                    ptr::copy(src, dst, unyielded_len); +                } + +                // memmove back untouched tail +                if tail != (start + unyielded_len) { +                    let src = source_vec.as_ptr().add(tail); +                    let dst = start_ptr.add(unyielded_len); +                    ptr::copy(src, dst, this.tail_len); +                } +            } + +            source_vec.set_len(start + unyielded_len + this.tail_len); +        } +    }  }  #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] @@ -133,7 +204,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {          let mut vec = self.vec; -        if mem::size_of::<T>() == 0 { +        if T::IS_ZST {              // ZSTs have no identity, so we don't need to move them around, we only need to drop the correct amount.              // this can be achieved by manipulating the Vec length instead of moving values out from `iter`.              unsafe { @@ -154,9 +225,9 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {          }          // as_slice() must only be called when iter.len() is > 0 because -        // vec::Splice modifies vec::Drain fields and may grow the vec which would invalidate -        // the iterator's internal pointers. Creating a reference to deallocated memory -        // is invalid even when it is zero-length +        // it also gets touched by vec::Splice which may turn it into a dangling pointer +        // which would make it and the vec pointer point to different allocations which would +        // lead to invalid pointer arithmetic below.          let drop_ptr = iter.as_slice().as_ptr();          unsafe {  |