Skip to content

Commit e68328b

Browse files
authored
Unrolled build for #146861
Rollup merge of #146861 - antonilol:vec_deque_extend_front, r=joboet add extend_front to VecDeque with specialization like extend ACP: rust-lang/libs-team#658 Tracking issue: #146975 _Text below was written before opening the ACP_ Feature was requested in #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 #146861 (review) - 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.
2 parents c5e283b + 5b96677 commit e68328b

File tree

6 files changed

+307
-2
lines changed

6 files changed

+307
-2
lines changed

library/alloc/src/collections/vec_deque/mod.rs

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub use self::iter::Iter;
5252

5353
mod iter;
5454

55-
use self::spec_extend::SpecExtend;
55+
use self::spec_extend::{SpecExtend, SpecExtendFront};
5656

5757
mod spec_extend;
5858

@@ -179,6 +179,21 @@ impl<T, A: Allocator> VecDeque<T, A> {
179179
self.len += 1;
180180
}
181181

182+
/// Prepends an element to the buffer.
183+
///
184+
/// # Safety
185+
///
186+
/// May only be called if `deque.len() < deque.capacity()`
187+
#[inline]
188+
unsafe fn push_front_unchecked(&mut self, element: T) {
189+
self.head = self.wrap_sub(self.head, 1);
190+
// SAFETY: Because of the precondition, it's guaranteed that there is space
191+
// in the logical array before the first element (where self.head is now).
192+
unsafe { self.buffer_write(self.head, element) };
193+
// This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`.
194+
self.len += 1;
195+
}
196+
182197
/// Moves an element out of the buffer
183198
#[inline]
184199
unsafe fn buffer_read(&mut self, off: usize) -> T {
@@ -505,6 +520,35 @@ impl<T, A: Allocator> VecDeque<T, A> {
505520
}
506521
}
507522

523+
/// Copies all values from `src` to `dst` in reversed order, wrapping around if needed.
524+
/// Assumes capacity is sufficient.
525+
/// Equivalent to calling [`VecDeque::copy_slice`] with a [reversed](https://doc.rust-lang.org/std/primitive.slice.html#method.reverse) slice.
526+
#[inline]
527+
unsafe fn copy_slice_reversed(&mut self, dst: usize, src: &[T]) {
528+
/// # Safety
529+
///
530+
/// See [`ptr::copy_nonoverlapping`].
531+
unsafe fn copy_nonoverlapping_reversed<T>(src: *const T, dst: *mut T, count: usize) {
532+
for i in 0..count {
533+
unsafe { ptr::copy_nonoverlapping(src.add(count - 1 - i), dst.add(i), 1) };
534+
}
535+
}
536+
537+
debug_assert!(src.len() <= self.capacity());
538+
let head_room = self.capacity() - dst;
539+
if src.len() <= head_room {
540+
unsafe {
541+
copy_nonoverlapping_reversed(src.as_ptr(), self.ptr().add(dst), src.len());
542+
}
543+
} else {
544+
let (left, right) = src.split_at(src.len() - head_room);
545+
unsafe {
546+
copy_nonoverlapping_reversed(right.as_ptr(), self.ptr().add(dst), right.len());
547+
copy_nonoverlapping_reversed(left.as_ptr(), self.ptr(), left.len());
548+
}
549+
}
550+
}
551+
508552
/// Writes all values from `iter` to `dst`.
509553
///
510554
/// # Safety
@@ -2122,6 +2166,73 @@ impl<T, A: Allocator> VecDeque<T, A> {
21222166
unsafe { self.buffer_write(self.to_physical_idx(len), value) }
21232167
}
21242168

