Refactor fn robin_hood
This commit is contained in:
parent
f8b8c3ae6b
commit
3778560809
2 changed files with 53 additions and 28 deletions
|
|
@ -421,24 +421,30 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) {
|
|||
/// to recalculate it.
|
||||
///
|
||||
/// `hash`, `k`, and `v` are the elements to "robin hood" into the hashtable.
|
||||
fn robin_hood<'a, K: 'a, V: 'a>(mut bucket: FullBucketMut<'a, K, V>,
|
||||
fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
|
||||
mut ib: usize,
|
||||
mut hash: SafeHash,
|
||||
mut k: K,
|
||||
mut v: V)
|
||||
mut key: K,
|
||||
mut val: V)
|
||||
-> &'a mut V {
|
||||
let starting_index = bucket.index();
|
||||
let size = {
|
||||
let table = bucket.table(); // FIXME "lifetime too short".
|
||||
table.size()
|
||||
};
|
||||
// Save the *starting point*.
|
||||
let mut bucket = bucket.stash();
|
||||
// There can be at most `size - dib` buckets to displace, because
|
||||
// in the worst case, there are `size` elements and we already are
|
||||
// `distance` buckets away from the initial one.
|
||||
// `displacement` buckets away from the initial one.
|
||||
let idx_end = starting_index + size - bucket.displacement();
|
||||
|
||||
loop {
|
||||
let (old_hash, old_key, old_val) = bucket.replace(hash, k, v);
|
||||
let (old_hash, old_key, old_val) = bucket.replace(hash, key, val);
|
||||
hash = old_hash;
|
||||
key = old_key;
|
||||
val = old_val;
|
||||
|
||||
loop {
|
||||
let probe = bucket.next();
|
||||
assert!(probe.index() != idx_end);
|
||||
|
|
@ -446,14 +452,10 @@ fn robin_hood<'a, K: 'a, V: 'a>(mut bucket: FullBucketMut<'a, K, V>,
|
|||
let full_bucket = match probe.peek() {
|
||||
Empty(bucket) => {
|
||||
// Found a hole!
|
||||
let b = bucket.put(old_hash, old_key, old_val);
|
||||
let bucket = bucket.put(hash, key, val);
|
||||
// Now that it's stolen, just read the value's pointer
|
||||
// right out of the table!
|
||||
return Bucket::at_index(b.into_table(), starting_index)
|
||||
.peek()
|
||||
.expect_full()
|
||||
.into_mut_refs()
|
||||
.1;
|
||||
// right out of the table! Go back to the *starting point*.
|
||||
return bucket.into_table().into_mut_refs().1;
|
||||
},
|
||||
Full(bucket) => bucket
|
||||
};
|
||||
|
|
@ -465,9 +467,6 @@ fn robin_hood<'a, K: 'a, V: 'a>(mut bucket: FullBucketMut<'a, K, V>,
|
|||
// Robin hood! Steal the spot.
|
||||
if ib < probe_ib {
|
||||
ib = probe_ib;
|
||||
hash = old_hash;
|
||||
k = old_key;
|
||||
v = old_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,28 @@ impl<K, V, M> Bucket<K, V, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V, M> Deref for FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> {
|
||||
type Target = RawTable<K, V>;
|
||||
fn deref(&self) -> &RawTable<K, V> {
|
||||
&self.table
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, M> DerefMut for FullBucket<K, V, M> where M: DerefMut<Target=RawTable<K, V>> {
|
||||
fn deref_mut(&mut self) -> &mut RawTable<K, V> {
|
||||
&mut self.table
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// `Put` is implemented for types which provide access to a table and cannot be invalidated
|
||||
/// by filling a bucket. A similar implementation for `Take` is possible.
|
||||
pub trait Put {}
|
||||
impl<K, V> Put for RawTable<K, V> {}
|
||||
impl<'t, K, V> Put for &'t mut RawTable<K, V> {}
|
||||
impl<K, V, M: Put> Put for Bucket<K, V, M> {}
|
||||
impl<K, V, M: Put> Put for FullBucket<K, V, M> {}
|
||||
|
||||
impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
|
||||
pub fn new(table: M, hash: SafeHash) -> Bucket<K, V, M> {
|
||||
Bucket::at_index(table, hash.inspect() as usize)
|
||||
|
|
@ -320,7 +342,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> EmptyBucket<K, V, M> {
|
||||
impl<K, V, M> EmptyBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut + Put {
|
||||
/// Puts given key and value pair, along with the key's hash,
|
||||
/// into this bucket in the hashtable. Note how `self` is 'moved' into
|
||||
/// this function, because this slot will no longer be empty when
|
||||
|
|
@ -359,6 +381,16 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Duplicates the current position. This can be useful for operations
|
||||
/// on two or more buckets.
|
||||
pub fn stash(self) -> FullBucket<K, V, Self> {
|
||||
FullBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the distance between this bucket and the 'ideal' location
|
||||
/// as determined by the key's hash stored in it.
|
||||
///
|
||||
|
|
@ -389,12 +421,14 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> FullBucket<K, V, M> {
|
||||
// We don't need a `Take` trait currently. This is why a mutable reference
|
||||
// to the table is required.
|
||||
impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
|
||||
/// Removes this bucket's key and value from the hashtable.
|
||||
///
|
||||
/// This works similarly to `put`, building an `EmptyBucket` out of the
|
||||
/// taken bucket.
|
||||
pub fn take(mut self) -> (EmptyBucket<K, V, M>, K, V) {
|
||||
pub fn take(mut self) -> (EmptyBucket<K, V, &'t mut RawTable<K, V>>, K, V) {
|
||||
self.table.size -= 1;
|
||||
|
||||
unsafe {
|
||||
|
|
@ -410,7 +444,9 @@ impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> FullBucket<K, V, M> {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut {
|
||||
pub fn replace(&mut self, h: SafeHash, k: K, v: V) -> (SafeHash, K, V) {
|
||||
unsafe {
|
||||
let old_hash = ptr::replace(self.raw.hash as *mut SafeHash, h);
|
||||
|
|
@ -455,16 +491,6 @@ impl<'t, K, V, M: Deref<Target=RawTable<K, V>> + DerefMut + 't> FullBucket<K, V,
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V, M> BucketState<K, V, M> {
|
||||
// For convenience.
|
||||
pub fn expect_full(self) -> FullBucket<K, V, M> {
|
||||
match self {
|
||||
Full(full) => full,
|
||||
Empty(..) => panic!("Expected full bucket")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, M: Deref<Target=RawTable<K, V>>> GapThenFull<K, V, M> {
|
||||
#[inline]
|
||||
pub fn full(&self) -> &FullBucket<K, V, M> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue