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:
commit
53c52a2ae4
3 changed files with 521 additions and 0 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue