Implement VecDeque::truncate_front()

Tracking issue: #140667

Signed-off-by: Vladimir Krivopalov <vladimir.krivopalov@gmail.com>
This commit is contained in:
Vladimir Krivopalov 2025-03-03 12:06:09 -05:00
parent 0eb0b8cb67
commit cdf4143eb8
3 changed files with 137 additions and 0 deletions

View file

@ -1188,6 +1188,73 @@ impl<T, A: Allocator> VecDeque<T, A> {
}
}
/// 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]

View file

@ -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)]

View file

@ -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()));
}