use alloc::vec::Vec;
use core as std;
#[derive(Debug)]
pub struct Remover<'a, T> {
vec: &'a mut Vec<T>,
unfiltered_start: usize,
unfiltered_end: usize,
}
impl<'a, T> Remover<'a, T> {
#[inline]
pub fn new(vec: &'a mut Vec<T>) -> Remover<'a, T> {
let unfiltered_end = vec.len();
unsafe {
vec.set_len(0);
}
Remover {
vec,
unfiltered_start: 0,
unfiltered_end,
}
}
#[inline]
pub fn index(&self) -> usize {
self.unfiltered_start
}
#[inline]
pub fn current(&self) -> Option<&T> {
if self.unfiltered_start < self.unfiltered_end {
unsafe { Some(&*self.vec.as_ptr().add(self.unfiltered_start)) }
} else {
None
}
}
#[inline]
pub fn current_mut(&mut self) -> Option<&mut T> {
if self.unfiltered_start < self.unfiltered_end {
unsafe { Some(&mut *self.vec.as_mut_ptr().add(self.unfiltered_start)) }
} else {
None
}
}
#[inline]
pub fn as_slices(&self) -> (&[T], &[T]) {
unsafe {
(
&self.vec[..],
std::slice::from_raw_parts(
self.vec.as_ptr().add(self.unfiltered_start),
self.unfiltered_end - self.unfiltered_start,
),
)
}
}
#[inline]
pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
unsafe {
(
std::slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.vec.len()),
std::slice::from_raw_parts_mut(
self.vec.as_mut_ptr().add(self.unfiltered_start),
self.unfiltered_end - self.unfiltered_start,
),
)
}
}
#[inline]
pub fn move_to(&mut self, index: usize) {
assert!(index <= self.unfiltered_end, "Index out of bounds");
let copy_len = index
.checked_sub(self.unfiltered_start)
.expect("Index must be larger than previous index");
unsafe {
let ptr = self.vec.as_mut_ptr();
ptr.add(self.unfiltered_start)
.copy_to(ptr.add(self.vec.len()), copy_len);
self.vec.set_len(self.vec.len() + copy_len);
self.unfiltered_start = index;
}
}
#[inline]
pub fn remove(&mut self) -> T {
assert!(
self.unfiltered_start < self.unfiltered_end,
"Removing out of bounds"
);
unsafe {
let item = self.vec.as_mut_ptr().add(self.unfiltered_start).read();
self.unfiltered_start += 1;
item
}
}
}
impl<'a, T> Drop for Remover<'a, T> {
#[inline]
fn drop(&mut self) {
let ptr = self.vec.as_mut_ptr();
let unfiltered_len = self.unfiltered_end - self.unfiltered_start;
unsafe {
ptr.add(self.unfiltered_start)
.copy_to(ptr.add(self.vec.len()), unfiltered_len);
self.vec.set_len(self.vec.len() + unfiltered_len)
}
}
}
#[cfg(test)]
mod tests {
use super::Remover;
use crate::proputils::prop_eq;
use alloc::{format, vec};
use proptest::prelude::*;
use std::fmt::Debug;
use std::vec::Vec;
#[test]
fn remover_empty() {
let mut items: Vec<usize> = Vec::new();
Remover::new(&mut items);
assert_eq!(items, vec![]);
}
#[test]
fn remover_single() {
let mut items = vec![1];
assert_eq!(Remover::new(&mut items).remove(), 1);
assert_eq!(items, vec![]);
}
#[test]
fn remover_two_first() {
let mut items = vec![1, 2];
assert_eq!(Remover::new(&mut items).remove(), 1);
assert_eq!(items, vec![2]);
}
#[test]
fn remover_two_second() {
let mut items = vec![1, 2];
let mut rem = Remover::new(&mut items);
rem.move_to(1);
assert_eq!(rem.remove(), 2);
drop(rem);
assert_eq!(items, vec![1]);
}
#[test]
fn remover_two_both() {
let mut items = vec![1, 2];
let mut rem = Remover::new(&mut items);
rem.move_to(0);
assert_eq!(rem.remove(), 1);
rem.move_to(1);
assert_eq!(rem.remove(), 2);
drop(rem);
assert_eq!(items, vec![]);
}
#[test]
#[should_panic]
fn remover_two_out_of_order() {
let mut items = vec![1, 2];
let mut remover = Remover::new(&mut items);
remover.move_to(1);
assert_eq!(remover.remove(), 2);
remover.move_to(0)
}
#[test]
#[should_panic]
fn remover_out_of_bounds() {
drop(Remover::new(&mut vec![1, 2]).move_to(3));
}
#[test]
#[should_panic]
fn remover_advance_out_of_bounds() {
Remover::new(&mut vec![1, 2]).move_to(4);
}
#[test]
fn remover_advance_one_past() {
let mut items = vec![1, 2];
let mut rem = Remover::new(&mut items);
rem.move_to(2);
assert_eq!(rem.index(), 2);
}
#[test]
fn remover_slices() {
let mut items = vec![1, 2, 3, 4];
let mut rem = Remover::new(&mut items);
rem.move_to(2);
assert_eq!(rem.remove(), 3);
assert_eq!(rem.as_slices(), (&[1, 2][..], &[4][..]));
assert_eq!(rem.as_mut_slices(), (&mut [1, 2][..], &mut [4][..]));
}
#[test]
fn remover_index() {
let mut items = vec![1, 2, 3, 4];
let mut remover = Remover::new(&mut items);
assert_eq!(remover.index(), 0);
remover.move_to(1);
assert_eq!(remover.remove(), 2);
assert_eq!(remover.index(), 2);
remover.move_to(3);
assert_eq!(remover.index(), 3);
}
struct NaiveRemover<'a, T> {
vec: &'a mut Vec<T>,
old_index: usize,
new_index: usize,
}
impl<'a, T> NaiveRemover<'a, T> {
fn new(vec: &mut Vec<T>) -> NaiveRemover<T> {
NaiveRemover {
vec,
old_index: 0,
new_index: 0,
}
}
fn index(&self) -> usize {
self.old_index
}
fn current(&self) -> Option<&T> {
self.vec.get(self.new_index)
}
fn remove(&mut self) -> T {
let item = self.vec.remove(self.new_index);
self.old_index += 1;
item
}
fn move_to(&mut self, index: usize) {
self.new_index += index.checked_sub(self.old_index).unwrap();
self.old_index = index;
}
fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
self.vec.split_at_mut(self.new_index)
}
fn as_slices(&self) -> (&[T], &[T]) {
self.vec.split_at(self.new_index)
}
}
#[derive(Debug, Clone)]
enum Op {
MoveTo(usize),
Remove,
}
fn prop_strategy() -> impl Strategy<Value = (Vec<u8>, Vec<Op>)> {
proptest::collection::vec(any::<u8>(), 0..100).prop_flat_map(|base| {
proptest::collection::vec(
prop_oneof![(0..base.len() + 1).prop_map(Op::MoveTo), Just(Op::Remove)],
0..100,
)
.prop_map(move |ops| (base.clone(), ops))
})
}
fn check_equals_naive_remover<T: Eq + Clone + Debug>(
base: Vec<T>,
ops: &[Op],
) -> Result<(), TestCaseError> {
let mut model_vec = base.clone();
let mut model = NaiveRemover::new(&mut model_vec);
let mut tested_vec = base;
let mut tested = Remover::new(&mut tested_vec);
ops.iter().try_for_each(|op| {
match op {
&Op::MoveTo(index) => {
prop_eq(|| model.move_to(index), || tested.move_to(index))
}
Op::Remove => prop_eq(|| model.remove(), || tested.remove()),
}?;
prop_assert_eq!(model.current(), tested.current());
prop_assert_eq!(model.index(), tested.index());
prop_assert_eq!(model.as_slices(), tested.as_slices());
prop_assert_eq!(model.as_mut_slices(), tested.as_mut_slices());
Ok(())
})?;
drop(tested);
prop_assert_eq!(model_vec, tested_vec);
Ok(())
}
proptest! {
#[test]
fn equals_naive_remover((base, ops) in prop_strategy()) {
check_equals_naive_remover(base, &ops[..])?
}
}
}