Add specialization for deque1.prepend(deque2.drain(range))

This is used when moving elements between `VecDeque`s
This pattern is also used in examples of `VecDeque::prepend`. (see its docs)
This commit is contained in:
Antoni Spaanderman 2026-01-02 16:05:42 +01:00
parent 2848c2ebe9
commit 7e425b8985
No known key found for this signature in database
GPG key ID: AE0B68E552E5DF8C
3 changed files with 180 additions and 14 deletions

View file

@ -25,10 +25,10 @@ pub struct Drain<
// drain_start is stored in deque.len
pub(super) drain_len: usize,
// index into the logical array, not the physical one (always lies in [0..deque.len))
idx: usize,
pub(super) idx: usize,
// number of elements after the drained range
pub(super) tail_len: usize,
remaining: usize,
pub(super) remaining: usize,
// Needed to make Drain covariant over T
_marker: PhantomData<&'a T>,
}
@ -53,7 +53,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
// Only returns pointers to the slices, as that's all we need
// to drop them. May only be called if `self.remaining != 0`.
unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
pub(super) unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
unsafe {
let deque = self.deque.as_ref();

View file

@ -1,7 +1,7 @@
use core::iter::{Copied, Rev, TrustedLen};
use core::slice;
use super::VecDeque;
use super::{Drain, VecDeque};
use crate::alloc::Allocator;
#[cfg(not(test))]
use crate::vec;
@ -157,7 +157,8 @@ impl<T, A: Allocator> SpecExtendFront<T, vec::IntoIter<T>> for VecDeque<T, A> {
#[track_caller]
fn spec_extend_front(&mut self, mut iterator: vec::IntoIter<T>) {
let slice = iterator.as_slice();
// SAFETY: elements in the slice are forgotten after this call
self.reserve(slice.len());
// SAFETY: `slice.len()` space was just reserved and elements in the slice are forgotten after this call
unsafe { prepend_reversed(self, slice) };
iterator.forget_remaining_elements();
}
@ -169,7 +170,8 @@ impl<T, A: Allocator> SpecExtendFront<T, Rev<vec::IntoIter<T>>> for VecDeque<T,
fn spec_extend_front(&mut self, iterator: Rev<vec::IntoIter<T>>) {
let mut iterator = iterator.into_inner();
let slice = iterator.as_slice();
// SAFETY: elements in the slice are forgotten after this call
self.reserve(slice.len());
// SAFETY: `slice.len()` space was just reserved and elements in the slice are forgotten after this call
unsafe { prepend(self, slice) };
iterator.forget_remaining_elements();
}
@ -182,7 +184,8 @@ where
#[track_caller]
fn spec_extend_front(&mut self, iter: Copied<slice::Iter<'a, T>>) {
let slice = iter.into_inner().as_slice();
// SAFETY: T is Copy because Copied<slice::Iter<'a, T>> is Iterator
self.reserve(slice.len());
// SAFETY: `slice.len()` space was just reserved and T is Copy because Copied<slice::Iter<'a, T>> is Iterator
unsafe { prepend_reversed(self, slice) };
}
}
@ -194,17 +197,69 @@ where
#[track_caller]
fn spec_extend_front(&mut self, iter: Rev<Copied<slice::Iter<'a, T>>>) {
let slice = iter.into_inner().into_inner().as_slice();
// SAFETY: T is Copy because Rev<Copied<slice::Iter<'a, T>>> is Iterator
self.reserve(slice.len());
// SAFETY: `slice.len()` space was just reserved and T is Copy because Rev<Copied<slice::Iter<'a, T>>> is Iterator
unsafe { prepend(self, slice) };
}
}
impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront<T, Drain<'a, T, A2>> for VecDeque<T, A1> {
#[track_caller]
fn spec_extend_front(&mut self, mut iter: Drain<'a, T, A2>) {
if iter.remaining == 0 {
return;
}
self.reserve(iter.remaining);
unsafe {
// SAFETY: iter.remaining != 0.
let (left, right) = iter.as_slices();
// SAFETY:
// - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`.
// - The elements in `left` and `right` are forgotten after these calls.
prepend_reversed(self, &*left);
prepend_reversed(self, &*right);
}
iter.idx += iter.remaining;
iter.remaining = 0;
}
}
impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront<T, Rev<Drain<'a, T, A2>>>
for VecDeque<T, A1>
{
#[track_caller]
fn spec_extend_front(&mut self, iter: Rev<Drain<'a, T, A2>>) {
let mut iter = iter.into_inner();
if iter.remaining == 0 {
return;
}
self.reserve(iter.remaining);
unsafe {
// SAFETY: iter.remaining != 0.
let (left, right) = iter.as_slices();
// SAFETY:
// - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`.
// - The elements in `left` and `right` are forgotten after these calls.
prepend(self, &*right);
prepend(self, &*left);
}
iter.idx += iter.remaining;
iter.remaining = 0;
}
}
/// Prepends elements of `slice` to `deque` using a copy.
///
/// # Safety
///
/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
/// - `deque` must have space for `slice.len()` new elements.
/// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`.
unsafe fn prepend<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
deque.reserve(slice.len());
unsafe {
deque.head = deque.wrap_sub(deque.head, slice.len());
deque.copy_slice(deque.head, slice);
@ -212,12 +267,13 @@ unsafe fn prepend<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
}
}
/// Prepends elements of `slice` to `deque` in reverse order using a copy.
///
/// # Safety
///
/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
/// - `deque` must have space for `slice.len()` new elements.
/// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`.
unsafe fn prepend_reversed<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
deque.reserve(slice.len());
unsafe {
deque.head = deque.wrap_sub(deque.head, slice.len());
deque.copy_slice_reversed(deque.head, slice);

View file

@ -2156,6 +2156,116 @@ fn test_extend_front_specialization_copy_slice() {
assert_eq!(v.as_slices(), ([5].as_slice(), [4, 3, 2].as_slice()));
}
#[test]
fn test_extend_front_specialization_deque_drain() {
// trigger 8 code paths: all combinations of prepend and extend_front, wrap and no wrap (src deque), wrap and no wrap (dst deque)
/// Get deque containing `[1, 2, 3, 4]`, possibly wrapping in the middle (between the 2 and 3).
fn test_deque(wrap: bool) -> VecDeque<i32> {
if wrap {
let mut v = VecDeque::with_capacity(4);
v.extend([3, 4]);
v.prepend([1, 2]);
assert_eq!(v.as_slices(), ([1, 2].as_slice(), [3, 4].as_slice()));
v
} else {
VecDeque::from([1, 2, 3, 4])
}
}
// prepend, v2.head == 0
let mut v1 = VecDeque::with_capacity(7);
let mut v2 = test_deque(false);
v1.prepend(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
assert_eq!(v1, [1, 2, 3, 4]);
v1.pop_back();
let mut v2 = test_deque(false);
// this should wrap around the physical buffer
v1.prepend(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
// check it really wrapped
assert_eq!(v1.as_slices(), ([1].as_slice(), [2, 3, 4, 1, 2, 3].as_slice()));
// extend_front, v2.head == 0
let mut v1 = VecDeque::with_capacity(7);
let mut v2 = test_deque(false);
v1.extend_front(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
assert_eq!(v1, [4, 3, 2, 1]);
v1.pop_back();
let mut v2 = test_deque(false);
// this should wrap around the physical buffer
v1.extend_front(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
// check it really wrapped
assert_eq!(v1.as_slices(), ([4].as_slice(), [3, 2, 1, 4, 3, 2].as_slice()));
// prepend, v2.head != 0
let mut v1 = VecDeque::with_capacity(7);
let mut v2 = test_deque(true);
v1.prepend(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
assert_eq!(v1, [1, 2, 3, 4]);
v1.pop_back();
let mut v2 = test_deque(true);
// this should wrap around the physical buffer
v1.prepend(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
// check it really wrapped
assert_eq!(v1.as_slices(), ([1].as_slice(), [2, 3, 4, 1, 2, 3].as_slice()));
// extend_front, v2.head != 0
let mut v1 = VecDeque::with_capacity(7);
let mut v2 = test_deque(true);
v1.extend_front(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
assert_eq!(v1, [4, 3, 2, 1]);
v1.pop_back();
let mut v2 = test_deque(true);
// this should wrap around the physical buffer
v1.extend_front(v2.drain(..));
// drain removes all elements but keeps the buffer
assert_eq!(v2, []);
assert!(v2.capacity() >= 4);
// check it really wrapped
assert_eq!(v1.as_slices(), ([4].as_slice(), [3, 2, 1, 4, 3, 2].as_slice()));
}
#[test]
fn test_splice() {
let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]);