Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 64 additions & 21 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,26 @@ where
/// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed
/// form must match those for the key type.
pub fn remove<'g, Q>(&'g self, key: &Q, guard: &'g Guard) -> Option<&'g V>
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
self.replace_node(key, None, None, guard)
}

/// Replaces node value with v, conditional upon match of cv.
/// If resulting value does not exists it removes the key (and its corresponding value) from this map.
/// This method does nothing if the key is not in the map.
/// Returns the previous value associated with the given key.
///
/// The key may be any borrowed form of the map's key type, but `Hash` and `Eq` on the borrowed
/// form must match those for the key type.
pub fn replace_node<'g, Q>(
&'g self,
key: &Q,
new_value : Option<V>,
old_value : Option<Shared<'g, V>>,
guard: &'g Guard) -> Option<&'g V>
where
K: Borrow<Q>,
Q: ?Sized + Hash + Eq,
Expand Down Expand Up @@ -1025,28 +1045,34 @@ where
let next = n.next.load(Ordering::SeqCst, guard);
if n.hash == h && n.key.borrow() == key {
let ev = n.value.load(Ordering::SeqCst, guard);
old_val = Some(ev);

// remove the BinEntry containing the removed key value pair from the bucket
if !pred.is_null() {
// either by changing the pointer of the previous BinEntry, if present
// safety: as above
unsafe { pred.deref() }
.as_node()
.unwrap()
.next
.store(next, Ordering::SeqCst);
} else {
// or by setting the next node as the first BinEntry if there is no previous entry
t.store_bin(i, next);
// found the node but we have a new value to replace the old one
if let Some(nv) = new_value {
n.value.store(Owned::new(nv), Ordering::SeqCst);
break;
// just remove the node if the value is the one we expected at method call
} else if old_value.is_none() || old_value.unwrap() == ev {
old_val = Some(ev);
// remove the BinEntry containing the removed key value pair from the bucket
if !pred.is_null() {
// either by changing the pointer of the previous BinEntry, if present
// safety: as above
unsafe { pred.deref() }
.as_node()
.unwrap()
.next
.store(next, Ordering::SeqCst);
} else {
// or by setting the next node as the first BinEntry if there is no previous entry
t.store_bin(i, next);
}

// in either case, mark the BinEntry as garbage, since it was just removed
// safety: as for val below / in put
unsafe { guard.defer_destroy(e) };

// since the key was found and only one node exists per key, we can break here
break;
}

// in either case, mark the BinEntry as garbage, since it was just removed
// safety: as for val below / in put
unsafe { guard.defer_destroy(e) };

// since the key was found and only one node exists per key, we can break here
break;
}
pred = e;
if next.is_null() {
Expand Down Expand Up @@ -1091,6 +1117,23 @@ where
None
}

/// Retains only the elements specified by the predicate.
///
/// In other words, remove all pairs (k, v) such that f(&k,&v) returns false.
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&K, &V) -> bool
{
let guard = epoch::pin();
// removed selected keys
for (k, v) in self.iter(&guard) {
if !f(k, v) {
// TODO: right now this removing the element by key without checking the value. The value should be used somehow in old_value
self.replace_node(k, None, None, &guard);
}
}
}

/// An iterator visiting all key-value pairs in arbitrary order.
/// The iterator element type is `(&'g K, &'g V)`.
///
Expand Down
67 changes: 66 additions & 1 deletion tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crossbeam_epoch as epoch;
use crossbeam_epoch::{self as epoch, Shared};
use flurry::*;
use std::sync::Arc;

Expand Down Expand Up @@ -37,6 +37,42 @@ fn get_key_value_empty() {
}
}

#[test]
fn replace_empty() {
let map = HashMap::<usize, usize>::new();

{
let guard = epoch::pin();
let old = map.replace_node(&42, None, None, &guard);
assert!(old.is_none());
}
}

#[test]
fn replace_existing() {
let map = HashMap::<usize, usize>::new();
{
let guard = epoch::pin();
map.insert(42, 42, &guard);
let old = map.replace_node(&42, Some(10), None, &guard);
assert!(old.is_none());
assert_eq!(*map.get(&42, &guard).unwrap(), 10);
}
}

#[test]
fn replace_old_value_non_matching() {
let map = HashMap::<usize, usize>::new();
{
let guard = epoch::pin();
map.insert(42, 42, &guard);
// TODO: fit test with an actual non valid old_value
let old = map.replace_node(&42, None, None, &guard);
assert!(old.is_none());
assert_eq!(*map.get(&42, &guard).unwrap(), 42);
}
}

#[test]
fn remove_empty() {
let map = HashMap::<usize, usize>::new();
Expand Down Expand Up @@ -350,3 +386,32 @@ fn clone_map_filled() {
map.insert("NewItem", 100, &epoch::pin());
assert_ne!(&map, &cloned_map);
}

#[test]
fn retain_empty() {
let mut map = HashMap::<&'static str, u32>::new();
map.retain(|_, _| false);
assert_eq!(map.len(), 0);
}

#[test]
fn retain_all_false() {
let mut map : HashMap::<u32, u32> = (0..10 as u32).map(|x| (x, x)).collect();
map.retain(|_, _| false);
assert_eq!(map.len(), 0);
}

#[test]
fn retain_all_true() {
let size = 10usize;
let mut map : HashMap::<usize, usize> = (0..size).map(|x| (x, x)).collect();
map.retain(|_, _| true);
assert_eq!(map.len(), size);
}

#[test]
fn retain_some() {
let mut map : HashMap::<u32, u32> = (0..10).map(|x| (x, x)).collect();
map.retain(|_, v| *v >= 5);
assert_eq!(map.len(), 5);
}