Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 83 additions & 1 deletion library/alloc/src/collections/vec_deque/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub use self::iter::Iter;

mod iter;

use self::spec_extend::SpecExtend;
use self::spec_extend::{SpecExtend, SpecExtendFront};

mod spec_extend;

Expand Down Expand Up @@ -174,6 +174,21 @@ impl<T, A: Allocator> VecDeque<T, A> {
self.len += 1;
}

/// Prepends an element to the buffer.
///
/// # Safety
///
/// May only be called if `deque.len() < deque.capacity()`
#[inline]
unsafe fn push_front_unchecked(&mut self, element: T) {
self.head = self.wrap_sub(self.head, 1);
// SAFETY: Because of the precondition, it's guaranteed that there is space
// in the logical array before the first element (where self.head is now).
unsafe { self.buffer_write(self.head, element) };
// This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`.
self.len += 1;
}

/// Moves an element out of the buffer
#[inline]
unsafe fn buffer_read(&mut self, off: usize) -> T {
Expand Down Expand Up @@ -1956,6 +1971,73 @@ impl<T, A: Allocator> VecDeque<T, A> {
unsafe { self.buffer_write(self.to_physical_idx(len), value) }
}

/// Prepends all contents of the iterator to the front of the deque.
/// The order of the contents is preserved.
///
/// To get behavior like [`append`][VecDeque::append] where elements are moved
/// from the other collection to this one, use `self.prepend(other.drain(..))`.
///
/// # Examples
///
/// ```
/// #![feature(deque_extend_front)]
/// use std::collections::VecDeque;
///
/// let mut deque = VecDeque::from([4, 5, 6]);
/// deque.prepend([1, 2, 3]);
/// assert_eq!(deque, [1, 2, 3, 4, 5, 6]);
/// ```
///
/// Move values between collections like [`append`][VecDeque::append] does but prepend to the front:
///
/// ```
/// #![feature(deque_extend_front)]
/// use std::collections::VecDeque;
///
/// let mut deque1 = VecDeque::from([4, 5, 6]);
/// let mut deque2 = VecDeque::from([1, 2, 3]);
/// deque1.prepend(deque2.drain(..));
/// assert_eq!(deque1, [1, 2, 3, 4, 5, 6]);
/// assert!(deque2.is_empty());
/// ```
#[unstable(feature = "deque_extend_front", issue = "146975")]
#[track_caller]
pub fn prepend<I: IntoIterator<Item = T, IntoIter: DoubleEndedIterator>>(&mut self, other: I) {
self.extend_front(other.into_iter().rev())
}

/// Prepends all contents of the iterator to the front of the deque,
/// as if [`push_front`][VecDeque::push_front] was called repeatedly with
/// the values yielded by the iterator.
///
/// # Examples
///
/// ```
/// #![feature(deque_extend_front)]
/// use std::collections::VecDeque;
///
/// let mut deque = VecDeque::from([4, 5, 6]);
/// deque.extend_front([3, 2, 1]);
/// assert_eq!(deque, [1, 2, 3, 4, 5, 6]);
/// ```
///
/// This behaves like [`push_front`][VecDeque::push_front] was called repeatedly:
///
/// ```
/// use std::collections::VecDeque;
///
/// let mut deque = VecDeque::from([4, 5, 6]);
/// for v in [3, 2, 1] {
/// deque.push_front(v);
/// }
/// assert_eq!(deque, [1, 2, 3, 4, 5, 6]);
/// ```
#[unstable(feature = "deque_extend_front", issue = "146975")]
#[track_caller]
pub fn extend_front<I: IntoIterator<Item = T>>(&mut self, iter: I) {
<Self as SpecExtendFront<T, I::IntoIter>>::spec_extend_front(self, iter.into_iter());
}

#[inline]
fn is_contiguous(&self) -> bool {
// Do the calculation like this to avoid overflowing if len + head > usize::MAX
Expand Down
95 changes: 95 additions & 0 deletions library/alloc/src/collections/vec_deque/spec_extend.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(not(test))]
use core::iter::Rev;
use core::iter::TrustedLen;
use core::slice;

Expand Down Expand Up @@ -114,3 +116,96 @@ where
}
}
}

// Specialization trait used for VecDeque::extend_front
pub(super) trait SpecExtendFront<T, I> {
#[track_caller]
fn spec_extend_front(&mut self, iter: I);
}

impl<T, I, A: Allocator> SpecExtendFront<T, I> for VecDeque<T, A>
where
I: Iterator<Item = T>,
{
#[track_caller]
default fn spec_extend_front(&mut self, mut iter: I) {
// This function should be the moral equivalent of:
//
// for item in iter {
// self.push_front(item);
// }

while let Some(element) = iter.next() {
let (lower, _) = iter.size_hint();
self.reserve(lower.saturating_add(1));

// SAFETY: We just reserved space for at least one element.
unsafe { self.push_front_unchecked(element) };

// Inner loop to avoid repeatedly calling `reserve`.
while self.len < self.capacity() {
let Some(element) = iter.next() else {
return;
};
// SAFETY: The loop condition guarantees that `self.len() < self.capacity()`.
unsafe { self.push_front_unchecked(element) };
}
}
}
}

#[cfg(not(test))]
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_mut_slice();
slice.reverse();
unsafe { prepend(self, slice) };
iterator.forget_remaining_elements();
}
}

#[cfg(not(test))]
impl<T, A: Allocator> SpecExtendFront<T, Rev<vec::IntoIter<T>>> for VecDeque<T, A> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a curious specialization. Is the expectation that people will frequently use Rev to prepend to a vecdeque?

Anyway, specializations should have dedicated tests since they're not covered by the regular ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a curious specialization. Is the expectation that people will frequently use Rev to prepend to a vecdeque?

This depends on the design of extend_front, if it will act like repeated push_front, specializing this can have significant performance improvements. This would be used when prepending elements from a Vec into a VecDeque, in original order, a bit like the requested VecDeque::prepend (#69939 (comment)).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepend (see tracking issue) will use this, it calls self.extend_front(other.into_iter().rev())

#[track_caller]
fn spec_extend_front(&mut self, iterator: Rev<vec::IntoIter<T>>) {
let mut iterator = iterator.into_inner();
unsafe { prepend(self, iterator.as_slice()) };
iterator.forget_remaining_elements();
}
}

// impl<T, A: Allocator> SpecExtendFront<T, Copied<slice::Iter<'_, T>>> for VecDeque<T, A>
// where
// T: Copy,
// {
// #[track_caller]
// fn spec_extend_front(&mut self, _iter: Copied<slice::Iter<'_, T>>) {
// // unsafe { prepend(self, slice) };
// // reverse in place?
// }
// }

// impl<T, A: Allocator> SpecExtendFront<T, Rev<Copied<slice::Iter<'_, T>>>> for VecDeque<T, A>
// where
// T: Copy,
// {
// #[track_caller]
// fn spec_extend_front(&mut self, iter: Rev<Copied<slice::Iter<'_, T>>>) {
// unsafe { prepend(self, iter.into_inner().it.as_slice()) };
// }
// }
Comment on lines +189 to +197
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to add specialization for deque.extend_front(slice.iter().rev()) where slice is &[T] with T: Copy (thus also deque.prepend(slice.iter())) but got stuck here. Copied's inner iterator can not be accessed (I made it pub while trying) and I get the following error: "cannot specialize on trait Clone". Any ideas on if and how this is possible? What does Extend do different so that it can specialize on Copy?


/// # Safety
///
/// `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
#[cfg(not(test))]
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);
deque.len += slice.len();
}
}
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
#![feature(ptr_alignment_type)]
#![feature(ptr_internals)]
#![feature(ptr_metadata)]
#![feature(rev_into_inner)]
#![feature(set_ptr_value)]
#![feature(sized_type_properties)]
#![feature(slice_from_ptr_range)]
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#![feature(char_max_len)]
#![feature(cow_is_borrowed)]
#![feature(core_intrinsics)]
#![feature(deque_extend_front)]
#![feature(downcast_unchecked)]
#![feature(exact_size_is_empty)]
#![feature(hashmap_internals)]
Expand Down
43 changes: 43 additions & 0 deletions library/alloctests/tests/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1849,3 +1849,46 @@ fn test_truncate_front() {
v.truncate_front(5);
assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice()));
}

#[test]
fn test_extend_front() {
let mut v = VecDeque::new();
v.extend_front(0..3);
assert_eq!(v, [2, 1, 0]);
v.extend_front(3..6);
assert_eq!(v, [5, 4, 3, 2, 1, 0]);
v.prepend([1; 4]);
assert_eq!(v, [1, 1, 1, 1, 5, 4, 3, 2, 1, 0]);

let mut v = VecDeque::with_capacity(8);
let cap = v.capacity();
v.extend(0..4);
v.truncate_front(2);
v.extend_front(4..8);
assert_eq!(v.as_slices(), ([7, 6].as_slice(), [5, 4, 2, 3].as_slice()));
assert_eq!(v.capacity(), cap);

let mut v = VecDeque::new();
v.extend_front([]);
v.extend_front(None);
v.extend_front(vec![]);
v.prepend([]);
v.prepend(None);
v.prepend(vec![]);
assert_eq!(v.capacity(), 0);
v.extend_front(Some(123));
assert_eq!(v, [123]);
}

#[test]
fn test_extend_front_specialization() {
let mut v = VecDeque::with_capacity(4);
v.prepend(vec![1, 2, 3]);
assert_eq!(v, [1, 2, 3]);
v.pop_front();
v.prepend((-4..2).collect::<Vec<_>>());
assert_eq!(v, (-4..=3).collect::<Vec<_>>());
v.clear();
v.extend_front(vec![1, 2, 3]);
assert_eq!(v, [3, 2, 1]);
}
Loading