Rollup merge of #146861 - antonilol:vec_deque_extend_front, r=joboet
add extend_front to VecDeque with specialization like extend ACP: https://github.com/rust-lang/libs-team/issues/658 Tracking issue: rust-lang/rust#146975 _Text below was written before opening the ACP_ Feature was requested in rust-lang/rust#69939, I recently also needed it so decided to implement it as my first contribution to the Rust standard library. I plan on doing more but wanted to start with a small change. Some questions I had (both on implementation and design) with answers: - Q: `extend` allows iterators that yield `&T` where `T` is `Clone`, should extend_front do too? A: No, users can use [`copied`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.copied) and/or [`cloned`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.cloned). - Q: Does this need a whole new trait like Extend or only a method on `VecDeque`? A: No, see ACP. - Q: How do I deal with all the code duplication? Most code is similar to that of `extend`, maybe there is a nice way to factor out the code around `push_unchecked`/`push_front_unchecked`. Will come back to this later. - Q: Why are certain things behind feature gates, `cfg(not(test))` like `vec::IntoIter` here and `cfg(not(no_global_oom_handling))` like `Vec::extend_from_within`? (I am also looking at implementing `VecDeque::extend_from_within`) A: See https://github.com/rust-lang/rust/pull/146861#pullrequestreview-3250163369 - Q: Should `extend_front` act like repeated pushes to the front of the queue? This reverses the order of the elements. Doing it different might incur an extra move if the iterator length is not known up front (where do you start placing elements in the buffer?). A: `extend_front` acts like repeated pushes, `prepend` preserves the element order, see ACP or tracking issue.
This commit is contained in:
commit
f7f128fd48
6 changed files with 307 additions and 2 deletions
|
|
@ -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;
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use core::iter::TrustedLen;
|
||||
use core::iter::{Copied, Rev, TrustedLen};
|
||||
use core::slice;
|
||||
|
||||
use super::VecDeque;
|
||||
|
|
@ -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> {
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue