Skip to content

Commit

Permalink
Refactor fn robin_hood
Browse files Browse the repository at this point in the history
  • Loading branch information
pczarn committed Mar 5, 2016
1 parent f8b8c3a commit 3778560
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 28 deletions.
29 changes: 14 additions & 15 deletions src/libstd/collections/hash/map.rs
Expand Up @@ -421,39 +421,41 @@ 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);

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
};
Expand All @@ -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;
}
}
Expand Down
52 changes: 39 additions & 13 deletions src/libstd/collections/hash/table.rs
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down Expand Up @@ -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> {
Expand Down

0 comments on commit 3778560

Please sign in to comment.