2169+
/// Prepends all contents of the iterator to the front of the deque.
2170+
/// The order of the contents is preserved.
2171+
///
2172+
/// To get behavior like [`append`][VecDeque::append] where elements are moved
2173+
/// from the other collection to this one, use `self.prepend(other.drain(..))`.
2174+
///
2175+
/// # Examples
2176+
///
2177+
/// ```
2178+
/// #![feature(deque_extend_front)]
2179+
/// use std::collections::VecDeque;
2180+
///
2181+
/// let mut deque = VecDeque::from([4, 5, 6]);
2182+
/// deque.prepend([1, 2, 3]);
2183+
/// assert_eq!(deque, [1, 2, 3, 4, 5, 6]);
2184+
/// ```
2185+
///
2186+
/// Move values between collections like [`append`][VecDeque::append] does but prepend to the front:
2187+
///
2188+
/// ```
2189+
/// #![feature(deque_extend_front)]
2190+
/// use std::collections::VecDeque;
2191+
///
2192+
/// let mut deque1 = VecDeque::from([4, 5, 6]);
2193+
/// let mut deque2 = VecDeque::from([1, 2, 3]);
2194+
/// deque1.prepend(deque2.drain(..));
2195+
/// assert_eq!(deque1, [1, 2, 3, 4, 5, 6]);
2196+
/// assert!(deque2.is_empty());
2197+
/// ```
2198+
#[unstable(feature = "deque_extend_front", issue = "146975")]
2199+
#[track_caller]
2200+
pub fn prepend<I: IntoIterator<Item = T, IntoIter: DoubleEndedIterator>>(&mut self, other: I) {
2201+
self.extend_front(other.into_iter().rev())
2202+
}
2203+
2204+
/// Prepends all contents of the iterator to the front of the deque,
2205+
/// as if [`push_front`][VecDeque::push_front] was called repeatedly with
2206+
/// the values yielded by the iterator.
2207+
///
2208+
/// # Examples
2209+
///
2210+
/// ```
2211+
/// #![feature(deque_extend_front)]
2212+
/// use std::collections::VecDeque;
2213+
///
2214+
/// let mut deque = VecDeque::from([4, 5, 6]);
2215+
/// deque.extend_front([3, 2, 1]);
2216+
/// assert_eq!(deque, [1, 2, 3, 4, 5, 6]);
2217+
/// ```
2218+
///
2219+
/// This behaves like [`push_front`][VecDeque::push_front] was called repeatedly:
2220+
///
2221+
/// ```
2222+
/// use std::collections::VecDeque;
2223+
///
2224+
/// let mut deque = VecDeque::from([4, 5, 6]);
2225+
/// for v in [3, 2, 1] {
2226+
/// deque.push_front(v);
2227+
/// }
2228+
/// assert_eq!(deque, [1, 2, 3, 4, 5, 6]);
2229+
/// ```
2230+
#[unstable(feature = "deque_extend_front", issue = "146975")]
2231+
#[track_caller]
2232+
pub fn extend_front<I: IntoIterator<Item = T>>(&mut self, iter: I) {
2233+
<Self as SpecExtendFront<T, I::IntoIter>>::spec_extend_front(self, iter.into_iter());
2234+
}
2235+
21252236
#[inline]
21262237
fn is_contiguous(&self) -> bool {
21272238
// Do the calculation like this to avoid overflowing if len + head > usize::MAX

library/alloc/src/collections/vec_deque/spec_extend.rs

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::iter::TrustedLen;
1+
use core::iter::{Copied, Rev, TrustedLen};
22
use core::slice;
33

44
use super::VecDeque;
@@ -114,3 +114,113 @@ where
114114
}
115115
}
116116
}
117+
118+
// Specialization trait used for VecDeque::extend_front
119+
pub(super) trait SpecExtendFront<T, I> {
120+
#[track_caller]
121+
fn spec_extend_front(&mut self, iter: I);
122+
}
123+
124+
impl<T, I, A: Allocator> SpecExtendFront<T, I> for VecDeque<T, A>
125+
where
126+
I: Iterator<Item = T>,
127+
{
128+
#[track_caller]
129+
default fn spec_extend_front(&mut self, mut iter: I) {
130+
// This function should be the moral equivalent of:
131+
//
132+
// for item in iter {
133+
// self.push_front(item);
134+
// }
135+
136+
while let Some(element) = iter.next() {
137+
let (lower, _) = iter.size_hint();
138+
self.reserve(lower.saturating_add(1));
139+
140+
// SAFETY: We just reserved space for at least one element.
141+
unsafe { self.push_front_unchecked(element) };
142+
143+
// Inner loop to avoid repeatedly calling `reserve`.
144+
while self.len < self.capacity() {
145+
let Some(element) = iter.next() else {
146+
return;
147+
};
148+
// SAFETY: The loop condition guarantees that `self.len() < self.capacity()`.
149+
unsafe { self.push_front_unchecked(element) };
150+
}
151+
}
152+
}
153+
}
154+
155+
#[cfg(not(test))]
156+
impl<T, A: Allocator> SpecExtendFront<T, vec::IntoIter<T>> for VecDeque<T, A> {
157+
#[track_caller]
158+
fn spec_extend_front(&mut self, mut iterator: vec::IntoIter<T>) {
159+
let slice = iterator.as_slice();
160+
// SAFETY: elements in the slice are forgotten after this call
161+
unsafe { prepend_reversed(self, slice) };
162+
iterator.forget_remaining_elements();
163+
}
164+
}
165+
166+
#[cfg(not(test))]
167+
impl<T, A: Allocator> SpecExtendFront<T, Rev<vec::IntoIter<T>>> for VecDeque<T, A> {
168+
#[track_caller]
169+
fn spec_extend_front(&mut self, iterator: Rev<vec::IntoIter<T>>) {
170+
let mut iterator = iterator.into_inner();
171+
let slice = iterator.as_slice();
172+
// SAFETY: elements in the slice are forgotten after this call
173+
unsafe { prepend(self, slice) };
174+
iterator.forget_remaining_elements();
175+
}
176+
}
177+
178+
impl<'a, T, A: Allocator> SpecExtendFront<T, Copied<slice::Iter<'a, T>>> for VecDeque<T, A>
179+
where
180+
Copied<slice::Iter<'a, T>>: Iterator<Item = T>,
181+
{
182+
#[track_caller]
183+
fn spec_extend_front(&mut self, iter: Copied<slice::Iter<'a, T>>) {
184+
let slice = iter.into_inner().as_slice();
185+
// SAFETY: T is Copy because Copied<slice::Iter<'a, T>> is Iterator
186+
unsafe { prepend_reversed(self, slice) };
187+
}
188+
}
189+
190+
impl<'a, T, A: Allocator> SpecExtendFront<T, Rev<Copied<slice::Iter<'a, T>>>> for VecDeque<T, A>
191+
where
192+
Rev<Copied<slice::Iter<'a, T>>>: Iterator<Item = T>,
193+
{
194+
#[track_caller]
195+
fn spec_extend_front(&mut self, iter: Rev<Copied<slice::Iter<'a, T>>>) {
196+
let slice = iter.into_inner().into_inner().as_slice();
197+
// SAFETY: T is Copy because Rev<Copied<slice::Iter<'a, T>>> is Iterator
198+
unsafe { prepend(self, slice) };
199+
}
200+
}
201+
202+
/// # Safety
203+
///
204+
/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
205+
unsafe fn prepend<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
206+
deque.reserve(slice.len());
207+
208+
unsafe {
209+
deque.head = deque.wrap_sub(deque.head, slice.len());
210+
deque.copy_slice(deque.head, slice);
211+
deque.len += slice.len();
212+
}
213+
}
214+
215+
/// # Safety
216+
///
217+
/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`.
218+
unsafe fn prepend_reversed<T, A: Allocator>(deque: &mut VecDeque<T, A>, slice: &[T]) {
219+
deque.reserve(slice.len());
220+
221+
unsafe {
222+
deque.head = deque.wrap_sub(deque.head, slice.len());
223+
deque.copy_slice_reversed(deque.head, slice);
224+
deque.len += slice.len();
225+
}
226+
}

library/alloc/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
#![feature(const_default)]
107107
#![feature(const_eval_select)]
108108
#![feature(const_heap)]
109+
#![feature(copied_into_inner)]
109110
#![feature(core_intrinsics)]
110111
#![feature(deprecated_suggestion)]
111112
#![feature(deref_pure_trait)]
@@ -134,6 +135,7 @@
134135
#![feature(ptr_alignment_type)]
135136
#![feature(ptr_internals)]
136137
#![feature(ptr_metadata)]
138+
#![feature(rev_into_inner)]
137139
#![feature(set_ptr_value)]
138140
#![feature(sized_type_properties)]
139141
#![feature(slice_from_ptr_range)]

library/alloctests/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![feature(assert_matches)]
2121
#![feature(char_internals)]
2222
#![feature(char_max_len)]
23+
#![feature(copied_into_inner)]
2324
#![feature(core_intrinsics)]
2425
#![feature(exact_size_is_empty)]
2526
#![feature(extend_one)]
@@ -32,6 +33,7 @@
3233
#![feature(maybe_uninit_uninit_array_transpose)]
3334
#![feature(ptr_alignment_type)]
3435
#![feature(ptr_internals)]
36+
#![feature(rev_into_inner)]
3537
#![feature(sized_type_properties)]
3638
#![feature(slice_iter_mut_as_mut_slice)]
3739
#![feature(slice_ptr_get)]

library/alloctests/tests/vec_deque.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,3 +2081,77 @@ fn test_extend_and_prepend_from_within() {
20812081
v.extend_from_within(..);
20822082
assert_eq!(v.iter().map(|s| &**s).collect::<String>(), "123123123123");
20832083
}
2084+
2085+
#[test]
2086+
fn test_extend_front() {
2087+
let mut v = VecDeque::new();
2088+
v.extend_front(0..3);
2089+
assert_eq!(v, [2, 1, 0]);
2090+
v.extend_front(3..6);
2091+
assert_eq!(v, [5, 4, 3, 2, 1, 0]);
2092+
v.prepend([1; 4]);
2093+
assert_eq!(v, [1, 1, 1, 1, 5, 4, 3, 2, 1, 0]);
2094+
2095+
let mut v = VecDeque::with_capacity(8);
2096+
let cap = v.capacity();
2097+
v.extend(0..4);
2098+
v.truncate_front(2);
2099+
v.extend_front(4..8);
2100+
assert_eq!(v.as_slices(), ([7, 6].as_slice(), [5, 4, 2, 3].as_slice()));
2101+
assert_eq!(v.capacity(), cap);
2102+
2103+
let mut v = VecDeque::new();
2104+
v.extend_front([]);
2105+
v.extend_front(None);
2106+
v.extend_front(vec![]);
2107+
v.prepend([]);
2108+
v.prepend(None);
2109+
v.prepend(vec![]);
2110+
assert_eq!(v.capacity(), 0);
2111+
v.extend_front(Some(123));
2112+
assert_eq!(v, [123]);
2113+
}
2114+
2115+
#[test]
2116+
fn test_extend_front_specialization_vec_into_iter() {
2117+
// trigger 4 code paths: all combinations of prepend and extend_front, wrap and no wrap
2118+
let mut v = VecDeque::with_capacity(4);
2119+
v.prepend(vec![1, 2, 3]);
2120+
assert_eq!(v, [1, 2, 3]);
2121+
v.pop_back();
2122+
// this should wrap around the physical buffer
2123+
v.prepend(vec![-1, 0]);
2124+
// check it really wrapped
2125+
assert_eq!(v.as_slices(), ([-1].as_slice(), [0, 1, 2].as_slice()));
2126+
2127+
let mut v = VecDeque::with_capacity(4);
2128+
v.extend_front(vec![1, 2, 3]);
2129+
assert_eq!(v, [3, 2, 1]);
2130+
v.pop_back();
2131+
// this should wrap around the physical buffer
2132+
v.extend_front(vec![4, 5]);
2133+
// check it really wrapped
2134+
assert_eq!(v.as_slices(), ([5].as_slice(), [4, 3, 2].as_slice()));
2135+
}
2136+
2137+
#[test]
2138+
fn test_extend_front_specialization_copy_slice() {
2139+
// trigger 4 code paths: all combinations of prepend and extend_front, wrap and no wrap
2140+
let mut v = VecDeque::with_capacity(4);
2141+
v.prepend([1, 2, 3].as_slice().iter().copied());
2142+
assert_eq!(v, [1, 2, 3]);
2143+
v.pop_back();
2144+
// this should wrap around the physical buffer
2145+
v.prepend([-1, 0].as_slice().iter().copied());
2146+
// check it really wrapped
2147+
assert_eq!(v.as_slices(), ([-1].as_slice(), [0, 1, 2].as_slice()));
2148+
2149+
let mut v = VecDeque::with_capacity(4);
2150+
v.extend_front([1, 2, 3].as_slice().iter().copied());
2151+
assert_eq!(v, [3, 2, 1]);
2152+
v.pop_back();
2153+
// this should wrap around the physical buffer
2154+
v.extend_front([4, 5].as_slice().iter().copied());
2155+
// check it really wrapped
2156+
assert_eq!(v.as_slices(), ([5].as_slice(), [4, 3, 2].as_slice()));
2157+
}

library/core/src/iter/adapters/copied.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ impl<I> Copied<I> {
2424
pub(in crate::iter) fn new(it: I) -> Copied<I> {
2525
Copied { it }
2626
}
27+
28+
#[doc(hidden)]
29+
#[unstable(feature = "copied_into_inner", issue = "none")]
30+
pub fn into_inner(self) -> I {
31+
self.it
32+
}
2733
}
2834

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

0 commit comments

Comments
 (0)