├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── bench.rs └── src ├── inserter.rs ├── lib.rs ├── proputils.rs └── remover.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Mike Pedersen "] 3 | categories = ["no-std", "rust-patterns"] 4 | keywords = ["no_std", "vec"] 5 | description = "Insert/remove multiple items from Vecs in O(n) time" 6 | edition = "2018" 7 | license = "MIT" 8 | name = "scanmut" 9 | readme = "README.md" 10 | repository = "https://github.com/mpdn/scanmut" 11 | version = "0.2.0" 12 | 13 | [[bench]] 14 | harness = false 15 | name = "bench" 16 | 17 | [dev-dependencies] 18 | proptest = "0.10.1" 19 | criterion = "0.3.0" 20 | rand = "0.7.2" 21 | rand_pcg = "0.2.1" 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Mike Pedersen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scanmut 2 | 3 | Insert or remove multiple elements from a `Vec` in `O(n)` time. 4 | 5 | This crate provides two types for versatile and efficient `Vec` mutation; `Inserter` and 6 | `Remover`. These two types can be seen as more generic implementations of `Vec::insert` and 7 | `Vec::drain`, allowing you to for example efficiently insert a slice, conditionally drain 8 | elements, or access elements of a `Vec` while draining from it. 9 | 10 | `Inserter` and `Remover` requires an ordering of the insert and removal indices; monotonically 11 | non-increasing for `Inserter` and monotonically increasing for `Remover`. 12 | 13 | For convenience, there are also extension traits adding common higher level operations using the 14 | `Inserter` and `Remover`. See `ScanMut`, `InsertSliceClone`, and `InsertSliceCopy`. 15 | 16 | ## Examples 17 | 18 | Inserting a slice into a vec using [ScanMut::insert_all]: 19 | 20 | ```rust 21 | use scanmut::prelude::*; 22 | 23 | let mut v = vec!['a', 'b', 'c']; 24 | v.insert_all(1, ['d', 'e'].iter().cloned()); 25 | assert_eq!(v, vec!['a', 'd', 'e', 'b', 'c']); 26 | ``` 27 | 28 | Removing multiple elements in one scan using [ScanMut::multi_remove]: 29 | 30 | ```rust 31 | use scanmut::prelude::*; 32 | 33 | let mut v = vec!['a', 'b', 'c']; 34 | v.multi_remove([0, 2].iter().cloned(), drop); 35 | assert_eq!(v, vec!['b']); 36 | ``` 37 | 38 | License: MIT 39 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use rand::RngCore; 3 | use rand_pcg::Lcg128Xsl64; 4 | use scanmut::ScanMut; 5 | 6 | fn criterion_benchmark(c: &mut Criterion) { 7 | let mut rng = Lcg128Xsl64::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7ac28fa16a64abf96); 8 | 9 | let items: Vec<_> = (0..100000).map(|_| rng.next_u32()).collect(); 10 | 11 | let mut insertions: Vec<_> = (0..10000) 12 | .map(|_| ((rng.next_u32() as usize) % items.len(), rng.next_u32())) 13 | .collect(); 14 | 15 | insertions.sort_by(|a, b| b.cmp(a)); 16 | 17 | let removal_limit = std::u32::MAX / 10; 18 | 19 | let removals: Vec<_> = items 20 | .iter() 21 | .enumerate() 22 | .filter(|&(_, &x)| x < removal_limit) 23 | .map(|(i, _)| i) 24 | .collect(); 25 | 26 | c.bench_function("multi_insert", |b| { 27 | b.iter(|| { 28 | let mut v = Vec::with_capacity(items.len() + insertions.len()); 29 | v.extend(items.iter().clone()); 30 | v = black_box(v); 31 | v.multi_insert(insertions.iter().cloned()); 32 | black_box(v); 33 | }) 34 | }); 35 | 36 | c.bench_function("multi_remove", |b| { 37 | b.iter(|| { 38 | let mut v = black_box(items.clone()); 39 | v.multi_remove(black_box(&removals).iter().cloned(), drop); 40 | black_box(v); 41 | }) 42 | }); 43 | 44 | // Attempt at a slightly more fair comparison to `retain` where the comparison is done within 45 | // the benchmark. Somehow this is still faster than `retain` as of writing. 46 | c.bench_function("multi_remove_indexes", |b| { 47 | b.iter(|| { 48 | let mut v = black_box(items.clone()); 49 | 50 | let removals: Vec = v 51 | .iter() 52 | .enumerate() 53 | .filter(|&(_, &x)| x < removal_limit) 54 | .map(|(i, _)| i) 55 | .collect(); 56 | 57 | v.multi_remove(removals.into_iter(), drop); 58 | black_box(v); 59 | }) 60 | }); 61 | 62 | c.bench_function("retain", |b| { 63 | b.iter(|| { 64 | let mut v = black_box(items.clone()); 65 | v.retain(|&x| x >= removal_limit); 66 | black_box(v); 67 | }) 68 | }); 69 | } 70 | 71 | criterion_group!(benches, criterion_benchmark); 72 | criterion_main!(benches); 73 | -------------------------------------------------------------------------------- /src/inserter.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core as std; 3 | 4 | /// An object which can insert multiple elements into a [Vec] in a single scan through the [Vec]. 5 | /// 6 | /// The [Inserter] has a position in the [Vec] at which it inserts, called the index. Initially, the 7 | /// index is at the **end** of the [Vec]. This index can only be moved further towards front of the 8 | /// [Vec], meaning that the indices of the inserted objects must be monotonically non-increasing 9 | /// (i.e. the next index must be less than or equal to the previous index). 10 | /// 11 | /// Dropping the [Inserter] 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 [Inserter] (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::Inserter; 22 | /// 23 | /// let mut items = vec!['a', 'b', 'c']; 24 | /// 25 | /// let mut inserter = Inserter::new(&mut items, 2); 26 | /// 27 | /// assert_eq!(inserter.index(), 3); // Initial index is at end of vec 28 | /// 29 | /// inserter.insert('d'); 30 | /// inserter.move_to(1); 31 | /// inserter.insert('e'); 32 | /// drop(inserter); 33 | /// 34 | /// assert_eq!(items, ['a', 'e', 'b', 'c', 'd']); 35 | /// ``` 36 | /// 37 | /// It is also possible to insert multiple items at the same position, but keep in mind that, since 38 | /// the index does not shift with the new element, the order will be the reverse of insertion order. 39 | /// 40 | /// ``` 41 | /// use scanmut::Inserter; 42 | /// 43 | /// let mut items = vec![1, 2, 3]; 44 | /// 45 | /// let mut inserter = Inserter::new(&mut items, 2); 46 | /// inserter.move_to(1); 47 | /// inserter.insert(4); 48 | /// inserter.insert(5); 49 | /// drop(inserter); 50 | /// 51 | /// assert_eq!(items, [1, 5, 4, 2, 3]); 52 | /// ``` 53 | 54 | #[derive(Debug)] 55 | pub struct Inserter<'a, T> { 56 | // The inserter works by keeping a "gap" of uninitialized values. This gap is initially 57 | // the size of out insertion capacity and is situated at the end of the vec. It then moves down 58 | // through the vec as we insert values, shrinking by one each time we insert a new element. 59 | // 60 | // The fields have the following invariant: 61 | // vec.len() <= merged_start <= merged_end <= vec.capacity() 62 | // 63 | // Where 64 | // [0,vec.len()[ are the unmerged elements 65 | // [vec.len(),merged_start[ is a gap of uninitialized values 66 | // [merged_start,merged_end[ are the merged elements 67 | vec: &'a mut Vec, 68 | merged_start: usize, 69 | merged_end: usize, 70 | } 71 | 72 | impl<'a, T> Inserter<'a, T> { 73 | /// Create a new `Inserter` with the specified maximum number of inserts 74 | /// 75 | /// # Panics 76 | /// 77 | /// Panics if the length plus the additional number of inserts exceeds `isize::MAX` bytes. 78 | #[inline] 79 | pub fn new(vec: &'a mut Vec, additional: usize) -> Inserter<'a, T> { 80 | vec.reserve(additional); 81 | 82 | let len = vec.len(); 83 | 84 | // Safety: len + additional will not overflow as the previous reserve would otherwise panic. 85 | 86 | let merged_start = len + additional; 87 | 88 | Inserter { 89 | merged_start, 90 | merged_end: merged_start, 91 | vec, 92 | } 93 | } 94 | 95 | /// Returns a pair of slices representing the current state of the `Vec`being inserted into. 96 | /// 97 | /// The first slice is the part of the `Vec` below the index. The second slice is the part of 98 | /// the `Vec` above or equal to the index. 99 | #[inline] 100 | pub fn as_slices(&self) -> (&[T], &[T]) { 101 | unsafe { 102 | ( 103 | &self.vec[..], 104 | std::slice::from_raw_parts( 105 | self.vec.as_ptr().add(self.merged_start), 106 | self.merged_end - self.merged_start, 107 | ), 108 | ) 109 | } 110 | } 111 | 112 | /// Returns a pair of mutable slices representing the current state of the `Vec` being inserted 113 | /// into. 114 | /// 115 | /// The first slice is the part of the `Vec` below the index. The second slice is the part of 116 | /// the `Vec` above or equal to the index. 117 | #[inline] 118 | pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { 119 | unsafe { 120 | ( 121 | std::slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.vec.len()), 122 | std::slice::from_raw_parts_mut( 123 | self.vec.as_mut_ptr().add(self.merged_start), 124 | self.merged_end - self.merged_start, 125 | ), 126 | ) 127 | } 128 | } 129 | 130 | /// Returns the remaining number of allowed inserts in this `Inserter`. 131 | #[inline] 132 | pub fn remaining_inserts(&self) -> usize { 133 | self.merged_start - self.vec.len() 134 | } 135 | 136 | /// Returns the current index of the inserter. 137 | #[inline] 138 | pub fn index(&self) -> usize { 139 | self.vec.len() 140 | } 141 | 142 | /// Moves this `Inserter` to the given index in the `Vec`. 143 | /// 144 | /// # Panics 145 | /// 146 | /// Panics if the index to move to is larger than the current index. 147 | #[inline] 148 | pub fn move_to(&mut self, index: usize) { 149 | let copy_len = self 150 | .vec 151 | .len() 152 | .checked_sub(index) 153 | .expect("Index must be less than or equal to previous index"); 154 | 155 | let ptr = self.vec.as_mut_ptr(); 156 | 157 | unsafe { 158 | let write_ptr = ptr.add(self.merged_start - copy_len); 159 | ptr.add(index).copy_to(write_ptr, copy_len); 160 | 161 | self.vec.set_len(index); 162 | self.merged_start -= copy_len; 163 | } 164 | } 165 | 166 | /// Inserts an element into the `Vec` at the specified index. 167 | /// 168 | /// # Panics 169 | /// 170 | /// Panics if there are no more inserts available for this `Inserter`. 171 | #[inline] 172 | pub fn insert(&mut self, item: T) { 173 | unsafe { 174 | self.allocate(1).write(item); 175 | } 176 | } 177 | 178 | /// Inserts all items from an iterator into the underlying `Vec` at the current index. 179 | pub fn insert_all(&mut self, items: I) 180 | where 181 | I: IntoIterator, 182 | I::IntoIter: DoubleEndedIterator, 183 | { 184 | items.into_iter().rev().for_each(|item| self.insert(item)) 185 | } 186 | 187 | /// Inserts all items from an iterator into the underlying `Vec` at the current index in 188 | /// reverse. 189 | pub fn insert_all_rev(&mut self, items: I) 190 | where 191 | I: IntoIterator, 192 | { 193 | items.into_iter().for_each(|item| self.insert(item)) 194 | } 195 | 196 | fn allocate(&mut self, len: usize) -> *mut T { 197 | assert!( 198 | len <= self.remaining_inserts(), 199 | "Insufficient insert capacity" 200 | ); 201 | 202 | self.merged_start -= len; 203 | 204 | unsafe { self.vec.as_mut_ptr().add(self.merged_start) } 205 | } 206 | } 207 | 208 | impl<'a, T: Clone> Inserter<'a, T> { 209 | /// Inserts items at the current index by cloning them from a slice 210 | pub fn insert_slice_clone(&mut self, items: &[T]) { 211 | self.insert_all(items.into_iter().cloned()) 212 | } 213 | } 214 | 215 | impl<'a, T: Copy> Inserter<'a, T> { 216 | /// Inserts items at the current index by copying them from a slice 217 | pub fn insert_slice_copy(&mut self, items: &[T]) { 218 | unsafe { 219 | let ptr = self.allocate(items.len()); 220 | items.as_ptr().copy_to_nonoverlapping(ptr, items.len()); 221 | } 222 | } 223 | } 224 | 225 | impl<'a, T> Drop for Inserter<'a, T> { 226 | #[inline] 227 | fn drop(&mut self) { 228 | let ptr = self.vec.as_mut_ptr(); 229 | let merged_len = self.merged_end - self.merged_start; 230 | 231 | unsafe { 232 | ptr.add(self.merged_start) 233 | .copy_to(ptr.add(self.vec.len()), merged_len); 234 | 235 | self.vec.set_len(self.vec.len() + merged_len); 236 | } 237 | } 238 | } 239 | 240 | #[cfg(test)] 241 | mod tests { 242 | use super::Inserter; 243 | use crate::proputils::prop_eq; 244 | use alloc::{format, vec}; 245 | use proptest::prelude::*; 246 | use std::fmt::Debug; 247 | use std::vec::Vec; 248 | 249 | #[test] 250 | fn inserter_empty() { 251 | let mut items = Vec::new(); 252 | let mut ins = Inserter::new(&mut items, 1); 253 | ins.insert(1); 254 | drop(ins); 255 | assert_eq!(items, vec![1]); 256 | } 257 | 258 | #[test] 259 | fn inserter_single_pre() { 260 | let mut items = vec![0]; 261 | let mut ins = Inserter::new(&mut items, 1); 262 | ins.move_to(0); 263 | ins.insert(1); 264 | drop(ins); 265 | assert_eq!(items, vec![1, 0]); 266 | } 267 | 268 | #[test] 269 | fn inserter_single_post() { 270 | let mut items = vec![0]; 271 | let mut ins = Inserter::new(&mut items, 1); 272 | ins.move_to(1); 273 | ins.insert(1); 274 | drop(ins); 275 | assert_eq!(items, vec![0, 1]); 276 | } 277 | 278 | #[test] 279 | fn inserter_threes() { 280 | let mut items = vec![1, 2, 3]; 281 | let mut ins = Inserter::new(&mut items, 3); 282 | ins.move_to(3); 283 | ins.insert(4); 284 | ins.move_to(2); 285 | ins.insert(5); 286 | ins.move_to(0); 287 | ins.insert(6); 288 | drop(ins); 289 | assert_eq!(items, vec![6, 1, 2, 5, 3, 4]); 290 | } 291 | 292 | #[test] 293 | #[should_panic] 294 | fn inserter_out_of_order() { 295 | let mut items = vec![1, 2, 3]; 296 | let mut ins = Inserter::new(&mut items, 3); 297 | ins.move_to(6); 298 | ins.insert(0); 299 | ins.move_to(4); 300 | ins.insert(3); 301 | } 302 | 303 | #[test] 304 | fn inserter_threes_additional_cap() { 305 | let mut items = vec![1, 2, 3]; 306 | let mut ins = Inserter::new(&mut items, 10); 307 | ins.move_to(3); 308 | ins.insert(4); 309 | ins.move_to(2); 310 | ins.insert(5); 311 | ins.move_to(0); 312 | ins.insert(6); 313 | drop(ins); 314 | assert_eq!(items, vec![6, 1, 2, 5, 3, 4]); 315 | } 316 | 317 | #[test] 318 | fn inserter_slices() { 319 | let mut items = vec![1, 2, 3]; 320 | let mut ins = Inserter::new(&mut items, 1); 321 | ins.move_to(1); 322 | ins.insert(4); 323 | 324 | assert_eq!(ins.as_slices(), (&[1][..], &[4, 2, 3][..])); 325 | assert_eq!(ins.as_mut_slices(), (&mut [1][..], &mut [4, 2, 3][..])); 326 | } 327 | 328 | #[test] 329 | fn inserter_max_index() { 330 | let mut items = vec![1, 2, 3]; 331 | let mut ins = Inserter::new(&mut items, 2); 332 | 333 | assert_eq!(ins.index(), 3); 334 | ins.move_to(3); 335 | ins.insert(4); 336 | assert_eq!(ins.index(), 3); 337 | ins.move_to(2); 338 | assert_eq!(ins.index(), 2); 339 | } 340 | 341 | #[test] 342 | #[should_panic] 343 | fn inserter_oob() { 344 | let mut items = vec![1]; 345 | let mut ins = Inserter::new(&mut items, 1); 346 | ins.move_to(2); 347 | } 348 | 349 | struct NaiveInserter<'a, T> { 350 | vec: &'a mut Vec, 351 | index: usize, 352 | remaining_inserts: usize, 353 | } 354 | 355 | impl<'a, T: Copy> NaiveInserter<'a, T> { 356 | fn new(vec: &mut Vec, additional: usize) -> NaiveInserter { 357 | let index = vec.len(); 358 | NaiveInserter { 359 | vec, 360 | index, 361 | remaining_inserts: additional, 362 | } 363 | } 364 | 365 | fn index(&self) -> usize { 366 | self.index 367 | } 368 | 369 | fn remaining_inserts(&self) -> usize { 370 | self.remaining_inserts 371 | } 372 | 373 | fn insert(&mut self, item: T) { 374 | assert!(self.remaining_inserts > 0); 375 | self.vec.insert(self.index, item); 376 | self.remaining_inserts -= 1; 377 | } 378 | 379 | fn move_to(&mut self, index: usize) { 380 | assert!(index <= self.index); 381 | self.index = index; 382 | } 383 | 384 | fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { 385 | self.vec.split_at_mut(self.index) 386 | } 387 | 388 | fn as_slices(&self) -> (&[T], &[T]) { 389 | self.vec.split_at(self.index) 390 | } 391 | 392 | fn insert_slice_copy(&mut self, slice: &[T]) { 393 | assert!(self.remaining_inserts >= slice.len()); 394 | self.remaining_inserts -= slice.len(); 395 | slice 396 | .iter() 397 | .rev() 398 | .copied() 399 | .for_each(|item| self.vec.insert(self.index, item)); 400 | } 401 | } 402 | 403 | #[derive(Debug, Clone)] 404 | enum Op { 405 | MoveTo(usize), 406 | Insert(T), 407 | InsertSlice(Vec), 408 | } 409 | 410 | fn prop_strategy() -> impl Strategy, Vec>, usize)> { 411 | proptest::collection::vec(any::(), 0..100).prop_flat_map(|base| { 412 | proptest::collection::vec( 413 | prop_oneof![ 414 | (0..base.len() + 1).prop_map(Op::MoveTo), 415 | any::().prop_map(Op::Insert), 416 | any::>().prop_map(Op::InsertSlice) 417 | ], 418 | 0..100, 419 | ) 420 | .prop_flat_map(move |ops| { 421 | let base = base.clone(); 422 | (0..ops.len() + 1) 423 | .prop_map(move |additional| (base.clone(), ops.clone(), additional)) 424 | }) 425 | }) 426 | } 427 | 428 | fn check_equals_naive_inserter( 429 | base: Vec, 430 | ops: &[Op], 431 | additional: usize, 432 | ) -> Result<(), TestCaseError> { 433 | let mut model_vec = base.clone(); 434 | let mut model = NaiveInserter::new(&mut model_vec, additional); 435 | let mut tested_vec = base; 436 | let mut tested = Inserter::new(&mut tested_vec, additional); 437 | 438 | ops.iter().try_for_each(|op| { 439 | match op { 440 | &Op::MoveTo(index) => { 441 | prop_eq(|| model.move_to(index), || tested.move_to(index)) 442 | } 443 | Op::Insert(item) => prop_eq( 444 | || model.insert(item.clone()), 445 | || tested.insert(item.clone()), 446 | ), 447 | Op::InsertSlice(slice) => prop_eq( 448 | || model.insert_slice_copy(slice), 449 | || tested.insert_slice_copy(slice), 450 | ), 451 | }?; 452 | 453 | prop_assert_eq!(model.remaining_inserts(), tested.remaining_inserts()); 454 | prop_assert_eq!(model.index(), tested.index()); 455 | prop_assert_eq!(model.as_slices(), tested.as_slices()); 456 | prop_assert_eq!(model.as_mut_slices(), tested.as_mut_slices()); 457 | 458 | Ok(()) 459 | })?; 460 | 461 | drop(tested); 462 | prop_assert_eq!(model_vec, tested_vec); 463 | 464 | Ok(()) 465 | } 466 | 467 | proptest! { 468 | #[test] 469 | fn equals_naive_inserter((base, ops, additional) in prop_strategy()) { 470 | check_equals_naive_inserter(base, &ops[..], additional)? 471 | } 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Insert or remove multiple elements from a [Vec] in `O(n)` time. 2 | //! 3 | //! This crate provides two types for versatile and efficient [Vec] mutation; [Inserter] and 4 | //! [Remover]. These two types can be seen as more generic implementations of [Vec::insert] and 5 | //! [Vec::drain], allowing you to for example efficiently insert a slice, conditionally drain 6 | //! elements, or access elements of a [Vec] while draining from it. 7 | //! 8 | //! [Inserter] and [Remover] requires an ordering of the insert and removal indices; monotonically 9 | //! non-increasing for [Inserter] and monotonically increasing for [Remover]. 10 | //! 11 | //! For convenience, there are also extension traits adding common higher level operations using the 12 | //! [Inserter] and [Remover]. See [ScanMut], [InsertSliceClone], and [InsertSliceCopy]. 13 | //! 14 | //! # Examples 15 | //! 16 | //! Inserting a slice into a vec using [ScanMut::insert_all]: 17 | //! 18 | //! ``` 19 | //! use scanmut::prelude::*; 20 | //! 21 | //! let mut v = vec!['a', 'b', 'c']; 22 | //! v.insert_all(1, ['d', 'e'].iter().cloned()); 23 | //! assert_eq!(v, vec!['a', 'd', 'e', 'b', 'c']); 24 | //! ``` 25 | //! 26 | //! Removing multiple elements in one scan using [ScanMut::multi_remove]: 27 | //! 28 | //! ``` 29 | //! use scanmut::prelude::*; 30 | //! 31 | //! let mut v = vec!['a', 'b', 'c']; 32 | //! v.multi_remove([0, 2].iter().cloned(), drop); 33 | //! assert_eq!(v, vec!['b']); 34 | //! ``` 35 | 36 | #![deny(missing_debug_implementations, missing_docs)] 37 | #![no_std] 38 | 39 | #[cfg(test)] 40 | extern crate std; 41 | extern crate alloc; 42 | 43 | mod inserter; 44 | mod remover; 45 | 46 | #[cfg(test)] 47 | pub mod proputils; 48 | 49 | use alloc::vec::Vec; 50 | pub use inserter::Inserter; 51 | pub use remover::Remover; 52 | 53 | /// Module of commonly-used traits 54 | pub mod prelude { 55 | pub use super::{InsertSliceClone, InsertSliceCopy, ScanMut}; 56 | } 57 | 58 | /// Multiple insert/remove functions 59 | pub trait ScanMut { 60 | /// Insert multiple elements at specific indices in `O(n)` time. 61 | /// 62 | /// Indices must be in monotonically non-increasing order (i.e. the next index must be smaller 63 | /// than or equal to the previous index). 64 | /// 65 | /// # Example 66 | /// 67 | /// ``` 68 | /// use scanmut::ScanMut; 69 | /// 70 | /// let mut v = vec![1, 2, 3, 4, 5]; 71 | /// v.multi_insert([(3, 8), (3, 7), (0, 6)].iter().cloned()); 72 | /// 73 | /// assert_eq!(v, vec![6, 1, 2, 3, 7, 8, 4, 5]); 74 | /// ``` 75 | /// 76 | /// # Panics 77 | /// 78 | /// Panics if 79 | /// - The indices are not monotonically non-increasing. 80 | /// - An index is out of bounds. 81 | /// 82 | fn multi_insert(&mut self, iter: I) 83 | where 84 | I: IntoIterator, 85 | I::IntoIter: ExactSizeIterator; 86 | 87 | /// Remove multiple elements by index and calls a sink function with the removed element. 88 | /// 89 | /// Indices must be in monotonically increasing order (i.e. the next index must be more than the 90 | /// previous index). 91 | /// 92 | /// # Example 93 | /// 94 | /// ``` 95 | /// use scanmut::ScanMut; 96 | /// 97 | /// let mut v = vec![1, 2, 3, 4, 5]; 98 | /// v.multi_remove([0, 3].iter().cloned(), drop); 99 | /// 100 | /// assert_eq!(v, vec![2, 3, 5]); 101 | /// ``` 102 | /// 103 | /// # Panics 104 | /// 105 | /// Panics if 106 | /// - The indices are not monotonically increasing. 107 | /// - An index is out of bounds. 108 | fn multi_remove(&mut self, iter: I, sink: F) 109 | where 110 | I: IntoIterator, 111 | F: FnMut(T); 112 | 113 | // Ideally, the above function would return an iterator of removed elements instead, but that 114 | // currently does not seem possible without generic associated types. 🤷 115 | 116 | /// Inserts items from an iterator at the given index. 117 | /// 118 | /// # Panics 119 | /// 120 | /// Panics if 121 | /// - The range `index..(index + items.len())` is out of bounds for this `Vec`. 122 | /// - The iterator is shorter or longer than the reported length. 123 | fn insert_all(&mut self, index: usize, items: I) 124 | where 125 | I: IntoIterator, 126 | I::IntoIter: ExactSizeIterator + DoubleEndedIterator; 127 | 128 | /// Inserts items in reverse from an iterator at the given index. 129 | /// 130 | /// # Panics 131 | /// 132 | /// Panics if 133 | /// - The range `index..(index + items.len())` is out of bounds for this `Vec`. 134 | /// - The iterator is shorter or longer than the reported length. 135 | fn insert_all_rev(&mut self, index: usize, items: I) 136 | where 137 | I: IntoIterator, 138 | I::IntoIter: ExactSizeIterator; 139 | } 140 | 141 | impl ScanMut for Vec { 142 | fn multi_insert(&mut self, iter: I) 143 | where 144 | I: IntoIterator, 145 | I::IntoIter: ExactSizeIterator, 146 | { 147 | let iter = iter.into_iter(); 148 | let mut inserter = Inserter::new(self, iter.len()); 149 | iter.for_each(|(ix, item)| { 150 | inserter.move_to(ix); 151 | inserter.insert(item); 152 | }); 153 | 154 | assert!( 155 | inserter.remaining_inserts() == 0, 156 | "Iterator shorter than reported length" 157 | ); 158 | } 159 | 160 | fn multi_remove(&mut self, iter: I, mut sink: F) 161 | where 162 | I: IntoIterator, 163 | F: FnMut(T), 164 | { 165 | let mut remover = Remover::new(self); 166 | iter.into_iter().for_each(|ix| { 167 | remover.move_to(ix); 168 | sink(remover.remove()); 169 | }); 170 | } 171 | 172 | fn insert_all(&mut self, index: usize, items: I) 173 | where 174 | I: IntoIterator, 175 | I::IntoIter: ExactSizeIterator + DoubleEndedIterator, 176 | { 177 | let iter = items.into_iter(); 178 | let mut inserter = Inserter::new(self, iter.len()); 179 | inserter.move_to(index); 180 | inserter.insert_all(iter); 181 | 182 | assert!( 183 | inserter.remaining_inserts() == 0, 184 | "Iterator shorter than reported length" 185 | ); 186 | } 187 | 188 | fn insert_all_rev(&mut self, index: usize, items: I) 189 | where 190 | I: IntoIterator, 191 | I::IntoIter: ExactSizeIterator, 192 | { 193 | let iter = items.into_iter(); 194 | let mut inserter = Inserter::new(self, iter.len()); 195 | inserter.move_to(index); 196 | inserter.insert_all_rev(iter); 197 | 198 | assert!( 199 | inserter.remaining_inserts() == 0, 200 | "Iterator shorter than reported length" 201 | ); 202 | } 203 | } 204 | 205 | /// ScanMut extension trait for `Vec`s with cloneable items. 206 | pub trait InsertSliceClone: ScanMut { 207 | /// Inserts items from a slice at the given index by cloning elements. 208 | /// 209 | /// # Panics 210 | /// 211 | /// Panics if 212 | /// - The range `index..(index + slice.len())` is out of bounds for this `Vec`. 213 | fn insert_slice_clone(&mut self, index: usize, slice: &[T]); 214 | } 215 | 216 | impl InsertSliceClone for Vec { 217 | fn insert_slice_clone(&mut self, index: usize, slice: &[T]) { 218 | let mut inserter = Inserter::new(self, slice.len()); 219 | inserter.move_to(index); 220 | inserter.insert_slice_clone(slice); 221 | } 222 | } 223 | 224 | /// ScanMut extension trait for `Vec`s with copyable items. 225 | pub trait InsertSliceCopy: InsertSliceClone { 226 | /// Inserts items from a slice at the given index by copying elements. 227 | /// 228 | /// # Panics 229 | /// 230 | /// Panics if 231 | /// - The range `index..(index + slice.len())` is out of bounds for this `Vec`. 232 | fn insert_slice_copy(&mut self, index: usize, slice: &[T]); 233 | } 234 | 235 | impl InsertSliceCopy for Vec { 236 | fn insert_slice_copy(&mut self, index: usize, slice: &[T]) { 237 | let mut inserter = Inserter::new(self, slice.len()); 238 | inserter.move_to(index); 239 | inserter.insert_slice_copy(slice); 240 | } 241 | } 242 | 243 | #[cfg(test)] 244 | mod tests { 245 | use super::*; 246 | use alloc::vec; 247 | use core::iter::once; 248 | 249 | #[test] 250 | fn multimutate_single_insert() { 251 | let mut items = Vec::new(); 252 | items.multi_insert(once((0, 1))); 253 | assert_eq!(items, vec![1]); 254 | } 255 | 256 | #[test] 257 | fn multimutate_single_insert_pre() { 258 | let mut items = vec![0]; 259 | items.multi_insert(once((0, 1))); 260 | assert_eq!(items, vec![1, 0]); 261 | } 262 | 263 | #[test] 264 | fn multimutate_single_insert_mid() { 265 | let mut items = vec![0, 2]; 266 | items.multi_insert(once((1, 1))); 267 | assert_eq!(items, vec![0, 1, 2]); 268 | } 269 | 270 | #[test] 271 | fn multimutate_single_insert_post() { 272 | let mut items = vec![0]; 273 | items.multi_insert(once((1, 1))); 274 | assert_eq!(items, vec![0, 1]); 275 | } 276 | 277 | #[test] 278 | #[should_panic] 279 | fn multimutate_single_insert_out_of_bounds() { 280 | let mut items = vec![0]; 281 | items.multi_insert(once((2, 1))); 282 | } 283 | 284 | #[test] 285 | fn multimutate_multiple_insert() { 286 | let mut items = vec![1]; 287 | items.multi_insert([(1, 2), (0, 0)].iter().cloned()); 288 | assert_eq!(items, vec![0, 1, 2]); 289 | } 290 | 291 | #[test] 292 | fn multimutate_single_insert_zero_sized() { 293 | let mut items = vec![()]; 294 | items.multi_insert(once((0, ()))); 295 | assert_eq!(items, vec![(), ()]); 296 | } 297 | 298 | #[test] 299 | #[should_panic] 300 | fn multimutate_single_insert_zero_sized_out_of_bounds() { 301 | let mut items = vec![()]; 302 | items.multi_insert(once((2, ()))); 303 | assert_eq!(items, vec![(), ()]); 304 | } 305 | 306 | #[test] 307 | #[should_panic] 308 | fn multimutate_remove() { 309 | let mut items = vec![1, 2, 3]; 310 | let mut index = 0; 311 | 312 | items.multi_remove(vec![1, 2], |x| { 313 | assert_eq!( 314 | x, 315 | match index { 316 | 0 => 1, 317 | 1 => 2, 318 | _ => panic!(), 319 | } 320 | ); 321 | 322 | index += 1; 323 | }); 324 | 325 | assert_eq!(items, [1]); 326 | } 327 | 328 | #[test] 329 | fn insert_all() { 330 | let mut items = vec![1, 2, 3]; 331 | items.insert_all(1, [4, 5, 6].iter().cloned()); 332 | assert_eq!(items, [1, 4, 5, 6, 2, 3]); 333 | } 334 | 335 | #[test] 336 | fn insert_all_end() { 337 | let mut items = vec![1, 2, 3]; 338 | items.insert_all(3, [4, 5, 6].iter().cloned()); 339 | assert_eq!(items, [1, 2, 3, 4, 5, 6]); 340 | } 341 | 342 | #[test] 343 | fn insert_all_rev() { 344 | let mut items = vec![1, 2, 3]; 345 | items.insert_all_rev(1, [4, 5, 6].iter().cloned()); 346 | assert_eq!(items, [1, 6, 5, 4, 2, 3]); 347 | } 348 | 349 | #[test] 350 | fn insert_all_rev_end() { 351 | let mut items = vec![1, 2, 3]; 352 | items.insert_all_rev(3, [4, 5, 6].iter().cloned()); 353 | assert_eq!(items, [1, 2, 3, 6, 5, 4]); 354 | } 355 | 356 | #[test] 357 | fn insert_slice_clone() { 358 | let mut items = vec![1, 2, 3]; 359 | items.insert_slice_clone(1, &[4, 5, 6]); 360 | assert_eq!(items, [1, 4, 5, 6, 2, 3]); 361 | } 362 | 363 | #[test] 364 | fn insert_slice_copy() { 365 | let mut items = vec![1, 2, 3]; 366 | items.insert_slice_copy(1, &[4, 5, 6]); 367 | assert_eq!(items, [1, 4, 5, 6, 2, 3]); 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /src/proputils.rs: -------------------------------------------------------------------------------- 1 | use proptest::prelude::*; 2 | 3 | /// Compares the result of two closures. 4 | /// 5 | /// Results are considered equal if both panic. 6 | pub fn prop_eq( 7 | model: impl FnOnce() -> T, 8 | tested: impl FnOnce() -> T, 9 | ) -> Result<(), TestCaseError> { 10 | use alloc::format; 11 | use std::panic::{catch_unwind, AssertUnwindSafe}; 12 | 13 | let a = catch_unwind(AssertUnwindSafe(|| model())); 14 | let b = catch_unwind(AssertUnwindSafe(|| tested())); 15 | 16 | prop_assert_eq!(a.is_ok(), b.is_ok(), "{:?},{:?}", a, b); 17 | 18 | if let (Ok(a), Ok(b)) = (a, b) { 19 | prop_assert_eq!(a, b); 20 | } 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /src/remover.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use 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)] 60 | pub 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, 73 | unfiltered_start: usize, 74 | unfiltered_end: usize, 75 | } 76 | 77 | impl<'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) -> 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 | 203 | impl<'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)] 219 | mod 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 = 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, 324 | old_index: usize, 325 | new_index: usize, 326 | } 327 | 328 | impl<'a, T> NaiveRemover<'a, T> { 329 | fn new(vec: &mut Vec) -> NaiveRemover { 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, Vec)> { 372 | proptest::collection::vec(any::(), 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( 382 | base: Vec, 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 | } 419 | --------------------------------------------------------------------------------