Rollup merge of #147161 - antonilol:vec-deque-extend-from-within, r=joboet

implement VecDeque extend_from_within and prepend_from_within

Tracking issue: rust-lang/rust#146975
This commit is contained in:
Matthias Krüger 2025-10-31 18:41:48 +01:00 committed by GitHub
commit 53c52a2ae4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 521 additions and 0 deletions

View file

@ -227,6 +227,78 @@ impl<T, A: Allocator> VecDeque<T, A> {
wrap_index(idx.wrapping_sub(subtrahend).wrapping_add(self.capacity()), self.capacity())
}
/// Get source, destination and count (like the arguments to [`ptr::copy_nonoverlapping`])
/// for copying `count` values from index `src` to index `dst`.
/// One of the ranges can wrap around the physical buffer, for this reason 2 triples are returned.
///
/// Use of the word "ranges" specifically refers to `src..src + count` and `dst..dst + count`.
///
/// # Safety
///
/// - Ranges must not overlap: `src.abs_diff(dst) >= count`.
/// - Ranges must be in bounds of the logical buffer: `src + count <= self.capacity()` and `dst + count <= self.capacity()`.
/// - `head` must be in bounds: `head < self.capacity()`.
#[cfg(not(no_global_oom_handling))]
unsafe fn nonoverlapping_ranges(
&mut self,
src: usize,
dst: usize,
count: usize,
head: usize,
) -> [(*const T, *mut T, usize); 2] {
// "`src` and `dst` must be at least as far apart as `count`"
debug_assert!(
src.abs_diff(dst) >= count,
"`src` and `dst` must not overlap. src={src} dst={dst} count={count}",
);
debug_assert!(
src.max(dst) + count <= self.capacity(),
"ranges must be in bounds. src={src} dst={dst} count={count} cap={}",
self.capacity(),
);
let wrapped_src = self.wrap_add(head, src);
let wrapped_dst = self.wrap_add(head, dst);
let room_after_src = self.capacity() - wrapped_src;
let room_after_dst = self.capacity() - wrapped_dst;
let src_wraps = room_after_src < count;
let dst_wraps = room_after_dst < count;
// Wrapping occurs if `capacity` is contained within `wrapped_src..wrapped_src + count` or `wrapped_dst..wrapped_dst + count`.
// Since these two ranges must not overlap as per the safety invariants of this function, only one range can wrap.
debug_assert!(
!(src_wraps && dst_wraps),
"BUG: at most one of src and dst can wrap. src={src} dst={dst} count={count} cap={}",
self.capacity(),
);
unsafe {
let ptr = self.ptr();
let src_ptr = ptr.add(wrapped_src);
let dst_ptr = ptr.add(wrapped_dst);
if src_wraps {
[
(src_ptr, dst_ptr, room_after_src),
(ptr, dst_ptr.add(room_after_src), count - room_after_src),
]
} else if dst_wraps {
[
(src_ptr, dst_ptr, room_after_dst),
(src_ptr.add(room_after_dst), ptr, count - room_after_dst),
]
} else {
[
(src_ptr, dst_ptr, count),
// null pointers are fine as long as the count is 0
(ptr::null(), ptr::null_mut(), 0),
]
}
}
}
/// Copies a contiguous block of memory len long from src to dst
#[inline]
unsafe fn copy(&mut self, src: usize, dst: usize, len: usize) {
@ -2971,6 +3043,222 @@ impl<T: Clone, A: Allocator> VecDeque<T, A> {
self.truncate(new_len);
}
}
/// Clones the elements at the range `src` and appends them to the end.
///
/// # Panics
///
/// Panics if the starting index is greater than the end index
/// or if either index is greater than the length of the vector.
///
/// # Examples
///
/// ```
/// #![feature(deque_extend_front)]
/// use std::collections::VecDeque;
///
/// let mut characters = VecDeque::from(['a', 'b', 'c', 'd', 'e']);
/// characters.extend_from_within(2..);
/// assert_eq!(characters, ['a', 'b', 'c', 'd', 'e', 'c', 'd', 'e']);
///
/// let mut numbers = VecDeque::from([0, 1, 2, 3, 4]);
/// numbers.extend_from_within(..2);
/// assert_eq!(numbers, [0, 1, 2, 3, 4, 0, 1]);
///
/// let mut strings = VecDeque::from([String::from("hello"), String::from("world"), String::from("!")]);
/// strings.extend_from_within(1..=2);
/// assert_eq!(strings, ["hello", "world", "!", "world", "!"]);
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "deque_extend_front", issue = "146975")]
pub fn extend_from_within<R>(&mut self, src: R)
where
R: RangeBounds<usize>,
{
let range = slice::range(src, ..self.len());
self.reserve(range.len());
// SAFETY:
// - `slice::range` guarantees that the given range is valid for indexing self
// - at least `range.len()` additional space is available
unsafe {
self.spec_extend_from_within(range);
}
}
/// Clones the elements at the range `src` and prepends them to the front.
///
/// # Panics
///
/// Panics if the starting index is greater than the end index
/// or if either index is greater than the length of the vector.
///
/// # Examples
///
/// ```
/// #![feature(deque_extend_front)]
/// use std::collections::VecDeque;
///
/// let mut characters = VecDeque::from(['a', 'b', 'c', 'd', 'e']);
/// characters.prepend_from_within(2..);
/// assert_eq!(characters, ['c', 'd', 'e', 'a', 'b', 'c', 'd', 'e']);
///
/// let mut numbers = VecDeque::from([0, 1, 2, 3, 4]);
/// numbers.prepend_from_within(..2);
/// assert_eq!(numbers, [0, 1, 0, 1, 2, 3, 4]);
///
/// let mut strings = VecDeque::from([String::from("hello"), String::from("world"), String::from("!")]);
/// strings.prepend_from_within(1..=2);
/// assert_eq!(strings, ["world", "!", "hello", "world", "!"]);
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "deque_extend_front", issue = "146975")]
pub fn prepend_from_within<R>(&mut self, src: R)
where
R: RangeBounds<usize>,
{
let range = slice::range(src, ..self.len());
self.reserve(range.len());
// SAFETY:
// - `slice::range` guarantees that the given range is valid for indexing self
// - at least `range.len()` additional space is available
unsafe {
self.spec_prepend_from_within(range);
}
}
}
/// Associated functions have the following preconditions:
///
/// - `src` needs to be a valid range: `src.start <= src.end <= self.len()`.
/// - The buffer must have enough spare capacity: `self.capacity() - self.len() >= src.len()`.
#[cfg(not(no_global_oom_handling))]
trait SpecExtendFromWithin {
unsafe fn spec_extend_from_within(&mut self, src: Range<usize>);
unsafe fn spec_prepend_from_within(&mut self, src: Range<usize>);
}
#[cfg(not(no_global_oom_handling))]
impl<T: Clone, A: Allocator> SpecExtendFromWithin for VecDeque<T, A> {
default unsafe fn spec_extend_from_within(&mut self, src: Range<usize>) {
let dst = self.len();
let count = src.end - src.start;
let src = src.start;
unsafe {
// SAFETY:
// - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values.
// - Ranges are in bounds: guaranteed by the caller.
let ranges = self.nonoverlapping_ranges(src, dst, count, self.head);
// `len` is updated after every clone to prevent leaking and
// leave the deque in the right state when a clone implementation panics
for (src, dst, count) in ranges {
for offset in 0..count {
dst.add(offset).write((*src.add(offset)).clone());
self.len += 1;
}
}
}
}
default unsafe fn spec_prepend_from_within(&mut self, src: Range<usize>) {
let dst = 0;
let count = src.end - src.start;
let src = src.start + count;
let new_head = self.wrap_sub(self.head, count);
let cap = self.capacity();
unsafe {
// SAFETY:
// - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values.
// - Ranges are in bounds: guaranteed by the caller.
let ranges = self.nonoverlapping_ranges(src, dst, count, new_head);
// Cloning is done in reverse because we prepend to the front of the deque,
// we can't get holes in the *logical* buffer.
// `head` and `len` are updated after every clone to prevent leaking and
// leave the deque in the right state when a clone implementation panics
// Clone the first range
let (src, dst, count) = ranges[1];
for offset in (0..count).rev() {
dst.add(offset).write((*src.add(offset)).clone());
self.head -= 1;
self.len += 1;
}
// Clone the second range
let (src, dst, count) = ranges[0];
let mut iter = (0..count).rev();
if let Some(offset) = iter.next() {
dst.add(offset).write((*src.add(offset)).clone());
// After the first clone of the second range, wrap `head` around
if self.head == 0 {
self.head = cap;
}
self.head -= 1;
self.len += 1;
// Continue like normal
for offset in iter {
dst.add(offset).write((*src.add(offset)).clone());
self.head -= 1;
self.len += 1;
}
}
}
}
}
#[cfg(not(no_global_oom_handling))]
impl<T: Copy, A: Allocator> SpecExtendFromWithin for VecDeque<T, A> {
unsafe fn spec_extend_from_within(&mut self, src: Range<usize>) {
let dst = self.len();
let count = src.end - src.start;
let src = src.start;
unsafe {
// SAFETY:
// - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values.
// - Ranges are in bounds: guaranteed by the caller.
let ranges = self.nonoverlapping_ranges(src, dst, count, self.head);
for (src, dst, count) in ranges {
ptr::copy_nonoverlapping(src, dst, count);
}
}
// SAFETY:
// - The elements were just initialized by `copy_nonoverlapping`
self.len += count;
}
unsafe fn spec_prepend_from_within(&mut self, src: Range<usize>) {
let dst = 0;
let count = src.end - src.start;
let src = src.start + count;
let new_head = self.wrap_sub(self.head, count);
unsafe {
// SAFETY:
// - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values.
// - Ranges are in bounds: guaranteed by the caller.
let ranges = self.nonoverlapping_ranges(src, dst, count, new_head);
for (src, dst, count) in ranges {
ptr::copy_nonoverlapping(src, dst, count);
}
}
// SAFETY:
// - The elements were just initialized by `copy_nonoverlapping`
self.head = new_head;
self.len += count;
}
}
/// Returns the index in the underlying buffer for a given logical element index.

