Skip to content

Commit f7766ea

Browse files
committed
implement retain for IndexMap/IndexSet
Signed-off-by: Jean-Pierre De Jesus DIAZ <me@jeandudey.tech>
1 parent 2bf9286 commit f7766ea

File tree

3 files changed

+100
-4
lines changed

3 files changed

+100
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111

1212
- Add `Clone` and `PartialEq` implementations to `HistoryBuffer`.
1313
- Added an object pool API. see the `pool::object` module level doc for details
14+
- Implemented `retain` for `IndexMap` and `IndexSet`.
1415

1516
### Changed
1617

src/indexmap.rs

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,11 @@ where
210210
let index = self.entries.len();
211211
unsafe { self.entries.push_unchecked(Bucket { hash, key, value }) };
212212
return Insert::Success(Inserted {
213-
index: self.insert_phase_2(probe, Pos::new(index, hash)),
213+
index: Self::insert_phase_2(
214+
&mut self.indices,
215+
probe,
216+
Pos::new(index, hash),
217+
),
214218
old_value: None,
215219
});
216220
} else if entry_hash == hash && unsafe { self.entries.get_unchecked(i).key == key }
@@ -241,9 +245,9 @@ where
241245
}
242246

243247
// phase 2 is post-insert where we forward-shift `Pos` in the indices.
244-
fn insert_phase_2(&mut self, mut probe: usize, mut old_pos: Pos) -> usize {
245-
probe_loop!(probe < self.indices.len(), {
246-
let pos = unsafe { self.indices.get_unchecked_mut(probe) };
248+
fn insert_phase_2(indices: &mut [Option<Pos>; N], mut probe: usize, mut old_pos: Pos) -> usize {
249+
probe_loop!(probe < indices.len(), {
250+
let pos = unsafe { indices.get_unchecked_mut(probe) };
247251

248252
let mut is_none = true; // work around lack of NLL
249253
if let Some(pos) = pos.as_mut() {
@@ -287,6 +291,50 @@ where
287291
(entry.key, entry.value)
288292
}
289293

294+
fn retain_in_order<F>(&mut self, mut keep: F)
295+
where
296+
F: FnMut(&mut K, &mut V) -> bool,
297+
{
298+
const INIT: Option<Pos> = None;
299+
300+
self.entries
301+
.retain_mut(|entry| keep(&mut entry.key, &mut entry.value));
302+
303+
if self.entries.len() < self.indices.len() {
304+
for index in self.indices.iter_mut() {
305+
*index = INIT;
306+
}
307+
308+
for (index, entry) in self.entries.iter().enumerate() {
309+
let mut probe = entry.hash.desired_pos(Self::mask());
310+
let mut dist = 0;
311+
312+
probe_loop!(probe < self.indices.len(), {
313+
let pos = &mut self.indices[probe];
314+
315+
if let Some(pos) = *pos {
316+
let entry_hash = pos.hash();
317+
318+
// robin hood: steal the spot if it's better for us
319+
let their_dist = entry_hash.probe_distance(Self::mask(), probe);
320+
if their_dist < dist {
321+
Self::insert_phase_2(
322+
&mut self.indices,
323+
probe,
324+
Pos::new(index, entry.hash),
325+
);
326+
break;
327+
}
328+
} else {
329+
*pos = Some(Pos::new(index, entry.hash));
330+
break;
331+
}
332+
dist += 1;
333+
});
334+
}
335+
}
336+
}
337+
290338
fn backward_shift_after_removal(&mut self, probe_at_remove: usize) {
291339
// backward shift deletion in self.indices
292340
// after probe, shift all non-ideally placed indices backward
@@ -890,6 +938,16 @@ where
890938
.map(|(probe, found)| self.core.remove_found(probe, found).1)
891939
}
892940

941+
/// Retains only the elements specified by the predicate.
942+
///
943+
/// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`.
944+
pub fn retain<F>(&mut self, mut f: F)
945+
where
946+
F: FnMut(&K, &mut V) -> bool,
947+
{
948+
self.core.retain_in_order(move |k, v| f(k, v));
949+
}
950+
893951
/* Private API */
894952
/// Return probe (indices) and position (entries)
895953
fn find<Q>(&self, key: &Q) -> Option<(usize, usize)>
@@ -1328,6 +1386,33 @@ mod tests {
13281386
assert_eq!(MAP_SLOTS - 1, src.len());
13291387
}
13301388

1389+
#[test]
1390+
fn retain() {
1391+
let mut none = almost_filled_map();
1392+
none.retain(|_, _| false);
1393+
assert!(none.is_empty());
1394+
1395+
let mut all = almost_filled_map();
1396+
all.retain(|_, _| true);
1397+
assert_eq!(all.len(), MAP_SLOTS - 1);
1398+
1399+
let mut even = almost_filled_map();
1400+
even.retain(|_, &mut v| v % 2 == 0);
1401+
assert_eq!(even.len(), (MAP_SLOTS - 1) / 2);
1402+
for &v in even.values() {
1403+
assert_eq!(v % 2, 0);
1404+
}
1405+
1406+
let mut odd = almost_filled_map();
1407+
odd.retain(|_, &mut v| v % 2 != 0);
1408+
assert_eq!(odd.len(), MAP_SLOTS / 2);
1409+
for &v in odd.values() {
1410+
assert_ne!(v % 2, 0);
1411+
}
1412+
assert_eq!(odd.insert(2, 2), Ok(None));
1413+
assert_eq!(odd.len(), (MAP_SLOTS / 2) + 1);
1414+
}
1415+
13311416
#[test]
13321417
fn entry_roll_through_all() {
13331418
let mut src: FnvIndexMap<usize, usize, MAP_SLOTS> = FnvIndexMap::new();

src/indexset.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,16 @@ where
469469
{
470470
self.map.remove(value).is_some()
471471
}
472+
473+
/// Retains only the elements specified by the predicate.
474+
///
475+
/// In other words, remove all elements `e` for which `f(&e)` returns `false`.
476+
pub fn retain<F>(&mut self, mut f: F)
477+
where
478+
F: FnMut(&T) -> bool,
479+
{
480+
self.map.retain(move |k, _| f(k));
481+
}
472482
}
473483

474484
impl<T, S, const N: usize> Clone for IndexSet<T, S, N>

0 commit comments

Comments
 (0)