Skip to content

Commit 58e60ac

Browse files
committed
Make VecDeque::from_iter O(1) from vec(_deque)::IntoIter
1 parent f5418b0 commit 58e60ac

File tree

5 files changed

+124
-11
lines changed

5 files changed

+124
-11
lines changed

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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 34 additions & 11 deletions
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,35 @@ impl<T, A: Allocator> VecDeque<T, A> {
586590
VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) }
587591
}
588592

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

27002733
#[stable(feature = "rust1", since = "1.0.0")]
27012734
impl<T> FromIterator<T> for VecDeque<T> {
2702-
#[inline]
27032735
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-
}
2736+
SpecFromIter::spec_from_iter(iter.into_iter())
27142737
}
27152738
}
27162739

Lines changed: 33 additions & 0 deletions
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

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

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

library/alloc/tests/vec_deque.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,3 +1736,29 @@ 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.into_iter());
1745+
1746+
let mut v = VecDeque::with_capacity(13);
1747+
v.extend(0..7);
1748+
check(v.into_iter());
1749+
1750+
fn check(mut it: impl Iterator<Item = i32>) {
1751+
assert_eq!(it.next(), Some(0));
1752+
assert_eq!(it.next(), Some(1));
1753+
let mut v: VecDeque<i32> = it.collect();
1754+
assert_eq!(v.capacity(), 13);
1755+
assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice()));
1756+
v.push_front(7);
1757+
assert_eq!(v.as_slices(), ([7, 2, 3, 4, 5, 6].as_slice(), [].as_slice()));
1758+
v.push_front(8);
1759+
assert_eq!(v.as_slices(), ([8, 7, 2, 3, 4, 5, 6].as_slice(), [].as_slice()));
1760+
v.push_front(9);
1761+
assert_eq!(v.as_slices(), ([9].as_slice(), [8, 7, 2, 3, 4, 5, 6].as_slice()));
1762+
assert_eq!(v.capacity(), 13);
1763+
}
1764+
}

0 commit comments

Comments
 (0)