-
Notifications
You must be signed in to change notification settings - Fork 210
WIP/RFC: shift_remove and friends #558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
775a3bf
b20d35d
851ab3d
45c16ae
a2ab5a9
176575f
f63d5ef
97eb1dd
5238e6a
4c49505
8b6b714
f21b089
1306704
2107222
b25ee91
c9f6827
25db274
337ea62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -302,43 +302,65 @@ where | |
where | ||
F: FnMut(&mut K, &mut V) -> bool, | ||
{ | ||
const INIT: Option<Pos> = None; | ||
|
||
self.entries | ||
.retain_mut(|entry| keep(&mut entry.key, &mut entry.value)); | ||
|
||
if self.entries.len() < self.indices.len() { | ||
for index in self.indices.iter_mut() { | ||
*index = INIT; | ||
} | ||
self.after_removal(); | ||
} | ||
} | ||
|
||
fn shift_remove_index(&mut self, index: usize) -> Option<(K, V)> { | ||
if index >= self.entries.len() { | ||
return None; | ||
} | ||
|
||
let bucket = self.entries.remove(index); | ||
|
||
self.after_removal(); | ||
|
||
Some((bucket.key, bucket.value)) | ||
} | ||
|
||
fn shift_remove_found(&mut self, _probe: usize, found: usize) -> (K, V) { | ||
let entry = self.entries.remove(found); | ||
|
||
self.after_removal(); /* Todo: pass probe if this starts taking an index parameter */ | ||
|
||
(entry.key, entry.value) | ||
} | ||
|
||
for (index, entry) in self.entries.iter().enumerate() { | ||
let mut probe = entry.hash.desired_pos(Self::mask()); | ||
let mut dist = 0; | ||
|
||
probe_loop!(probe < self.indices.len(), { | ||
let pos = &mut self.indices[probe]; | ||
|
||
if let Some(pos) = *pos { | ||
let entry_hash = pos.hash(); | ||
|
||
// robin hood: steal the spot if it's better for us | ||
let their_dist = entry_hash.probe_distance(Self::mask(), probe); | ||
if their_dist < dist { | ||
Self::insert_phase_2( | ||
&mut self.indices, | ||
probe, | ||
Pos::new(index, entry.hash), | ||
); | ||
break; | ||
} | ||
} else { | ||
*pos = Some(Pos::new(index, entry.hash)); | ||
// Todo: Should this take in a parameter to allow it to only process the moved | ||
// elements? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Technically it's O(n) in both cases, but one is O( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done (see subsequent push). I don't fully understand the hashing logic, so I may have missed a subtlety. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong, fix incoming. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also add another test for the wrong logic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added a test for the wrong logic. However, I'm having a hard time figuring out what the correct logic should be. I can replace only the affected entries' slots in I can revert the "optimization" of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a test, I'm going to try the re-hashing logic from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I put the logic from |
||
fn after_removal(&mut self) { | ||
const INIT: Option<Pos> = None; | ||
|
||
for index in self.indices.iter_mut() { | ||
*index = INIT; | ||
} | ||
|
||
for (index, entry) in self.entries.iter().enumerate() { | ||
let mut probe = entry.hash.desired_pos(Self::mask()); | ||
let mut dist = 0; | ||
|
||
probe_loop!(probe < self.indices.len(), { | ||
let pos = &mut self.indices[probe]; | ||
|
||
if let Some(pos) = *pos { | ||
let entry_hash = pos.hash(); | ||
|
||
// robin hood: steal the spot if it's better for us | ||
let their_dist = entry_hash.probe_distance(Self::mask(), probe); | ||
if their_dist < dist { | ||
Self::insert_phase_2(&mut self.indices, probe, Pos::new(index, entry.hash)); | ||
break; | ||
} | ||
dist += 1; | ||
}); | ||
} | ||
} else { | ||
*pos = Some(Pos::new(index, entry.hash)); | ||
break; | ||
} | ||
dist += 1; | ||
}); | ||
} | ||
} | ||
|
||
|
@@ -1231,6 +1253,152 @@ where | |
.map(|(probe, found)| self.core.remove_found(probe, found).1) | ||
} | ||
|
||
/// Remove the key-value pair at position `index` and return them. | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Like [`Vec::remove`], the pair is removed by shifting all | ||
/// remaining items. This maintains the remaining elements' relative | ||
/// insertion order, but is a more expensive operation | ||
/// | ||
/// Return `None` if `index` is not in `0..len()`. | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Computes in *O*(n) time (average). | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use heapless::index_map::FnvIndexMap; | ||
/// | ||
/// let mut map = FnvIndexMap::<_, _, 8>::new(); | ||
/// map.insert(3, "a").unwrap(); | ||
/// map.insert(2, "b").unwrap(); | ||
/// map.insert(1, "c").unwrap(); | ||
/// let removed = map.shift_remove_index(1); | ||
/// assert_eq!(removed, Some((2, "b"))); | ||
/// assert_eq!(map.len(), 2); | ||
/// | ||
/// let mut iter = map.iter(); | ||
/// assert_eq!(iter.next(), Some((&3, &"a"))); | ||
/// assert_eq!(iter.next(), Some((&1, &"c"))); | ||
/// assert_eq!(iter.next(), None); | ||
/// ``` | ||
pub fn shift_remove_index(&mut self, index: usize) -> Option<(K, V)> { | ||
self.core.shift_remove_index(index) | ||
} | ||
|
||
/// Remove the key-value pair equivalent to `key` and return it and | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// the index it had. | ||
/// | ||
/// Like [`Vec::remove`], the pair is removed by shifting all | ||
/// remaining items. This maintains the remaining elements' relative | ||
/// insertion order, but is a more expensive operation | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Return `None` if `key` is not in map. | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Computes in **O(n)** time (average). | ||
/// # Examples | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// ``` | ||
/// use heapless::index_map::FnvIndexMap; | ||
/// | ||
/// let mut map = FnvIndexMap::<_, _, 8>::new(); | ||
/// map.insert(3, "a").unwrap(); | ||
/// map.insert(2, "b").unwrap(); | ||
/// map.insert(1, "c").unwrap(); | ||
/// let removed = map.shift_remove_full(&2); | ||
/// assert_eq!(removed, Some((1, 2, "b"))); | ||
/// assert_eq!(map.len(), 2); | ||
/// assert_eq!(map.shift_remove_full(&2), None); | ||
/// | ||
/// let mut iter = map.iter(); | ||
/// assert_eq!(iter.next(), Some((&3, &"a"))); | ||
/// assert_eq!(iter.next(), Some((&1, &"c"))); | ||
/// assert_eq!(iter.next(), None); | ||
/// ``` | ||
pub fn shift_remove_full<Q>(&mut self, key: &Q) -> Option<(usize, K, V)> | ||
where | ||
K: Borrow<Q>, | ||
Q: ?Sized + Hash + Eq, | ||
{ | ||
self.find(key).map(|(probe, found)| { | ||
let (k, v) = self.core.shift_remove_found(probe, found); | ||
(found, k, v) | ||
}) | ||
} | ||
|
||
/// Remove and return the key-value pair equivalent to `key`. | ||
/// | ||
/// Like [`Vec::remove`], the pair is removed by shifting all | ||
/// remaining items. This maintains the remaining elements' relative | ||
/// insertion order, but is a more expensive operation | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Return `None` if `key` is not in map. | ||
/// | ||
/// Computes in **O(n)** time (average). | ||
/// # Examples | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// ``` | ||
/// use heapless::index_map::FnvIndexMap; | ||
/// | ||
/// let mut map = FnvIndexMap::<_, _, 8>::new(); | ||
/// map.insert(3, "a").unwrap(); | ||
/// map.insert(2, "b").unwrap(); | ||
/// map.insert(1, "c").unwrap(); | ||
/// let removed = map.shift_remove_entry(&2); | ||
/// assert_eq!(removed, Some((2, "b"))); | ||
/// assert_eq!(map.len(), 2); | ||
/// assert_eq!(map.shift_remove_entry(&2), None); | ||
/// | ||
/// let mut iter = map.iter(); | ||
/// assert_eq!(iter.next(), Some((&3, &"a"))); | ||
/// assert_eq!(iter.next(), Some((&1, &"c"))); | ||
/// assert_eq!(iter.next(), None); | ||
/// ``` | ||
pub fn shift_remove_entry<Q>(&mut self, key: &Q) -> Option<(K, V)> | ||
where | ||
K: Borrow<Q>, | ||
Q: ?Sized + Hash + Eq, | ||
{ | ||
self.shift_remove_full(key).map(|(_idx, k, v)| (k, v)) | ||
} | ||
|
||
/// Remove the key-value pair equivalent to `key` and return | ||
/// its value. | ||
/// | ||
/// Like [`Vec::remove`], the pair is removed by shifting all of the | ||
/// elements that follow it, preserving their relative order. | ||
/// **This perturbs the index of all of those elements!** | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Return `None` if `key` is not in map. | ||
/// | ||
/// Computes in **O(n)** time (average). | ||
prutschman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// use heapless::index_map::FnvIndexMap; | ||
/// | ||
/// let mut map = FnvIndexMap::<_, _, 8>::new(); | ||
/// map.insert(3, "a").unwrap(); | ||
/// map.insert(2, "b").unwrap(); | ||
/// map.insert(1, "c").unwrap(); | ||
/// let removed = map.shift_remove(&2); | ||
/// assert_eq!(removed, Some(("b"))); | ||
/// assert_eq!(map.len(), 2); | ||
/// assert_eq!(map.shift_remove(&2), None); | ||
/// | ||
/// let mut iter = map.iter(); | ||
/// assert_eq!(iter.next(), Some((&3, &"a"))); | ||
/// assert_eq!(iter.next(), Some((&1, &"c"))); | ||
/// assert_eq!(iter.next(), None); | ||
/// ``` | ||
pub fn shift_remove<Q>(&mut self, key: &Q) -> Option<V> | ||
where | ||
K: Borrow<Q>, | ||
Q: ?Sized + Hash + Eq, | ||
{ | ||
self.shift_remove_full(key).map(|(_idx, _k, v)| v) | ||
} | ||
|
||
/// Retains only the elements specified by the predicate. | ||
/// | ||
/// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`. | ||
|
Uh oh!
There was an error while loading. Please reload this page.