Skip to content

Commit 5156fbd

Browse files
authored
Rollup merge of #105453 - scottmcm:vecdeque_from_iter, r=the8472
Make `VecDeque::from_iter` O(1) from `vec(_deque)::IntoIter` As suggested in #105046 (comment) by r? ``@the8472`` `Vec` & `VecDeque`'s `IntoIter`s own the allocations, and even if advanced can be turned into `VecDeque`s in O(1). This is just a specialization, not an API or doc commitment, so I don't think it needs an FCP.
2 parents c44326e + 6648134 commit 5156fbd

File tree

5 files changed

+139
-11
lines changed

5 files changed

+139
-11
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ impl<T, A: Allocator> IntoIter<T, A> {
2525
pub(super) fn new(inner: VecDeque<T, A>) -> Self {
2626
IntoIter { inner }
2727
}
28+
29+
pub(super) fn into_vecdeque(self) -> VecDeque<T, A> {
30+
self.inner
31+
}
2832
}
2933

3034
#[stable(feature = "collection_debug", since = "1.17.0")]

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

+37-11
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ use self::spec_extend::SpecExtend;
5555

5656
mod spec_extend;
5757

58+
use self::spec_from_iter::SpecFromIter;
59+
60+
mod spec_from_iter;
61+
5862
#[cfg(test)]
5963
mod tests;
6064

@@ -586,6 +590,38 @@ impl<T, A: Allocator> VecDeque<T, A> {
586590
VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) }
587591
}
588592

593+
/// Creates a `VecDeque` from a raw allocation, when the initialized
594+
/// part of that allocation forms a *contiguous* subslice thereof.
595+
///
596+
/// For use by `vec::IntoIter::into_vecdeque`
597+
///
598+
/// # Safety
599+
///
600+
/// All the usual requirements on the allocated memory like in
601+
/// `Vec::from_raw_parts_in`, but takes a *range* of elements that are
602+
/// initialized rather than only supporting `0..len`. Requires that
603+
/// `initialized.start` ≤ `initialized.end` ≤ `capacity`.
604+
#[inline]
605+
pub(crate) unsafe fn from_contiguous_raw_parts_in(
606+
ptr: *mut T,
607+
initialized: Range<usize>,
608+
capacity: usize,
609+
alloc: A,
610+
) -> Self {
611+
debug_assert!(initialized.start <= initialized.end);
612+
debug_assert!(initialized.end <= capacity);
613+
614+
// SAFETY: Our safety precondition guarantees the range length won't wrap,
615+
// and that the allocation is valid for use in `RawVec`.
616+
unsafe {
617+
VecDeque {
618+
head: initialized.start,
619+
len: initialized.end.unchecked_sub(initialized.start),
620+
buf: RawVec::from_raw_parts_in(ptr, capacity, alloc),
621+
}
622+
}
623+
}
624+
589625
/// Provides a reference to the element at the given index.
590626
///
591627
/// Element at index 0 is the front of the queue.
@@ -2699,18 +2735,8 @@ impl<T, A: Allocator> IndexMut<usize> for VecDeque<T, A> {
26992735

27002736
#[stable(feature = "rust1", since = "1.0.0")]
27012737
impl<T> FromIterator<T> for VecDeque<T> {
2702-
#[inline]
27032738
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> VecDeque<T> {
2704-
// Since converting is O(1) now, might as well re-use that logic
2705-
// (including things like the `vec::IntoIter`→`Vec` specialization)
2706-
// especially as that could save us some monomorphiziation work
2707-
// if one uses the same iterators (like slice ones) with both.
2708-
return from_iter_via_vec(iter.into_iter());
2709-
2710-
#[inline]
2711-
fn from_iter_via_vec<U>(iter: impl Iterator<Item = U>) -> VecDeque<U> {
2712-
Vec::from_iter(iter).into()
2713-
}
2739+
SpecFromIter::spec_from_iter(iter.into_iter())
27142740
}
27152741
}
27162742

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use super::{IntoIter, VecDeque};
2+
3+
/// Specialization trait used for `VecDeque::from_iter`
4+
pub(super) trait SpecFromIter<T, I> {
5+
fn spec_from_iter(iter: I) -> Self;
6+
}
7+
8+
impl<T, I> SpecFromIter<T, I> for VecDeque<T>
9+
where
10+
I: Iterator<Item = T>,
11+
{
12+
default fn spec_from_iter(iterator: I) -> Self {
13+
// Since converting is O(1) now, just re-use the `Vec` logic for
14+
// anything where we can't do something extra-special for `VecDeque`,
15+
// especially as that could save us some monomorphiziation work
16+
// if one uses the same iterators (like slice ones) with both.
17+
crate::vec::Vec::from_iter(iterator).into()
18+
}
19+
}
20+
21+
impl<T> SpecFromIter<T, crate::vec::IntoIter<T>> for VecDeque<T> {
22+
#[inline]
23+
fn spec_from_iter(iterator: crate::vec::IntoIter<T>) -> Self {
24+
iterator.into_vecdeque()
25+
}
26+
}
27+
28+
impl<T> SpecFromIter<T, IntoIter<T>> for VecDeque<T> {
29+
#[inline]
30+
fn spec_from_iter(iterator: IntoIter<T>) -> Self {
31+
iterator.into_vecdeque()
32+
}
33+
}

library/alloc/src/vec/into_iter.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#[cfg(not(no_global_oom_handling))]
22
use super::AsVecIntoIter;
33
use crate::alloc::{Allocator, Global};
4+
#[cfg(not(no_global_oom_handling))]
5+
use crate::collections::VecDeque;
46
use crate::raw_vec::RawVec;
57
use core::array;
68
use core::fmt;
@@ -132,6 +134,33 @@ impl<T, A: Allocator> IntoIter<T, A> {
132134
pub(crate) fn forget_remaining_elements(&mut self) {
133135
self.ptr = self.end;
134136
}
137+
138+
#[cfg(not(no_global_oom_handling))]
139+
#[inline]
140+
pub(crate) fn into_vecdeque(self) -> VecDeque<T, A> {
141+
// Keep our `Drop` impl from dropping the elements and the allocator
142+
let mut this = ManuallyDrop::new(self);
143+
144+
// SAFETY: This allocation originally came from a `Vec`, so it passes
145+
// all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`,
146+
// so the `sub_ptr`s below cannot wrap, and will produce a well-formed
147+
// range. `end` ≤ `buf + cap`, so the range will be in-bounds.
148+
// Taking `alloc` is ok because nothing else is going to look at it,
149+
// since our `Drop` impl isn't going to run so there's no more code.
150+
unsafe {
151+
let buf = this.buf.as_ptr();
152+
let initialized = if T::IS_ZST {
153+
// All the pointers are the same for ZSTs, so it's fine to
154+
// say that they're all at the beginning of the "allocation".
155+
0..this.len()
156+
} else {
157+
this.ptr.sub_ptr(buf)..this.end.sub_ptr(buf)
158+
};
159+
let cap = this.cap;
160+
let alloc = ManuallyDrop::take(&mut this.alloc);
161+
VecDeque::from_contiguous_raw_parts_in(buf, initialized, cap, alloc)
162+
}
163+
}
135164
}
136165

137166
#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")]

library/alloc/tests/vec_deque.rs

+36
Original file line numberDiff line numberDiff line change
@@ -1736,3 +1736,39 @@ fn test_resize_keeps_reserved_space_from_item() {
17361736
d.resize(1, v);
17371737
assert_eq!(d[0].capacity(), 1234);
17381738
}
1739+
1740+
#[test]
1741+
fn test_collect_from_into_iter_keeps_allocation() {
1742+
let mut v = Vec::with_capacity(13);
1743+
v.extend(0..7);
1744+
check(v.as_ptr(), v.last().unwrap(), v.into_iter());
1745+
1746+
let mut v = VecDeque::with_capacity(13);
1747+
v.extend(0..7);
1748+
check(&v[0], &v[v.len() - 1], v.into_iter());
1749+
1750+
fn check(buf: *const i32, last: *const i32, mut it: impl Iterator<Item = i32>) {
1751+
assert_eq!(it.next(), Some(0));
1752+
assert_eq!(it.next(), Some(1));
1753+
1754+
let mut v: VecDeque<i32> = it.collect();
1755+
assert_eq!(v.capacity(), 13);
1756+
assert_eq!(v.as_slices().0.as_ptr(), buf.wrapping_add(2));
1757+
assert_eq!(&v[v.len() - 1] as *const _, last);
1758+
1759+
assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice()));
1760+
v.push_front(7);
1761+
assert_eq!(v.as_slices(), ([7, 2, 3, 4, 5, 6].as_slice(), [].as_slice()));
1762+
v.push_front(8);
1763+
assert_eq!(v.as_slices(), ([8, 7, 2, 3, 4, 5, 6].as_slice(), [].as_slice()));
1764+
1765+
// Now that we've adding thing in place of the two that we removed from
1766+
// the front of the iterator, we're back to matching the buffer pointer.
1767+
assert_eq!(v.as_slices().0.as_ptr(), buf);
1768+
assert_eq!(&v[v.len() - 1] as *const _, last);
1769+
1770+
v.push_front(9);
1771+
assert_eq!(v.as_slices(), ([9].as_slice(), [8, 7, 2, 3, 4, 5, 6].as_slice()));
1772+
assert_eq!(v.capacity(), 13);
1773+
}
1774+
}

0 commit comments

Comments
 (0)