Skip to content

Commit 05ca4ed

Browse files
authored
Update the extract_if feature (#381)
* Update the `extract_if` feature * doctest
1 parent dff2390 commit 05ca4ed

File tree

2 files changed

+66
-13
lines changed

2 files changed

+66
-13
lines changed

src/lib.rs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ where
403403
vec: &'a mut SmallVec<T, N>,
404404
/// The index of the item that will be inspected by the next call to `next`.
405405
idx: usize,
406+
/// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`.
407+
end: usize,
406408
/// The number of items that have been drained (removed) thus far.
407409
del: usize,
408410
/// The original length of `vec` prior to draining.
@@ -433,7 +435,7 @@ where
433435

434436
fn next(&mut self) -> Option<T> {
435437
unsafe {
436-
while self.idx < self.old_len {
438+
while self.idx < self.end {
437439
let i = self.idx;
438440
let v = core::slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
439441
let drained = (self.pred)(&mut v[i]);
@@ -456,7 +458,7 @@ where
456458
}
457459

458460
fn size_hint(&self) -> (usize, Option<usize>) {
459-
(0, Some(self.old_len - self.idx))
461+
(0, Some(self.end - self.idx))
460462
}
461463
}
462464

@@ -903,12 +905,15 @@ impl<T, const N: usize> SmallVec<T, N> {
903905
}
904906

905907
#[cfg(feature = "extract_if")]
906-
/// Creates an iterator which uses a closure to determine if an element should be removed.
908+
/// Creates an iterator which uses a closure to determine if element in the range should be removed.
907909
///
908-
/// If the closure returns true, the element is removed and yielded.
910+
/// If the closure returns true, then the element is removed and yielded.
909911
/// If the closure returns false, the element will remain in the vector and will not be yielded
910912
/// by the iterator.
911913
///
914+
/// Only elements that fall in the provided range are considered for extraction, but any elements
915+
/// after the range will still have to be moved if any element has been extracted.
916+
///
912917
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
913918
/// or the iteration short-circuits, then the remaining elements will be retained.
914919
/// Use [`retain`] with a negated predicate if you do not need the returned iterator.
@@ -918,10 +923,12 @@ impl<T, const N: usize> SmallVec<T, N> {
918923
/// Using this method is equivalent to the following code:
919924
/// ```
920925
/// # use smallvec::SmallVec;
926+
/// # use std::cmp::min;
921927
/// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 };
922928
/// # let mut vec: SmallVec<i32, 8> = SmallVec::from_slice(&[1i32, 2, 3, 4, 5, 6]);
929+
/// # let range = 1..4;
923930
/// let mut i = 0;
924-
/// while i < vec.len() {
931+
/// while i < min(vec.len(), range.end) {
925932
/// if some_predicate(&mut vec[i]) {
926933
/// let val = vec.remove(i);
927934
/// // your code here
@@ -936,8 +943,12 @@ impl<T, const N: usize> SmallVec<T, N> {
936943
/// But `extract_if` is easier to use. `extract_if` is also more efficient,
937944
/// because it can backshift the elements of the array in bulk.
938945
///
939-
/// Note that `extract_if` also lets you mutate every element in the filter closure,
940-
/// regardless of whether you choose to keep or remove it.
946+
/// Note that `extract_if` also lets you mutate the elements passed to the filter closure,
947+
/// regardless of whether you choose to keep or remove them.
948+
///
949+
/// # Panics
950+
///
951+
/// If `range` is out of bounds.
941952
///
942953
/// # Examples
943954
///
@@ -947,17 +958,58 @@ impl<T, const N: usize> SmallVec<T, N> {
947958
/// # use smallvec::SmallVec;
948959
/// let mut numbers: SmallVec<i32, 16> = SmallVec::from_slice(&[1i32, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]);
949960
///
950-
/// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::<SmallVec<i32, 16>>();
961+
/// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::<SmallVec<i32, 16>>();
951962
/// let odds = numbers;
952963
///
953964
/// assert_eq!(evens, SmallVec::<i32, 16>::from_slice(&[2i32, 4, 6, 8, 14]));
954965
/// assert_eq!(odds, SmallVec::<i32, 16>::from_slice(&[1i32, 3, 5, 9, 11, 13, 15]));
955966
/// ```
956-
pub fn extract_if<F>(&mut self, filter: F) -> ExtractIf<'_, T, N, F>
967+
///
968+
/// Using the range argument to only process a part of the vector:
969+
///
970+
/// ```
971+
/// # use smallvec::SmallVec;
972+
/// let mut items: SmallVec<i32, 16> = SmallVec::from_slice(&[0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2]);
973+
/// let ones = items.extract_if(7.., |x| *x == 1).collect::<SmallVec<i32, 16>>();
974+
/// assert_eq!(items, SmallVec::<i32, 16>::from_slice(&[0, 0, 0, 0, 0, 0, 0, 2, 2, 2]));
975+
/// assert_eq!(ones.len(), 3);
976+
/// ```
977+
pub fn extract_if<F, R>(&mut self, range: R, filter: F) -> ExtractIf<'_, T, N, F>
957978
where
958979
F: FnMut(&mut T) -> bool,
980+
R: core::ops::RangeBounds<usize>,
959981
{
960982
let old_len = self.len();
983+
// This line can be used instead once `core::slice::range` is stable.
984+
//let core::ops::Range { start, end } = core::slice::range(range, ..old_len);
985+
let (start, end) = {
986+
let len = old_len;
987+
988+
let start = match range.start_bound() {
989+
core::ops::Bound::Included(&start) => start,
990+
core::ops::Bound::Excluded(start) => {
991+
start.checked_add(1).unwrap_or_else(|| panic!("attempted to index slice from after maximum usize"))
992+
}
993+
core::ops::Bound::Unbounded => 0,
994+
};
995+
996+
let end = match range.end_bound() {
997+
core::ops::Bound::Included(end) => {
998+
end.checked_add(1).unwrap_or_else(|| panic!("attempted to index slice up to maximum usize"))
999+
}
1000+
core::ops::Bound::Excluded(&end) => end,
1001+
core::ops::Bound::Unbounded => len,
1002+
};
1003+
1004+
if start > end {
1005+
panic!("slice index starts at {start} but ends at {end}");
1006+
}
1007+
if end > len {
1008+
panic!("range end index {end} out of range for slice of length {len}");
1009+
}
1010+
1011+
(start, end)
1012+
};
9611013

9621014
// Guard against us getting leaked (leak amplification)
9631015
unsafe {
@@ -966,7 +1018,8 @@ impl<T, const N: usize> SmallVec<T, N> {
9661018

9671019
ExtractIf {
9681020
vec: self,
969-
idx: 0,
1021+
idx: start,
1022+
end,
9701023
del: 0,
9711024
old_len,
9721025
pred: filter,

src/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,11 +1088,11 @@ fn test_clone_from() {
10881088
#[cfg(feature = "extract_if")]
10891089
#[test]
10901090
fn test_extract_if() {
1091-
let mut a: SmallVec<u8, 2> = smallvec![1u8, 2, 3, 4, 5, 6, 7, 8];
1091+
let mut a: SmallVec<u8, 2> = smallvec![0, 1u8, 2, 3, 4, 5, 6, 7, 8, 0];
10921092

1093-
let b: SmallVec<u8, 2> = a.extract_if(|x| *x % 3 == 0).collect();
1093+
let b: SmallVec<u8, 2> = a.extract_if(1..9, |x| *x % 3 == 0).collect();
10941094

1095-
assert_eq!(a, SmallVec::<u8, 2>::from_slice(&[1u8, 2, 4, 5, 7, 8]));
1095+
assert_eq!(a, SmallVec::<u8, 2>::from_slice(&[0, 1u8, 2, 4, 5, 7, 8, 0]));
10961096
assert_eq!(b, SmallVec::<u8, 2>::from_slice(&[3u8, 6]));
10971097
}
10981098

0 commit comments

Comments
 (0)