implement VecDeque extend_from_within and prepend_from_within, add tests

This commit is contained in:
Antoni Spaanderman 2025-10-23 13:52:55 +02:00
parent 469357eb48
commit 63bb238e5d
No known key found for this signature in database
GPG key ID: AE0B68E552E5DF8C
3 changed files with 521 additions and 0 deletions

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");
}