From d4ae1207b62dd7c311c95a347e63253ec4f5b786 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Tue, 30 Sep 2025 22:35:30 +0200 Subject: [PATCH] implement VecDeque extend_front and prepend, add tests --- .../alloc/src/collections/vec_deque/mod.rs | 84 +++++++++++++++- .../src/collections/vec_deque/spec_extend.rs | 95 +++++++++++++++++++ library/alloc/src/lib.rs | 1 + library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/vec_deque.rs | 43 +++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index ac619a42d356d..1169e62ce7982 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -47,7 +47,7 @@ pub use self::iter::Iter; mod iter; -use self::spec_extend::SpecExtend; +use self::spec_extend::{SpecExtend, SpecExtendFront}; mod spec_extend; @@ -174,6 +174,21 @@ impl VecDeque { 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 { @@ -1956,6 +1971,73 @@ impl VecDeque { 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>(&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>(&mut self, iter: I) { + >::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 diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index 6c2199135e08a..3e830d2afe676 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -1,3 +1,5 @@ +#[cfg(not(test))] +use core::iter::Rev; use core::iter::TrustedLen; use core::slice; @@ -114,3 +116,96 @@ where } } } + +// Specialization trait used for VecDeque::extend_front +pub(super) trait SpecExtendFront { + #[track_caller] + fn spec_extend_front(&mut self, iter: I); +} + +impl SpecExtendFront for VecDeque +where + I: Iterator, +{ + #[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 SpecExtendFront> for VecDeque { + #[track_caller] + fn spec_extend_front(&mut self, mut iterator: vec::IntoIter) { + let slice = iterator.as_mut_slice(); + slice.reverse(); + unsafe { prepend(self, slice) }; + iterator.forget_remaining_elements(); + } +} + +#[cfg(not(test))] +impl SpecExtendFront>> for VecDeque { + #[track_caller] + fn spec_extend_front(&mut self, iterator: Rev>) { + let mut iterator = iterator.into_inner(); + unsafe { prepend(self, iterator.as_slice()) }; + iterator.forget_remaining_elements(); + } +} + +// impl SpecExtendFront>> for VecDeque +// where +// T: Copy, +// { +// #[track_caller] +// fn spec_extend_front(&mut self, _iter: Copied>) { +// // unsafe { prepend(self, slice) }; +// // reverse in place? +// } +// } + +// impl SpecExtendFront>>> for VecDeque +// where +// T: Copy, +// { +// #[track_caller] +// fn spec_extend_front(&mut self, iter: Rev>>) { +// unsafe { prepend(self, iter.into_inner().it.as_slice()) }; +// } +// } + +/// # Safety +/// +/// `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`. +#[cfg(not(test))] +unsafe fn prepend(deque: &mut VecDeque, 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(); + } +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index fc3266b74793c..481263b57ce17 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -132,6 +132,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)] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 49fb21ef5f3ac..c3935b586ab2b 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -6,6 +6,7 @@ #![feature(char_max_len)] #![feature(cow_is_borrowed)] #![feature(core_intrinsics)] +#![feature(deque_extend_front)] #![feature(downcast_unchecked)] #![feature(exact_size_is_empty)] #![feature(hashmap_internals)] diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index a82906d55e5d0..62bbca2e830d3 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1849,3 +1849,46 @@ fn test_truncate_front() { v.truncate_front(5); assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice())); } + +#[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() { + let mut v = VecDeque::with_capacity(4); + v.prepend(vec![1, 2, 3]); + assert_eq!(v, [1, 2, 3]); + v.pop_front(); + v.prepend((-4..2).collect::>()); + assert_eq!(v, (-4..=3).collect::>()); + v.clear(); + v.extend_front(vec![1, 2, 3]); + assert_eq!(v, [3, 2, 1]); +}