Auto merge of #32058 - pczarn:hashmap-initial-refactoring, r=apasel422

Basic refactoring of HashMap
This commit is contained in:
bors 2016-03-22 21:21:45 -07:00
commit 8ba2ea5fad
2 changed files with 250 additions and 224 deletions

View File

@ -9,7 +9,6 @@
// except according to those terms.
use self::Entry::*;
use self::SearchResult::*;
use self::VacantEntryState::*;
use borrow::Borrow;
@ -26,7 +25,6 @@ use super::table::{
Bucket,
EmptyBucket,
FullBucket,
FullBucketImm,
FullBucketMut,
RawTable,
SafeHash
@ -342,10 +340,11 @@ pub struct HashMap<K, V, S = RandomState> {
}
/// Search for a pre-hashed key.
#[inline]
fn search_hashed<K, V, M, F>(table: M,
hash: SafeHash,
mut is_match: F)
-> SearchResult<K, V, M> where
-> InternalEntry<K, V, M> where
M: Deref<Target=RawTable<K, V>>,
F: FnMut(&K) -> bool,
{
@ -353,37 +352,50 @@ fn search_hashed<K, V, M, F>(table: M,
// undefined behavior when Bucket::new gets the raw bucket in this
// case, immediately return the appropriate search result.
if table.capacity() == 0 {
return TableRef(table);
return InternalEntry::TableIsEmpty;
}
let size = table.size();
let size = table.size() as isize;
let mut probe = Bucket::new(table, hash);
let ib = probe.index();
let ib = probe.index() as isize;
while probe.index() != ib + size {
loop {
let full = match probe.peek() {
Empty(b) => return TableRef(b.into_table()), // hit an empty bucket
Full(b) => b
Empty(bucket) => {
// Found a hole!
return InternalEntry::Vacant {
hash: hash,
elem: NoElem(bucket),
};
}
Full(bucket) => bucket
};
if full.distance() + ib < full.index() {
let robin_ib = full.index() as isize - full.displacement() as isize;
if ib < robin_ib {
// Found a luckier bucket than me.
// We can finish the search early if we hit any bucket
// with a lower distance to initial bucket than we've probed.
return TableRef(full.into_table());
return InternalEntry::Vacant {
hash: hash,
elem: NeqElem(full, robin_ib as usize),
};
}
// If the hash doesn't match, it can't be this one..
if hash == full.hash() {
// If the key doesn't match, it can't be this one..
if is_match(full.read().0) {
return FoundExisting(full);
return InternalEntry::Occupied {
elem: full
};
}
}
probe = full.next();
debug_assert!(probe.index() as isize != ib + size + 1);
}
TableRef(probe.into_table())
}
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) {
@ -393,7 +405,7 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) {
None => return (retkey, retval)
};
while gap.full().distance() != 0 {
while gap.full().displacement() != 0 {
gap = match gap.shift() {
Some(b) => b,
None => break
@ -409,78 +421,63 @@ 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.
let idx_end = starting_index + size - bucket.distance();
// `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);
debug_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*.
//
// This use of `into_table` is misleading. It turns the
// bucket, which is a FullBucket on top of a
// FullBucketMut, into just one FullBucketMut. The "table"
// refers to the inner FullBucketMut in this context.
return bucket.into_table().into_mut_refs().1;
},
Full(bucket) => bucket
};
let probe_ib = full_bucket.index() - full_bucket.distance();
let probe_ib = full_bucket.index() - full_bucket.displacement();
bucket = full_bucket;
// Robin hood! Steal the spot.
if ib < probe_ib {
ib = probe_ib;
hash = old_hash;
k = old_key;
v = old_val;
break;
}
}
}
}
/// A result that works like Option<FullBucket<..>> but preserves
/// the reference that grants us access to the table in any case.
enum SearchResult<K, V, M> {
// This is an entry that holds the given key:
FoundExisting(FullBucket<K, V, M>),
// There was no such entry. The reference is given back:
TableRef(M)
}
impl<K, V, M> SearchResult<K, V, M> {
fn into_option(self) -> Option<FullBucket<K, V, M>> {
match self {
FoundExisting(bucket) => Some(bucket),
TableRef(_) => None
}
}
}
impl<K, V, S> HashMap<K, V, S>
where K: Eq + Hash, S: BuildHasher
{
@ -491,20 +488,20 @@ impl<K, V, S> HashMap<K, V, S>
/// Search for a key, yielding the index if it's found in the hashtable.
/// If you already have the hash for the key lying around, use
/// search_hashed.
fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> Option<FullBucketImm<'a, K, V>>
#[inline]
fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> InternalEntry<K, V, &'a RawTable<K, V>>
where K: Borrow<Q>, Q: Eq + Hash
{
let hash = self.make_hash(q);
search_hashed(&self.table, hash, |k| q.eq(k.borrow()))
.into_option()
}
fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> Option<FullBucketMut<'a, K, V>>
#[inline]
fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> InternalEntry<K, V, &'a mut RawTable<K, V>>
where K: Borrow<Q>, Q: Eq + Hash
{
let hash = self.make_hash(q);
search_hashed(&mut self.table, hash, |k| q.eq(k.borrow()))
.into_option()
}
// The caller should ensure that invariants by Robin Hood Hashing hold.
@ -711,7 +708,7 @@ impl<K, V, S> HashMap<K, V, S>
loop {
bucket = match bucket.peek() {
Full(full) => {
if full.distance() == 0 {
if full.displacement() == 0 {
// This bucket occupies its ideal spot.
// It indicates the start of another "cluster".
bucket = full.into_bucket();
@ -804,53 +801,19 @@ impl<K, V, S> HashMap<K, V, S>
///
/// If the key already exists, the hashtable will be returned untouched
/// and a reference to the existing element will be returned.
fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> &mut V {
self.insert_or_replace_with(hash, k, v, |_, _, _, _| ())
}
fn insert_or_replace_with<'a, F>(&'a mut self,
hash: SafeHash,
k: K,
v: V,
mut found_existing: F)
-> &'a mut V where
F: FnMut(&mut K, &mut V, K, V),
{
// Worst case, we'll find one empty bucket among `size + 1` buckets.
let size = self.table.size();
let mut probe = Bucket::new(&mut self.table, hash);
let ib = probe.index();
loop {
let mut bucket = match probe.peek() {
Empty(bucket) => {
// Found a hole!
return bucket.put(hash, k, v).into_mut_refs().1;
}
Full(bucket) => bucket
};
// hash matches?
if bucket.hash() == hash {
// key matches?
if k == *bucket.read_mut().0 {
let (bucket_k, bucket_v) = bucket.into_mut_refs();
debug_assert!(k == *bucket_k);
// Key already exists. Get its reference.
found_existing(bucket_k, bucket_v, k, v);
return bucket_v;
}
fn insert_hashed_nocheck(&mut self, hash: SafeHash, k: K, v: V) -> Option<V> {
let entry = search_hashed(&mut self.table, hash, |key| *key == k).into_entry(k);
match entry {
Some(Occupied(mut elem)) => {
Some(elem.insert(v))
}
let robin_ib = bucket.index() as isize - bucket.distance() as isize;
if (ib as isize) < robin_ib {
// Found a luckier bucket than me. Better steal his spot.
return robin_hood(bucket, robin_ib as usize, hash, k, v);
Some(Vacant(elem)) => {
elem.insert(v);
None
}
None => {
unreachable!()
}
probe = bucket.next();
assert!(probe.index() != ib + size + 1);
}
}
@ -977,9 +940,7 @@ impl<K, V, S> HashMap<K, V, S>
pub fn entry(&mut self, key: K) -> Entry<K, V> {
// Gotta resize now.
self.reserve(1);
let hash = self.make_hash(&key);
search_entry_hashed(&mut self.table, hash, key)
self.search_mut(&key).into_entry(key).expect("unreachable")
}
/// Returns the number of elements in the map.
@ -1082,7 +1043,7 @@ impl<K, V, S> HashMap<K, V, S>
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where K: Borrow<Q>, Q: Hash + Eq
{
self.search(k).map(|bucket| bucket.into_refs().1)
self.search(k).into_occupied_bucket().map(|bucket| bucket.into_refs().1)
}
/// Returns true if the map contains a value for the specified key.
@ -1105,7 +1066,7 @@ impl<K, V, S> HashMap<K, V, S>
pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool
where K: Borrow<Q>, Q: Hash + Eq
{
self.search(k).is_some()
self.search(k).into_occupied_bucket().is_some()
}
/// Returns a mutable reference to the value corresponding to the key.
@ -1130,7 +1091,7 @@ impl<K, V, S> HashMap<K, V, S>
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where K: Borrow<Q>, Q: Hash + Eq
{
self.search_mut(k).map(|bucket| bucket.into_mut_refs().1)
self.search_mut(k).into_occupied_bucket().map(|bucket| bucket.into_mut_refs().1)
}
/// Inserts a key-value pair into the map.
@ -1161,12 +1122,7 @@ impl<K, V, S> HashMap<K, V, S>
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
let hash = self.make_hash(&k);
self.reserve(1);
let mut retval = None;
self.insert_or_replace_with(hash, k, v, |_, val_ref, _, val| {
retval = Some(replace(val_ref, val));
});
retval
self.insert_hashed_nocheck(hash, k, v)
}
/// Removes a key from the map, returning the value at the key if the key
@ -1194,54 +1150,7 @@ impl<K, V, S> HashMap<K, V, S>
return None
}
self.search_mut(k).map(|bucket| pop_internal(bucket).1)
}
}
fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHash, k: K)
-> Entry<'a, K, V>
{
// Worst case, we'll find one empty bucket among `size + 1` buckets.
let size = table.size();
let mut probe = Bucket::new(table, hash);
let ib = probe.index();
loop {
let bucket = match probe.peek() {
Empty(bucket) => {
// Found a hole!
return Vacant(VacantEntry {
hash: hash,
key: k,
elem: NoElem(bucket),
});
},
Full(bucket) => bucket
};
// hash matches?
if bucket.hash() == hash {
// key matches?
if k == *bucket.read().0 {
return Occupied(OccupiedEntry{
elem: bucket,
});
}
}
let robin_ib = bucket.index() as isize - bucket.distance() as isize;
if (ib as isize) < robin_ib {
// Found a luckier bucket than me. Better steal his spot.
return Vacant(VacantEntry {
hash: hash,
key: k,
elem: NeqElem(bucket, robin_ib as usize),
});
}
probe = bucket.next();
assert!(probe.index() != ib + size + 1);
self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1)
}
}
@ -1362,18 +1271,47 @@ pub struct Drain<'a, K: 'a, V: 'a> {
inner: iter::Map<table::Drain<'a, K, V>, fn((SafeHash, K, V)) -> (K, V)>
}
/// A view into a single occupied location in a HashMap.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
elem: FullBucket<K, V, &'a mut RawTable<K, V>>,
enum InternalEntry<K, V, M> {
Occupied {
elem: FullBucket<K, V, M>,
},
Vacant {
hash: SafeHash,
elem: VacantEntryState<K, V, M>,
},
TableIsEmpty,
}
/// A view into a single empty location in a HashMap.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct VacantEntry<'a, K: 'a, V: 'a> {
hash: SafeHash,
key: K,
elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>,
impl<K, V, M> InternalEntry<K, V, M> {
#[inline]
fn into_occupied_bucket(self) -> Option<FullBucket<K, V, M>> {
match self {
InternalEntry::Occupied { elem } => Some(elem),
_ => None,
}
}
}
impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> {
#[inline]
fn into_entry(self, key: K) -> Option<Entry<'a, K, V>> {
match self {
InternalEntry::Occupied { elem } => {
Some(Occupied(OccupiedEntry {
key: Some(key),
elem: elem
}))
}
InternalEntry::Vacant { hash, elem } => {
Some(Vacant(VacantEntry {
hash: hash,
key: key,
elem: elem,
}))
}
InternalEntry::TableIsEmpty => None
}
}
}
/// A view into a single location in a map, which may be vacant or occupied.
@ -1392,6 +1330,21 @@ pub enum Entry<'a, K: 'a, V: 'a> {
),
}
/// A view into a single occupied location in a HashMap.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
key: Option<K>,
elem: FullBucket<K, V, &'a mut RawTable<K, V>>,
}
/// A view into a single empty location in a HashMap.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct VacantEntry<'a, K: 'a, V: 'a> {
hash: SafeHash,
key: K,
elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>,
}
/// Possible states of a VacantEntry.
enum VacantEntryState<K, V, M> {
/// The index is occupied, but the key to insert has precedence,
@ -1592,6 +1545,12 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
pub fn remove(self) -> V {
pop_internal(self.elem).1
}
/// Returns a key that was used for search.
///
/// The key was retained for further use.
fn take_key(&mut self) -> Option<K> {
self.key.take()
}
}
impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
@ -1696,7 +1655,7 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
type Key = K;
fn get(&self, key: &Q) -> Option<&K> {
self.search(key).map(|bucket| bucket.into_refs().0)
self.search(key).into_occupied_bucket().map(|bucket| bucket.into_refs().0)
}
fn take(&mut self, key: &Q) -> Option<K> {
@ -1704,18 +1663,22 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
return None
}
self.search_mut(key).map(|bucket| pop_internal(bucket).0)
self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0)
}
fn replace(&mut self, key: K) -> Option<K> {
let hash = self.make_hash(&key);
self.reserve(1);
let mut retkey = None;
self.insert_or_replace_with(hash, key, (), |key_ref, _, key, _| {
retkey = Some(replace(key_ref, key));
});
retkey
match self.entry(key) {
Occupied(mut occupied) => {
let key = occupied.take_key().unwrap();
Some(mem::replace(occupied.elem.read_mut().0, key))
}
Vacant(vacant) => {
vacant.insert(());
None
}
}
}
}
@ -1750,6 +1713,20 @@ mod test_map {
assert_eq!(*m.get(&2).unwrap(), 4);
}
#[test]
fn test_clone() {
let mut m = HashMap::new();
assert_eq!(m.len(), 0);
assert!(m.insert(1, 2).is_none());
assert_eq!(m.len(), 1);
assert!(m.insert(2, 4).is_none());
assert_eq!(m.len(), 2);
let m2 = m.clone();
assert_eq!(*m2.get(&1).unwrap(), 2);
assert_eq!(*m2.get(&2).unwrap(), 4);
assert_eq!(m2.len(), 2);
}
thread_local! { static DROP_VECTOR: RefCell<Vec<isize>> = RefCell::new(Vec::new()) }
#[derive(Hash, PartialEq, Eq)]
@ -1841,7 +1818,7 @@ mod test_map {
}
#[test]
fn test_move_iter_drops() {
fn test_into_iter_drops() {
DROP_VECTOR.with(|v| {
*v.borrow_mut() = vec![0; 200];
});
@ -1906,11 +1883,35 @@ mod test_map {
}
#[test]
fn test_empty_pop() {
fn test_empty_remove() {
let mut m: HashMap<isize, bool> = HashMap::new();
assert_eq!(m.remove(&0), None);
}
#[test]
fn test_empty_entry() {
let mut m: HashMap<isize, bool> = HashMap::new();
match m.entry(0) {
Occupied(_) => panic!(),
Vacant(_) => {}
}
assert!(*m.entry(0).or_insert(true));
assert_eq!(m.len(), 1);
}
#[test]
fn test_empty_iter() {
let mut m: HashMap<isize, bool> = HashMap::new();
assert_eq!(m.drain().next(), None);
assert_eq!(m.keys().next(), None);
assert_eq!(m.values().next(), None);
assert_eq!(m.iter().next(), None);
assert_eq!(m.iter_mut().next(), None);
assert_eq!(m.len(), 0);
assert!(m.is_empty());
assert_eq!(m.into_iter().next(), None);
}
#[test]
fn test_lots_of_insertions() {
let mut m = HashMap::new();

View File

@ -201,23 +201,47 @@ impl<K, V, M> EmptyBucket<K, V, M> {
pub fn table(&self) -> &M {
&self.table
}
/// Move out the reference to the table.
pub fn into_table(self) -> M {
self.table
}
}
impl<K, V, M> Bucket<K, V, M> {
/// Move out the reference to the table.
pub fn into_table(self) -> M {
self.table
}
/// Get the raw index.
pub fn index(&self) -> usize {
self.idx
}
}
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
}
}
/// `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<K, V> {
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V>;
}
impl<'t, K, V> Put<K, V> for &'t mut RawTable<K, V> {
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
*self
}
}
impl<K, V, M> Put<K, V> for Bucket<K, V, M> where M: Put<K, V> {
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
self.table.borrow_table_mut()
}
}
impl<K, V, M> Put<K, V> for FullBucket<K, V, M> where M: Put<K, V> {
unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
self.table.borrow_table_mut()
}
}
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)
@ -268,22 +292,14 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
/// Modifies the bucket pointer in place to make it point to the next slot.
pub fn next(&mut self) {
// Branchless bucket iteration step.
// As we reach the end of the table...
// We take the current idx: 0111111b
// Xor it by its increment: ^ 1000000b
// ------------
// 1111111b
// Then AND with the capacity: & 1000000b
// ------------
// to get the backwards offset: 1000000b
// ... and it's zero at all other times.
let maybe_wraparound_dist = (self.idx ^ (self.idx + 1)) & self.table.capacity();
// Finally, we obtain the offset 1 or the offset -cap + 1.
let dist = 1 - (maybe_wraparound_dist as isize);
self.idx += 1;
let range = self.table.capacity();
// This code is branchless thanks to a conditional move.
let dist = if self.idx & (range - 1) == 0 {
1 - range as isize
} else {
1
};
unsafe {
self.raw = self.raw.offset(dist);
}
@ -326,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: Put<K, V> {
/// 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
@ -340,9 +356,9 @@ impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> EmptyBucket<K, V, M> {
*self.raw.hash = hash.inspect();
ptr::write(self.raw.key, key);
ptr::write(self.raw.val, value);
}
self.table.size += 1;
self.table.borrow_table_mut().size += 1;
}
FullBucket { raw: self.raw, idx: self.idx, table: self.table }
}
@ -365,12 +381,22 @@ 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.
///
/// In the cited blog posts above, this is called the "distance to
/// initial bucket", or DIB. Also known as "probe count".
pub fn distance(&self) -> usize {
pub fn displacement(&self) -> usize {
// Calculates the distance one has to travel when going from
// `hash mod capacity` onwards to `idx mod capacity`, wrapping around
// if the destination is not reached before the end of the table.
@ -395,12 +421,15 @@ 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 take a mutable reference to the table instead of accepting anything that
// implements `DerefMut` to prevent fn `take` from being called on `stash`ed
// buckets.
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 {
@ -416,7 +445,11 @@ impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> FullBucket<K, V, M> {
)
}
}
}
// This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases
// where `M` is a full bucket or table reference type with mutable access to the table.
impl<K, V, M> FullBucket<K, V, M> where M: Put<K, V> {
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);
@ -426,7 +459,9 @@ impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> FullBucket<K, V, M> {
(old_hash, old_key, old_val)
}
}
}
impl<K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut {
/// Gets mutable references to the key and value at a given index.
pub fn read_mut(&mut self) -> (&mut K, &mut V) {
unsafe {
@ -436,7 +471,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>> + DerefMut> FullBucket<K, V, M> {
}
}
impl<'t, K, V, M: Deref<Target=RawTable<K, V>> + 't> FullBucket<K, V, M> {
impl<'t, K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + 't {
/// Exchange a bucket state for immutable references into the table.
/// Because the underlying reference to the table is also consumed,
/// no further changes to the structure of the table are possible;
@ -450,7 +485,7 @@ impl<'t, K, V, M: Deref<Target=RawTable<K, V>> + 't> FullBucket<K, V, M> {
}
}
impl<'t, K, V, M: Deref<Target=RawTable<K, V>> + DerefMut + 't> FullBucket<K, V, M> {
impl<'t, K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut + 't {
/// This works similarly to `into_refs`, exchanging a bucket state
/// for mutable references into the table.
pub fn into_mut_refs(self) -> (&'t mut K, &'t mut V) {
@ -461,17 +496,7 @@ 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> {
impl<K, V, M> GapThenFull<K, V, M> where M: Deref<Target=RawTable<K, V>> {
#[inline]
pub fn full(&self) -> &FullBucket<K, V, M> {
&self.full