View file

@ -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)]

View file

@ -1,3 +1,4 @@
use core::cell::Cell;
use core::num::NonZero;
use std::assert_matches::assert_matches;
use std::collections::TryReserveErrorKind::*;
@ -1849,3 +1850,234 @@ 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_from_within() {
let mut v = VecDeque::with_capacity(8);
v.extend(0..6);
v.truncate_front(4);
assert_eq!(v, [2, 3, 4, 5]);
v.extend_from_within(1..4);
assert_eq!(v, [2, 3, 4, 5, 3, 4, 5]);
// check it really wrapped
assert_eq!(v.as_slices(), ([2, 3, 4, 5, 3, 4].as_slice(), [5].as_slice()));
v.extend_from_within(1..=2);
assert_eq!(v, [2, 3, 4, 5, 3, 4, 5, 3, 4]);
v.extend_from_within(..3);
assert_eq!(v, [2, 3, 4, 5, 3, 4, 5, 3, 4, 2, 3, 4]);
}
/// Struct that allows tracking clone and drop calls and can be set to panic on calling clone.
struct CloneTracker<'a> {
id: usize,
// Counters can be set to None if not needed.
clone: Option<&'a Cell<u32>>,
drop: Option<&'a Cell<u32>>,
panic: bool,
}
impl<'a> CloneTracker<'a> {
pub const DUMMY: Self = Self { id: 999, clone: None, drop: None, panic: false };
}
impl<'a> Clone for CloneTracker<'a> {
fn clone(&self) -> Self {
if self.panic {
panic!();
}
if let Some(clone_count) = self.clone {
clone_count.update(|c| c + 1);
}
Self { id: self.id, clone: self.clone, drop: self.drop, panic: false }
}
}
impl<'a> Drop for CloneTracker<'a> {
fn drop(&mut self) {
if let Some(drop_count) = self.drop {
drop_count.update(|c| c + 1);
}
}
}
#[test]
fn test_extend_from_within_clone() {
let clone_counts = [const { Cell::new(0) }; 4];
let mut v = VecDeque::with_capacity(10);
// insert 2 dummy elements to have the buffer wrap later
v.extend([CloneTracker::DUMMY; 2]);
v.extend(clone_counts.iter().enumerate().map(|(id, clone_count)| CloneTracker {
id,
clone: Some(clone_count),
drop: None,
panic: false,
}));
// remove the dummy elements
v.truncate_front(4);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3]);
v.extend_from_within(2..);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3, 2, 3]);
// elements at index 2 and 3 should have been cloned once
assert_eq!(clone_counts.each_ref().map(Cell::get), [0, 0, 1, 1]);
// it is important that the deque wraps because of this operation, we want to test if wrapping is handled correctly
v.extend_from_within(1..5);
// total length is 10, 8 in the first part and 2 in the second part
assert_eq!(v.as_slices().0.len(), 8);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3, 2, 3, 1, 2, 3, 2]);
// the new elements are from indices 1, 2, 3 and 2, those elements should have their clone count
// incremented (clone count at index 2 gets incremented twice so ends up at 3)
assert_eq!(clone_counts.each_ref().map(Cell::get), [0, 1, 3, 2]);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_extend_from_within_clone_panic() {
let clone_counts = [const { Cell::new(0) }; 4];
let drop_count = Cell::new(0);
let mut v = VecDeque::with_capacity(8);
// insert 2 dummy elements to have the buffer wrap later
v.extend([CloneTracker::DUMMY; 2]);
v.extend(clone_counts.iter().enumerate().map(|(id, clone_count)| CloneTracker {
id,
clone: Some(clone_count),
drop: Some(&drop_count),
panic: false,
}));
// remove the dummy elements
v.truncate_front(4);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3]);
// panic after wrapping
v[2].panic = true;
catch_unwind(AssertUnwindSafe(|| {
v.extend_from_within(..);
}))
.unwrap_err();
v[2].panic = false;
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3, 0, 1]);
// the first 2 elements were cloned
assert_eq!(clone_counts.each_ref().map(Cell::get), [1, 1, 0, 0]);
// nothing should have been dropped
assert_eq!(drop_count.get(), 0);
v.truncate_front(2);
assert_eq!(drop_count.get(), 4);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1]);
// panic before wrapping
v[1].panic = true;
catch_unwind(AssertUnwindSafe(|| {
v.extend_from_within(..);
}))
.unwrap_err();
v[1].panic = false;
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 0]);
// only the first element was cloned
assert_eq!(clone_counts.each_ref().map(Cell::get), [2, 1, 0, 0]);
// nothing more should have been dropped
assert_eq!(drop_count.get(), 4);
}
#[test]
fn test_prepend_from_within() {
let mut v = VecDeque::with_capacity(8);
v.extend(0..6);
v.truncate_front(4);
v.prepend_from_within(..=0);
assert_eq!(v.as_slices(), ([2, 2, 3, 4, 5].as_slice(), [].as_slice()));
v.prepend_from_within(2..);
assert_eq!(v.as_slices(), ([3, 4].as_slice(), [5, 2, 2, 3, 4, 5].as_slice()));
v.prepend_from_within(..);
assert_eq!(v, [[3, 4, 5, 2, 2, 3, 4, 5]; 2].as_flattened());
}
#[test]
fn test_prepend_from_within_clone() {
let clone_counts = [const { Cell::new(0) }; 4];
// insert 2 dummy elements to have the buffer wrap later
let mut v = VecDeque::with_capacity(10);
v.extend([CloneTracker::DUMMY; 2]);
v.extend(clone_counts.iter().enumerate().map(|(id, clone_count)| CloneTracker {
id,
clone: Some(clone_count),
drop: None,
panic: false,
}));
// remove the dummy elements
v.truncate_front(4);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3]);
v.prepend_from_within(..2);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 0, 1, 2, 3]);
v.prepend_from_within(1..5);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [1, 0, 1, 2, 0, 1, 0, 1, 2, 3]);
// count the number of each element and subtract one (clone should have been called n-1 times if we have n elements)
// example: 0 appears 3 times so should have been cloned twice, 1 appears 4 times so cloned 3 times, etc
assert_eq!(clone_counts.each_ref().map(Cell::get), [2, 3, 1, 0]);
}
#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_prepend_from_within_clone_panic() {
let clone_counts = [const { Cell::new(0) }; 4];
let drop_count = Cell::new(0);
let mut v = VecDeque::with_capacity(8);
// insert 2 dummy elements to have the buffer wrap later
v.extend([CloneTracker::DUMMY; 2]);
v.extend(clone_counts.iter().enumerate().map(|(id, clone_count)| CloneTracker {
id,
clone: Some(clone_count),
drop: Some(&drop_count),
panic: false,
}));
// remove the dummy elements
v.truncate_front(4);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [0, 1, 2, 3]);
// panic after wrapping
v[1].panic = true;
catch_unwind(AssertUnwindSafe(|| {
v.prepend_from_within(..);
}))
.unwrap_err();
v[1].panic = false;
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [2, 3, 0, 1, 2, 3]);
// the last 2 elements were cloned
assert_eq!(clone_counts.each_ref().map(Cell::get), [0, 0, 1, 1]);
// nothing should have been dropped
assert_eq!(drop_count.get(), 0);
v.truncate_front(2);
assert_eq!(drop_count.get(), 4);
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [2, 3]);
// panic before wrapping
v[0].panic = true;
catch_unwind(AssertUnwindSafe(|| {
v.prepend_from_within(..);
}))
.unwrap_err();
v[0].panic = false;
assert_eq!(v.iter().map(|tr| tr.id).collect::<Vec<_>>(), [3, 2, 3]);
// only the first element was cloned
assert_eq!(clone_counts.each_ref().map(Cell::get), [0, 0, 1, 2]);
// nothing more should have been dropped
assert_eq!(drop_count.get(), 4);
}
#[test]
fn test_extend_and_prepend_from_within() {
let mut v = ('0'..='9').map(String::from).collect::<VecDeque<_>>();
v.truncate_front(5);
v.extend_from_within(4..);
v.prepend_from_within(..2);
assert_eq!(v.iter().map(|s| &**s).collect::<String>(), "56567899");
v.clear();
v.extend(['1', '2', '3'].map(String::from));
v.prepend_from_within(..);
v.extend_from_within(..);
assert_eq!(v.iter().map(|s| &**s).collect::<String>(), "123123123123");
}