diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index ac619a42d356d..2ef7f047e034b 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -227,6 +227,78 @@ impl VecDeque { 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 VecDeque { 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(&mut self, src: R) + where + R: RangeBounds, + { + 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(&mut self, src: R) + where + R: RangeBounds, + { + 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); + + unsafe fn spec_prepend_from_within(&mut self, src: Range); +} + +#[cfg(not(no_global_oom_handling))] +impl SpecExtendFromWithin for VecDeque { + default unsafe fn spec_extend_from_within(&mut self, src: Range) { + 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) { + 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 SpecExtendFromWithin for VecDeque { + unsafe fn spec_extend_from_within(&mut self, src: Range) { + 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) { + 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. diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f94f92397bb18..c2649be0558a1 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..0a4a0e0cac4d7 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -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>, + drop: Option<&'a Cell>, + 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::>(), [0, 1, 2, 3]); + + v.extend_from_within(2..); + assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [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::>(), [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::>(), [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::>(), [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::>(), [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::>(), [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::>(), [0, 1, 2, 3]); + + v.prepend_from_within(..2); + assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 0, 1, 2, 3]); + v.prepend_from_within(1..5); + assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [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::>(), [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::>(), [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::>(), [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::>(), [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::>(); + v.truncate_front(5); + v.extend_from_within(4..); + v.prepend_from_within(..2); + assert_eq!(v.iter().map(|s| &**s).collect::(), "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::(), "123123123123"); +}