implement VecDeque extend_from_within and prepend_from_within, add tests
This commit is contained in:
parent
469357eb48
commit
63bb238e5d
3 changed files with 521 additions and 0 deletions
|
|
@ -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