Skip to content
Merged
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
113 changes: 112 additions & 1 deletion library/alloc/src/collections/vec_deque/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,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 @@ -179,6 +179,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 @@ -505,6 +520,35 @@ impl<T, A: Allocator> VecDeque<T, A> {
}
}

/// Copies all values from `src` to `dst` in reversed order, wrapping around if needed.
/// Assumes capacity is sufficient.
/// Equivalent to calling [`VecDeque::copy_slice`] with a [reversed](https://doc.rust-lang.org/std/primitive.slice.html#method.reverse) slice.
#[inline]
unsafe fn copy_slice_reversed(&mut self, dst: usize, src: &[T]) {
/// # Safety
///
/// See [`ptr::copy_nonoverlapping`].
unsafe fn copy_nonoverlapping_reversed<T>(src: *const T, dst: *mut T, count: usize) {
for i in 0..count {
unsafe { ptr::copy_nonoverlapping(src.add(count - 1 - i), dst.add(i), 1) };
}
}

debug_assert!(src.len() <= self.capacity());
let head_room = self.capacity() - dst;
if src.len() <= head_room {
unsafe {
copy_nonoverlapping_reversed(src.as_ptr(), self.ptr().add(dst), src.len());
}
} else {
let (left, right) = src.split_at(src.len() - head_room);
unsafe {
copy_nonoverlapping_reversed(right.as_ptr(), self.ptr().add(dst), right.len());
copy_nonoverlapping_reversed(left.as_ptr(), self.ptr(), left.len());
}
}
}

/// Writes all values from `iter` to `dst`.
///
/// # Safety
Expand Down Expand Up @@ -2122,6 +2166,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
112 changes: 111 additions & 1 deletion library/alloc/src/collections/vec_deque/spec_extend.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::iter::TrustedLen;
use core::iter::{Copied, Rev, TrustedLen};
use core::slice;

use super::VecDeque;
Expand Down Expand Up @@ -114,3 +114,113 @@ 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_slice();
// SAFETY: elements in the slice are forgotten after this call
unsafe { prepend_reversed(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();
let slice = iterator.as_slice();
// SAFETY: elements in the slice are forgotten after this call
unsafe { prepend(self, slice) };
iterator.forget_remaining_elements();
}
}

impl<'a, T, A: Allocator> SpecExtendFront<T, Copied<slice::Iter<'a, T>>> for VecDeque<T, A>
where
Copied<slice::Iter<'a, T>>: Iterator<Item = T>,
{
#[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
unsafe { prepend_reversed(self, slice) };
}
}

impl<'a, T, A: Allocator> SpecExtendFront<T, Rev<Copied<slice::Iter<'a, T>>>> for VecDeque<T, A>
where
Rev<Copied<slice::Iter<'a, T>>>: Iterator<Item = T>,
{
#[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
unsafe { prepend(self, slice) };
}
}

/// # Safety
///
/// Elements of `slice` will be copied into the deque, make sure to forget the items 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);
deque.len += slice.len();
}
}

/// # Safety
///
/// Elements of `slice` will be copied into the deque, make sure to forget the items 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);
deque.len += slice.len();
}
}
2 changes: 2 additions & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
#![feature(const_default)]
#![feature(const_eval_select)]
#![feature(const_heap)]
#![feature(copied_into_inner)]
#![feature(core_intrinsics)]
#![feature(deprecated_suggestion)]
#![feature(deref_pure_trait)]
Expand Down Expand Up @@ -134,6 +135,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
2 changes: 2 additions & 0 deletions library/alloctests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#![feature(assert_matches)]
#![feature(char_internals)]
#![feature(char_max_len)]
#![feature(copied_into_inner)]
#![feature(core_intrinsics)]
#![feature(exact_size_is_empty)]
#![feature(extend_one)]
Expand All @@ -32,6 +33,7 @@
#![feature(maybe_uninit_uninit_array_transpose)]
#![feature(ptr_alignment_type)]
#![feature(ptr_internals)]
#![feature(rev_into_inner)]
#![feature(sized_type_properties)]
#![feature(slice_iter_mut_as_mut_slice)]
#![feature(slice_ptr_get)]
Expand Down
74 changes: 74 additions & 0 deletions library/alloctests/tests/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2081,3 +2081,77 @@ fn test_extend_and_prepend_from_within() {
v.extend_from_within(..);
assert_eq!(v.iter().map(|s| &**s).collect::<String>(), "123123123123");
}

#[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_vec_into_iter() {
// trigger 4 code paths: all combinations of prepend and extend_front, wrap and no wrap
let mut v = VecDeque::with_capacity(4);
v.prepend(vec![1, 2, 3]);
assert_eq!(v, [1, 2, 3]);
v.pop_back();
// this should wrap around the physical buffer
v.prepend(vec![-1, 0]);
// check it really wrapped
assert_eq!(v.as_slices(), ([-1].as_slice(), [0, 1, 2].as_slice()));

let mut v = VecDeque::with_capacity(4);
v.extend_front(vec![1, 2, 3]);
assert_eq!(v, [3, 2, 1]);
v.pop_back();
// this should wrap around the physical buffer
v.extend_front(vec![4, 5]);
// check it really wrapped
assert_eq!(v.as_slices(), ([5].as_slice(), [4, 3, 2].as_slice()));
}

#[test]
fn test_extend_front_specialization_copy_slice() {
// trigger 4 code paths: all combinations of prepend and extend_front, wrap and no wrap
let mut v = VecDeque::with_capacity(4);
v.prepend([1, 2, 3].as_slice().iter().copied());
assert_eq!(v, [1, 2, 3]);
v.pop_back();
// this should wrap around the physical buffer
v.prepend([-1, 0].as_slice().iter().copied());
// check it really wrapped
assert_eq!(v.as_slices(), ([-1].as_slice(), [0, 1, 2].as_slice()));

let mut v = VecDeque::with_capacity(4);
v.extend_front([1, 2, 3].as_slice().iter().copied());
assert_eq!(v, [3, 2, 1]);
v.pop_back();
// this should wrap around the physical buffer
v.extend_front([4, 5].as_slice().iter().copied());
// check it really wrapped
assert_eq!(v.as_slices(), ([5].as_slice(), [4, 3, 2].as_slice()));
}
6 changes: 6 additions & 0 deletions library/core/src/iter/adapters/copied.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ impl<I> Copied<I> {
pub(in crate::iter) fn new(it: I) -> Copied<I> {
Copied { it }
}

#[doc(hidden)]
#[unstable(feature = "copied_into_inner", issue = "none")]
pub fn into_inner(self) -> I {
self.it
}
}

fn copy_fold<T: Copy, Acc>(mut f: impl FnMut(Acc, T) -> Acc) -> impl FnMut(Acc, &T) -> Acc {
Expand Down
Loading