Skip to main content

scanmut/
remover.rs

1use alloc::vec::Vec;
2use core as std;
3
4/// An object that can remove multiple elements from a [Vec] in a single scan through the [Vec].
5///
6/// The [Remover] has a position in the [Vec] at which it inserts, called the index. Initially, the
7/// index is at the **start** of the [Vec]. This index can only be moved further towards end of the
8/// [Vec], meaning that the indices of the removed objects must be monotonically increasing
9/// (i.e. the next index must be larger than the previous index).
10///
11/// Dropping the [Remover] without using the entire insertion capacity has a runtime cost
12/// proportional to the size of the merged elements. Dropping after using the entire insertion
13/// capacity has no runtime cost.
14///
15/// Leaking the [Remover] (through [std::mem::forget] or similar) may leak some items of the [Vec]
16/// and will leave the [Vec] in an unspecified but valid state
17///
18/// # Example
19///
20/// ```
21/// use scanmut::Remover;
22///
23/// let mut items = vec!['a', 'b', 'c'];
24///
25/// let mut remover = Remover::new(&mut items);
26///
27/// assert_eq!(remover.index(), 0); // Initial index is at start of the vec
28/// assert_eq!(remover.remove(), 'a');
29///
30/// assert_eq!(remover.index(), 1);  // Removing an element increments the index
31/// remover.move_to(2);
32///
33/// assert_eq!(remover.index(), 2);
34/// assert_eq!(remover.remove(), 'c');
35///
36/// assert_eq!(remover.index(), 3);
37///
38/// drop(remover);
39///
40/// assert_eq!(items, ['b']);
41/// ```
42///
43/// As calling [Remover::remove] will increment the index, it can be used to remove items in a row:
44///
45/// ```
46/// use scanmut::Remover;
47///
48/// let mut items = vec!['a', 'b', 'c', 'd'];
49///
50/// let mut remover = Remover::new(&mut items);
51///
52/// remover.move_to(1);
53/// assert_eq!(remover.remove(), 'b');
54/// assert_eq!(remover.remove(), 'c');
55/// drop(remover);
56///
57/// assert_eq!(items, ['a', 'd']);
58/// ```
59#[derive(Debug)]
60pub struct Remover<'a, T> {
61    // The remover works by keeping a "gap" of uninitialized values. This gap is initially
62    // of length zero and at the start of the vec, and moves up through the vec as we remove elements,
63    // increasing in size by one each time we remove an element.
64    //
65    // The fields have the following invariant:
66    //     vec.len() <= unfiltered_start <= unfiltered_end <= vec.capacity()
67    //
68    // Where
69    //     [0,vec.len()[                      are the filtered elements
70    //     [vec.len(),unfiltered_start[       is a gap of uninitialized values
71    //     [unfiltered_start,unfiltered_end[  are the unfiltered elements
72    vec: &'a mut Vec<T>,
73    unfiltered_start: usize,
74    unfiltered_end: usize,
75}
76
77impl<'a, T> Remover<'a, T> {
78    /// Create a new new `Remover` for removing elements from a `Vec`.
79    #[inline]
80    pub fn new(vec: &'a mut Vec<T>) -> Remover<'a, T> {
81        let unfiltered_end = vec.len();
82
83        unsafe {
84            vec.set_len(0);
85        }
86
87        Remover {
88            vec,
89            unfiltered_start: 0,
90            unfiltered_end,
91        }
92    }
93
94    /// The current index of this remover.
95    #[inline]
96    pub fn index(&self) -> usize {
97        self.unfiltered_start
98    }
99
100    /// Returns a reference to the item at the current index or `None` if the remover is past the
101    /// end of the underlying `Vec`.
102    #[inline]
103    pub fn current(&self) -> Option<&T> {
104        if self.unfiltered_start < self.unfiltered_end {
105            unsafe { Some(&*self.vec.as_ptr().add(self.unfiltered_start)) }
106        } else {
107            None
108        }
109    }
110
111    /// Returns a mutable reference to the item at the current index or `None` if the remover is
112    /// past the end of the underlying `Vec`.
113    #[inline]
114    pub fn current_mut(&mut self) -> Option<&mut T> {
115        if self.unfiltered_start < self.unfiltered_end {
116            unsafe { Some(&mut *self.vec.as_mut_ptr().add(self.unfiltered_start)) }
117        } else {
118            None
119        }
120    }
121
122    /// Returns a pair of slices representing the current state of the `Vec` being removed from.
123    ///
124    /// The first slice is the part of the `Vec` below the index. The second slice is the part of
125    /// the `Vec` above or equal to the index.
126    #[inline]
127    pub fn as_slices(&self) -> (&[T], &[T]) {
128        unsafe {
129            (
130                &self.vec[..],
131                std::slice::from_raw_parts(
132                    self.vec.as_ptr().add(self.unfiltered_start),
133                    self.unfiltered_end - self.unfiltered_start,
134                ),
135            )
136        }
137    }
138
139    /// Returns a pair of mutable slices representing the current state of the `Vec` being removed
140    /// from.
141    ///
142    /// The first slice is the part of the `Vec` below the index. The second slice is the part of
143    /// the `Vec` above or equal to the index.
144    #[inline]
145    pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
146        unsafe {
147            (
148                std::slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.vec.len()),
149                std::slice::from_raw_parts_mut(
150                    self.vec.as_mut_ptr().add(self.unfiltered_start),
151                    self.unfiltered_end - self.unfiltered_start,
152                ),
153            )
154        }
155    }
156
157    /// Moves this `Remover` to the given index in the `Vec`.
158    ///
159    /// # Panics
160    ///
161    /// Panics if the index is less than or equal to the current index.
162    #[inline]
163    pub fn move_to(&mut self, index: usize) {
164        assert!(index <= self.unfiltered_end, "Index out of bounds");
165
166        let copy_len = index
167            .checked_sub(self.unfiltered_start)
168            .expect("Index must be larger than previous index");
169
170        unsafe {
171            let ptr = self.vec.as_mut_ptr();
172
173            ptr.add(self.unfiltered_start)
174                .copy_to(ptr.add(self.vec.len()), copy_len);
175
176            self.vec.set_len(self.vec.len() + copy_len);
177            self.unfiltered_start = index;
178        }
179    }
180
181    /// Removes an element from the `Vec` at the current index.
182    ///
183    /// This also increments the index to the next position.
184    ///
185    /// # Panics
186    ///
187    /// Panics if the index is at the end of the vec.
188    #[inline]
189    pub fn remove(&mut self) -> T {
190        assert!(
191            self.unfiltered_start < self.unfiltered_end,
192            "Removing out of bounds"
193        );
194
195        unsafe {
196            let item = self.vec.as_mut_ptr().add(self.unfiltered_start).read();
197            self.unfiltered_start += 1;
198            item
199        }
200    }
201}
202
203impl<'a, T> Drop for Remover<'a, T> {
204    #[inline]
205    fn drop(&mut self) {
206        let ptr = self.vec.as_mut_ptr();
207        let unfiltered_len = self.unfiltered_end - self.unfiltered_start;
208
209        unsafe {
210            ptr.add(self.unfiltered_start)
211                .copy_to(ptr.add(self.vec.len()), unfiltered_len);
212
213            self.vec.set_len(self.vec.len() + unfiltered_len)
214        }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::Remover;
221    use crate::proputils::prop_eq;
222    use alloc::{format, vec};
223    use proptest::prelude::*;
224    use std::fmt::Debug;
225    use std::vec::Vec;
226
227    #[test]
228    fn remover_empty() {
229        let mut items: Vec<usize> = Vec::new();
230        Remover::new(&mut items);
231        assert_eq!(items, vec![]);
232    }
233
234    #[test]
235    fn remover_single() {
236        let mut items = vec![1];
237        assert_eq!(Remover::new(&mut items).remove(), 1);
238        assert_eq!(items, vec![]);
239    }
240
241    #[test]
242    fn remover_two_first() {
243        let mut items = vec![1, 2];
244        assert_eq!(Remover::new(&mut items).remove(), 1);
245        assert_eq!(items, vec![2]);
246    }
247
248    #[test]
249    fn remover_two_second() {
250        let mut items = vec![1, 2];
251        let mut rem = Remover::new(&mut items);
252        rem.move_to(1);
253        assert_eq!(rem.remove(), 2);
254        drop(rem);
255        assert_eq!(items, vec![1]);
256    }
257
258    #[test]
259    fn remover_two_both() {
260        let mut items = vec![1, 2];
261        let mut rem = Remover::new(&mut items);
262        rem.move_to(0);
263        assert_eq!(rem.remove(), 1);
264        rem.move_to(1);
265        assert_eq!(rem.remove(), 2);
266        drop(rem);
267        assert_eq!(items, vec![]);
268    }
269
270    #[test]
271    #[should_panic]
272    fn remover_two_out_of_order() {
273        let mut items = vec![1, 2];
274        let mut remover = Remover::new(&mut items);
275        remover.move_to(1);
276        assert_eq!(remover.remove(), 2);
277        remover.move_to(0)
278    }
279
280    #[test]
281    #[should_panic]
282    fn remover_out_of_bounds() {
283        drop(Remover::new(&mut vec![1, 2]).move_to(3));
284    }
285
286    #[test]
287    #[should_panic]
288    fn remover_advance_out_of_bounds() {
289        Remover::new(&mut vec![1, 2]).move_to(4);
290    }
291
292    #[test]
293    fn remover_advance_one_past() {
294        let mut items = vec![1, 2];
295        let mut rem = Remover::new(&mut items);
296        rem.move_to(2);
297        assert_eq!(rem.index(), 2);
298    }
299
300    #[test]
301    fn remover_slices() {
302        let mut items = vec![1, 2, 3, 4];
303        let mut rem = Remover::new(&mut items);
304        rem.move_to(2);
305        assert_eq!(rem.remove(), 3);
306        assert_eq!(rem.as_slices(), (&[1, 2][..], &[4][..]));
307        assert_eq!(rem.as_mut_slices(), (&mut [1, 2][..], &mut [4][..]));
308    }
309
310    #[test]
311    fn remover_index() {
312        let mut items = vec![1, 2, 3, 4];
313        let mut remover = Remover::new(&mut items);
314        assert_eq!(remover.index(), 0);
315        remover.move_to(1);
316        assert_eq!(remover.remove(), 2);
317        assert_eq!(remover.index(), 2);
318        remover.move_to(3);
319        assert_eq!(remover.index(), 3);
320    }
321
322    struct NaiveRemover<'a, T> {
323        vec: &'a mut Vec<T>,
324        old_index: usize,
325        new_index: usize,
326    }
327
328    impl<'a, T> NaiveRemover<'a, T> {
329        fn new(vec: &mut Vec<T>) -> NaiveRemover<T> {
330            NaiveRemover {
331                vec,
332                old_index: 0,
333                new_index: 0,
334            }
335        }
336
337        fn index(&self) -> usize {
338            self.old_index
339        }
340
341        fn current(&self) -> Option<&T> {
342            self.vec.get(self.new_index)
343        }
344
345        fn remove(&mut self) -> T {
346            let item = self.vec.remove(self.new_index);
347            self.old_index += 1;
348            item
349        }
350
351        fn move_to(&mut self, index: usize) {
352            self.new_index += index.checked_sub(self.old_index).unwrap();
353            self.old_index = index;
354        }
355
356        fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
357            self.vec.split_at_mut(self.new_index)
358        }
359
360        fn as_slices(&self) -> (&[T], &[T]) {
361            self.vec.split_at(self.new_index)
362        }
363    }
364
365    #[derive(Debug, Clone)]
366    enum Op {
367        MoveTo(usize),
368        Remove,
369    }
370
371    fn prop_strategy() -> impl Strategy<Value = (Vec<u8>, Vec<Op>)> {
372        proptest::collection::vec(any::<u8>(), 0..100).prop_flat_map(|base| {
373            proptest::collection::vec(
374                prop_oneof![(0..base.len() + 1).prop_map(Op::MoveTo), Just(Op::Remove)],
375                0..100,
376            )
377            .prop_map(move |ops| (base.clone(), ops))
378        })
379    }
380
381    fn check_equals_naive_remover<T: Eq + Clone + Debug>(
382        base: Vec<T>,
383        ops: &[Op],
384    ) -> Result<(), TestCaseError> {
385        let mut model_vec = base.clone();
386        let mut model = NaiveRemover::new(&mut model_vec);
387        let mut tested_vec = base;
388        let mut tested = Remover::new(&mut tested_vec);
389
390        ops.iter().try_for_each(|op| {
391            match op {
392                &Op::MoveTo(index) => {
393                    prop_eq(|| model.move_to(index), || tested.move_to(index))
394                }
395                Op::Remove => prop_eq(|| model.remove(), || tested.remove()),
396            }?;
397
398            prop_assert_eq!(model.current(), tested.current());
399            prop_assert_eq!(model.index(), tested.index());
400            prop_assert_eq!(model.as_slices(), tested.as_slices());
401            prop_assert_eq!(model.as_mut_slices(), tested.as_mut_slices());
402
403            Ok(())
404        })?;
405
406        drop(tested);
407        prop_assert_eq!(model_vec, tested_vec);
408
409        Ok(())
410    }
411
412    proptest! {
413        #[test]
414        fn equals_naive_remover((base, ops) in prop_strategy()) {
415            check_equals_naive_remover(base, &ops[..])?
416        }
417    }
418}