From cdf4143eb8209926e39a36df244880734bd46a23 Mon Sep 17 00:00:00 2001 From: Vladimir Krivopalov Date: Mon, 3 Mar 2025 12:06:09 -0500 Subject: [PATCH] Implement `VecDeque::truncate_front()` Tracking issue: #140667 Signed-off-by: Vladimir Krivopalov --- .../alloc/src/collections/vec_deque/mod.rs | 67 ++++++++++++++++++ library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/vec_deque.rs | 69 +++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index f8844e2d3a5cb..712f38a76c018 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1188,6 +1188,73 @@ impl VecDeque { } } + /// Shortens the deque, keeping the last `len` elements and dropping + /// the rest. + /// + /// If `len` is greater or equal to the deque's current length, this has + /// no effect. + /// + /// # Examples + /// + /// ``` + /// # #![feature(vec_deque_truncate_front)] + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_front(5); + /// buf.push_front(10); + /// buf.push_front(15); + /// assert_eq!(buf, [15, 10, 5]); + /// assert_eq!(buf.as_slices(), (&[15, 10, 5][..], &[][..])); + /// buf.truncate_front(1); + /// assert_eq!(buf.as_slices(), (&[5][..], &[][..])); + /// ``` + #[unstable(feature = "vec_deque_truncate_front", issue = "140667")] + pub fn truncate_front(&mut self, len: usize) { + /// Runs the destructor for all items in the slice when it gets dropped (normally or + /// during unwinding). + struct Dropper<'a, T>(&'a mut [T]); + + impl<'a, T> Drop for Dropper<'a, T> { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + } + } + } + + unsafe { + if len >= self.len { + // No action is taken + return; + } + + let (front, back) = self.as_mut_slices(); + if len > back.len() { + // The 'back' slice remains unchanged. + // front.len() + back.len() == self.len, so 'end' is non-negative + // and end < front.len() + let end = front.len() - (len - back.len()); + let drop_front = front.get_unchecked_mut(..end) as *mut _; + self.head += end; + self.len = len; + ptr::drop_in_place(drop_front); + } else { + let drop_front = front as *mut _; + // 'end' is non-negative by the condition above + let end = back.len() - len; + let drop_back = back.get_unchecked_mut(..end) as *mut _; + self.head = self.to_physical_idx(self.len - len); + self.len = len; + + // Make sure the second half is dropped even when a destructor + // in the first one panics. + let _back_dropper = Dropper(&mut *drop_back); + ptr::drop_in_place(drop_front); + } + } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f1f4cc6f93bbd..38309585fad61 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -37,6 +37,7 @@ #![feature(str_as_str)] #![feature(strict_provenance_lints)] #![feature(vec_deque_pop_if)] +#![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![allow(internal_features)] diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 1b03c29e5bda1..b77ea3a312bef 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1686,6 +1686,40 @@ fn truncate_leak() { assert_eq!(unsafe { DROPS }, 7); } +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn truncate_front_leak() { + static mut DROPS: i32 = 0; + + struct D(bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.0 { + panic!("panic in `drop`"); + } + } + } + + let mut q = VecDeque::new(); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(true)); + q.push_front(D(false)); + q.push_front(D(false)); + + catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok(); + + assert_eq!(unsafe { DROPS }, 7); +} + #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_leak() { @@ -1863,3 +1897,38 @@ fn test_collect_from_into_iter_keeps_allocation() { assert_eq!(v.capacity(), 13); } } + +#[test] +fn test_truncate_front() { + let mut v = VecDeque::with_capacity(13); + v.extend(0..7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.truncate_front(10); + assert_eq!(v.len(), 7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.truncate_front(7); + assert_eq!(v.len(), 7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.truncate_front(3); + assert_eq!(v.as_slices(), ([4, 5, 6].as_slice(), [].as_slice())); + assert_eq!(v.len(), 3); + v.truncate_front(0); + assert_eq!(v.as_slices(), ([].as_slice(), [].as_slice())); + assert_eq!(v.len(), 0); + + v.clear(); + v.extend(0..7); + assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); + v.push_front(9); + v.push_front(8); + v.push_front(7); + assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(12); + assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(10); + assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(8); + assert_eq!(v.as_slices(), ([9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); + v.truncate_front(5); + assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice())); +}