Simplify HashMap Bucket interface
* Store capacity_mask instead of capacity * Move bucket index into RawBucket * Bucket index is now always within [0..table_capacity) * Clone RawTable using RawBucket * Simplify iterators by moving logic into RawBuckets * Make retain aware of the number of elements
This commit is contained in:
parent
5309a3e31d
commit
f07ebd6097
2 changed files with 164 additions and 190 deletions
|
|
@ -472,7 +472,7 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>)
|
|||
}
|
||||
|
||||
// Now we've done all our shifting. Return the value we grabbed earlier.
|
||||
(retkey, retval, gap.into_bucket().into_table())
|
||||
(retkey, retval, gap.into_table())
|
||||
}
|
||||
|
||||
/// Perform robin hood bucket stealing at the given `bucket`. You must
|
||||
|
|
@ -485,14 +485,14 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
|
|||
mut key: K,
|
||||
mut val: V)
|
||||
-> FullBucketMut<'a, K, V> {
|
||||
let start_index = bucket.index();
|
||||
let size = bucket.table().size();
|
||||
// Save the *starting point*.
|
||||
let mut bucket = bucket.stash();
|
||||
let raw_capacity = bucket.table().capacity();
|
||||
// There can be at most `size - dib` buckets to displace, because
|
||||
// in the worst case, there are `size` elements and we already are
|
||||
// `displacement` buckets away from the initial one.
|
||||
let idx_end = start_index + size - bucket.displacement();
|
||||
let idx_end = (bucket.index() + size - bucket.displacement()) % raw_capacity;
|
||||
// Save the *starting point*.
|
||||
let mut bucket = bucket.stash();
|
||||
|
||||
loop {
|
||||
let (old_hash, old_key, old_val) = bucket.replace(hash, key, val);
|
||||
|
|
@ -568,11 +568,8 @@ impl<K, V, S> HashMap<K, V, S>
|
|||
// The caller should ensure that invariants by Robin Hood Hashing hold
|
||||
// and that there's space in the underlying table.
|
||||
fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) {
|
||||
let raw_cap = self.raw_capacity();
|
||||
let mut buckets = Bucket::new(&mut self.table, hash);
|
||||
// note that buckets.index() keeps increasing
|
||||
// even if the pointer wraps back to the first bucket.
|
||||
let limit_bucket = buckets.index() + raw_cap;
|
||||
let start_index = buckets.index();
|
||||
|
||||
loop {
|
||||
// We don't need to compare hashes for value swap.
|
||||
|
|
@ -585,7 +582,7 @@ impl<K, V, S> HashMap<K, V, S>
|
|||
Full(b) => b.into_bucket(),
|
||||
};
|
||||
buckets.next();
|
||||
debug_assert!(buckets.index() < limit_bucket);
|
||||
debug_assert!(buckets.index() != start_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1244,24 +1241,25 @@ impl<K, V, S> HashMap<K, V, S>
|
|||
pub fn retain<F>(&mut self, mut f: F)
|
||||
where F: FnMut(&K, &mut V) -> bool
|
||||
{
|
||||
if self.table.capacity() == 0 || self.table.size() == 0 {
|
||||
if self.table.size() == 0 {
|
||||
return;
|
||||
}
|
||||
let mut elems_left = self.table.size();
|
||||
let mut bucket = Bucket::head_bucket(&mut self.table);
|
||||
bucket.prev();
|
||||
let tail = bucket.index();
|
||||
loop {
|
||||
let start_index = bucket.index();
|
||||
while elems_left != 0 {
|
||||
bucket = match bucket.peek() {
|
||||
Full(mut full) => {
|
||||
elems_left -= 1;
|
||||
let should_remove = {
|
||||
let (k, v) = full.read_mut();
|
||||
!f(k, v)
|
||||
};
|
||||
if should_remove {
|
||||
let prev_idx = full.index();
|
||||
let prev_raw = full.raw();
|
||||
let (_, _, t) = pop_internal(full);
|
||||
Bucket::new_from(prev_raw, prev_idx, t)
|
||||
Bucket::new_from(prev_raw, t)
|
||||
} else {
|
||||
full.into_bucket()
|
||||
}
|
||||
|
|
@ -1271,9 +1269,7 @@ impl<K, V, S> HashMap<K, V, S>
|
|||
}
|
||||
};
|
||||
bucket.prev(); // reverse iteration
|
||||
if bucket.index() == tail {
|
||||
break;
|
||||
}
|
||||
debug_assert!(elems_left == 0 || bucket.index() != start_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ impl TaggedHashUintPtr {
|
|||
/// when the RawTable is created and is accessible with the `tag` and `set_tag`
|
||||
/// functions.
|
||||
pub struct RawTable<K, V> {
|
||||
capacity: usize,
|
||||
capacity_mask: usize,
|
||||
size: usize,
|
||||
hashes: TaggedHashUintPtr,
|
||||
|
||||
|
|
@ -125,10 +125,13 @@ pub struct RawTable<K, V> {
|
|||
unsafe impl<K: Send, V: Send> Send for RawTable<K, V> {}
|
||||
unsafe impl<K: Sync, V: Sync> Sync for RawTable<K, V> {}
|
||||
|
||||
// An unsafe view of a RawTable bucket
|
||||
// Valid indexes are within [0..table_capacity)
|
||||
pub struct RawBucket<K, V> {
|
||||
hash: *mut HashUint,
|
||||
hash_start: *mut HashUint,
|
||||
// We use *const to ensure covariance with respect to K and V
|
||||
pair: *const (K, V),
|
||||
pair_start: *const (K, V),
|
||||
idx: usize,
|
||||
_marker: marker::PhantomData<(K, V)>,
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +144,6 @@ impl<K, V> Clone for RawBucket<K, V> {
|
|||
|
||||
pub struct Bucket<K, V, M> {
|
||||
raw: RawBucket<K, V>,
|
||||
idx: usize,
|
||||
table: M,
|
||||
}
|
||||
|
||||
|
|
@ -154,13 +156,11 @@ impl<K, V, M: Copy> Clone for Bucket<K, V, M> {
|
|||
|
||||
pub struct EmptyBucket<K, V, M> {
|
||||
raw: RawBucket<K, V>,
|
||||
idx: usize,
|
||||
table: M,
|
||||
}
|
||||
|
||||
pub struct FullBucket<K, V, M> {
|
||||
raw: RawBucket<K, V>,
|
||||
idx: usize,
|
||||
table: M,
|
||||
}
|
||||
|
||||
|
|
@ -232,13 +232,17 @@ fn can_alias_safehash_as_hash() {
|
|||
assert_eq!(size_of::<SafeHash>(), size_of::<HashUint>())
|
||||
}
|
||||
|
||||
// RawBucket methods are unsafe as it's possible to
|
||||
// make a RawBucket point to invalid memory using safe code.
|
||||
impl<K, V> RawBucket<K, V> {
|
||||
unsafe fn offset(self, count: isize) -> RawBucket<K, V> {
|
||||
RawBucket {
|
||||
hash: self.hash.offset(count),
|
||||
pair: self.pair.offset(count),
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
unsafe fn hash(&self) -> *mut HashUint {
|
||||
self.hash_start.offset(self.idx as isize)
|
||||
}
|
||||
unsafe fn pair(&self) -> *mut (K, V) {
|
||||
self.pair_start.offset(self.idx as isize) as *mut (K, V)
|
||||
}
|
||||
unsafe fn hash_pair(&self) -> (*mut HashUint, *mut (K, V)) {
|
||||
(self.hash(), self.pair())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +262,7 @@ impl<K, V, M> FullBucket<K, V, M> {
|
|||
}
|
||||
/// Get the raw index.
|
||||
pub fn index(&self) -> usize {
|
||||
self.idx
|
||||
self.raw.idx
|
||||
}
|
||||
/// Get the raw bucket.
|
||||
pub fn raw(&self) -> RawBucket<K, V> {
|
||||
|
|
@ -280,7 +284,7 @@ impl<K, V, M> EmptyBucket<K, V, M> {
|
|||
impl<K, V, M> Bucket<K, V, M> {
|
||||
/// Get the raw index.
|
||||
pub fn index(&self) -> usize {
|
||||
self.idx
|
||||
self.raw.idx
|
||||
}
|
||||
/// get the table.
|
||||
pub fn into_table(self) -> M {
|
||||
|
|
@ -331,12 +335,11 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
|
|||
Bucket::at_index(table, hash.inspect() as usize)
|
||||
}
|
||||
|
||||
pub fn new_from(r: RawBucket<K, V>, i: usize, t: M)
|
||||
pub fn new_from(r: RawBucket<K, V>, t: M)
|
||||
-> Bucket<K, V, M>
|
||||
{
|
||||
Bucket {
|
||||
raw: r,
|
||||
idx: i,
|
||||
table: t,
|
||||
}
|
||||
}
|
||||
|
|
@ -346,18 +349,16 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
|
|||
// This is an uncommon case though, so avoid it in release builds.
|
||||
debug_assert!(table.capacity() > 0,
|
||||
"Table should have capacity at this point");
|
||||
let ib_index = ib_index & (table.capacity() - 1);
|
||||
let ib_index = ib_index & table.capacity_mask;
|
||||
Bucket {
|
||||
raw: unsafe { table.first_bucket_raw().offset(ib_index as isize) },
|
||||
idx: ib_index,
|
||||
raw: table.raw_bucket_at(ib_index),
|
||||
table: table,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first(table: M) -> Bucket<K, V, M> {
|
||||
Bucket {
|
||||
raw: table.first_bucket_raw(),
|
||||
idx: 0,
|
||||
raw: table.raw_bucket_at(0),
|
||||
table: table,
|
||||
}
|
||||
}
|
||||
|
|
@ -401,48 +402,30 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
|
|||
/// the appropriate types to call most of the other functions in
|
||||
/// this module.
|
||||
pub fn peek(self) -> BucketState<K, V, M> {
|
||||
match unsafe { *self.raw.hash } {
|
||||
match unsafe { *self.raw.hash() } {
|
||||
EMPTY_BUCKET => {
|
||||
Empty(EmptyBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
Full(FullBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Modifies the bucket pointer in place to make it point to the next slot.
|
||||
/// Modifies the bucket in place to make it point to the next slot.
|
||||
pub fn next(&mut self) {
|
||||
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);
|
||||
}
|
||||
self.raw.idx = self.raw.idx.wrapping_add(1) & self.table.capacity_mask;
|
||||
}
|
||||
|
||||
/// Modifies the bucket pointer in place to make it point to the previous slot.
|
||||
/// Modifies the bucket in place to make it point to the previous slot.
|
||||
pub fn prev(&mut self) {
|
||||
let range = self.table.capacity();
|
||||
let new_idx = self.idx.wrapping_sub(1) & (range - 1);
|
||||
let dist = (new_idx as isize).wrapping_sub(self.idx as isize);
|
||||
self.idx = new_idx;
|
||||
unsafe {
|
||||
self.raw = self.raw.offset(dist);
|
||||
}
|
||||
self.raw.idx = self.raw.idx.wrapping_sub(1) & self.table.capacity_mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -458,7 +441,6 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> {
|
|||
pub fn into_bucket(self) -> Bucket<K, V, M> {
|
||||
Bucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
}
|
||||
}
|
||||
|
|
@ -466,7 +448,6 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> {
|
|||
pub fn gap_peek(self) -> Result<GapThenFull<K, V, M>, Bucket<K, V, M>> {
|
||||
let gap = EmptyBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: (),
|
||||
};
|
||||
|
||||
|
|
@ -494,15 +475,14 @@ impl<K, V, M> EmptyBucket<K, V, M>
|
|||
/// Use `make_hash` to construct a `SafeHash` to pass to this function.
|
||||
pub fn put(mut self, hash: SafeHash, key: K, value: V) -> FullBucket<K, V, M> {
|
||||
unsafe {
|
||||
*self.raw.hash = hash.inspect();
|
||||
ptr::write(self.raw.pair as *mut (K, V), (key, value));
|
||||
*self.raw.hash() = hash.inspect();
|
||||
ptr::write(self.raw.pair(), (key, value));
|
||||
|
||||
self.table.borrow_table_mut().size += 1;
|
||||
}
|
||||
|
||||
FullBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
}
|
||||
}
|
||||
|
|
@ -510,15 +490,14 @@ impl<K, V, M> EmptyBucket<K, V, M>
|
|||
/// Puts given key, remain value uninitialized.
|
||||
/// It is only used for inplacement insertion.
|
||||
pub unsafe fn put_key(mut self, hash: SafeHash, key: K) -> FullBucket<K, V, M> {
|
||||
*self.raw.hash = hash.inspect();
|
||||
let pair_mut = self.raw.pair as *mut (K, V);
|
||||
ptr::write(&mut (*pair_mut).0, key);
|
||||
*self.raw.hash() = hash.inspect();
|
||||
let pair_ptr = self.raw.pair();
|
||||
ptr::write(&mut (*pair_ptr).0, key);
|
||||
|
||||
self.table.borrow_table_mut().size += 1;
|
||||
|
||||
FullBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
}
|
||||
}
|
||||
|
|
@ -536,7 +515,6 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> {
|
|||
pub fn into_bucket(self) -> Bucket<K, V, M> {
|
||||
Bucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
}
|
||||
}
|
||||
|
|
@ -546,7 +524,6 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> {
|
|||
pub fn stash(self) -> FullBucket<K, V, Self> {
|
||||
FullBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self,
|
||||
}
|
||||
}
|
||||
|
|
@ -560,17 +537,20 @@ impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> {
|
|||
// 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.
|
||||
(self.idx.wrapping_sub(self.hash().inspect() as usize)) & (self.table.capacity() - 1)
|
||||
(self.raw.idx.wrapping_sub(self.hash().inspect() as usize)) & self.table.capacity_mask
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hash(&self) -> SafeHash {
|
||||
unsafe { SafeHash { hash: *self.raw.hash } }
|
||||
unsafe { SafeHash { hash: *self.raw.hash() } }
|
||||
}
|
||||
|
||||
/// Gets references to the key and value at a given index.
|
||||
pub fn read(&self) -> (&K, &V) {
|
||||
unsafe { (&(*self.raw.pair).0, &(*self.raw.pair).1) }
|
||||
unsafe {
|
||||
let pair_ptr = self.raw.pair();
|
||||
(&(*pair_ptr).0, &(*pair_ptr).1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -586,11 +566,10 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
|
|||
self.table.size -= 1;
|
||||
|
||||
unsafe {
|
||||
*self.raw.hash = EMPTY_BUCKET;
|
||||
let (k, v) = ptr::read(self.raw.pair);
|
||||
*self.raw.hash() = EMPTY_BUCKET;
|
||||
let (k, v) = ptr::read(self.raw.pair());
|
||||
(EmptyBucket {
|
||||
raw: self.raw,
|
||||
idx: self.idx,
|
||||
table: self.table,
|
||||
},
|
||||
k,
|
||||
|
|
@ -604,9 +583,9 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
|
|||
pub unsafe fn remove_key(&mut self) {
|
||||
self.table.size -= 1;
|
||||
|
||||
*self.raw.hash = EMPTY_BUCKET;
|
||||
let pair_mut = self.raw.pair as *mut (K, V);
|
||||
ptr::drop_in_place(&mut (*pair_mut).0); // only drop key
|
||||
*self.raw.hash() = EMPTY_BUCKET;
|
||||
let pair_ptr = self.raw.pair();
|
||||
ptr::drop_in_place(&mut (*pair_ptr).0); // only drop key
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -617,8 +596,8 @@ impl<K, V, M> FullBucket<K, V, M>
|
|||
{
|
||||
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);
|
||||
let (old_key, old_val) = ptr::replace(self.raw.pair as *mut (K, V), (k, v));
|
||||
let old_hash = ptr::replace(self.raw.hash() as *mut SafeHash, h);
|
||||
let (old_key, old_val) = ptr::replace(self.raw.pair(), (k, v));
|
||||
|
||||
(old_hash, old_key, old_val)
|
||||
}
|
||||
|
|
@ -630,8 +609,10 @@ impl<K, V, M> FullBucket<K, V, M>
|
|||
{
|
||||
/// Gets mutable references to the key and value at a given index.
|
||||
pub fn read_mut(&mut self) -> (&mut K, &mut V) {
|
||||
let pair_mut = self.raw.pair as *mut (K, V);
|
||||
unsafe { (&mut (*pair_mut).0, &mut (*pair_mut).1) }
|
||||
unsafe {
|
||||
let pair_ptr = self.raw.pair();
|
||||
(&mut (*pair_ptr).0, &mut (*pair_ptr).1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -644,7 +625,10 @@ impl<'t, K, V, M> FullBucket<K, V, M>
|
|||
/// in exchange for this, the returned references have a longer lifetime
|
||||
/// than the references returned by `read()`.
|
||||
pub fn into_refs(self) -> (&'t K, &'t V) {
|
||||
unsafe { (&(*self.raw.pair).0, &(*self.raw.pair).1) }
|
||||
unsafe {
|
||||
let pair_ptr = self.raw.pair();
|
||||
(&(*pair_ptr).0, &(*pair_ptr).1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -654,8 +638,10 @@ impl<'t, K, V, M> FullBucket<K, V, M>
|
|||
/// 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) {
|
||||
let pair_mut = self.raw.pair as *mut (K, V);
|
||||
unsafe { (&mut (*pair_mut).0, &mut (*pair_mut).1) }
|
||||
unsafe {
|
||||
let pair_ptr = self.raw.pair();
|
||||
(&mut (*pair_ptr).0, &mut (*pair_ptr).1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -667,22 +653,23 @@ impl<K, V, M> GapThenFull<K, V, M>
|
|||
&self.full
|
||||
}
|
||||
|
||||
pub fn into_bucket(self) -> Bucket<K, V, M> {
|
||||
self.full.into_bucket()
|
||||
pub fn into_table(self) -> M {
|
||||
self.full.into_table()
|
||||
}
|
||||
|
||||
pub fn shift(mut self) -> Result<GapThenFull<K, V, M>, Bucket<K, V, M>> {
|
||||
unsafe {
|
||||
*self.gap.raw.hash = mem::replace(&mut *self.full.raw.hash, EMPTY_BUCKET);
|
||||
ptr::copy_nonoverlapping(self.full.raw.pair, self.gap.raw.pair as *mut (K, V), 1);
|
||||
let (gap_hash, gap_pair) = self.gap.raw.hash_pair();
|
||||
let (full_hash, full_pair) = self.full.raw.hash_pair();
|
||||
*gap_hash = mem::replace(&mut *full_hash, EMPTY_BUCKET);
|
||||
ptr::copy_nonoverlapping(full_pair, gap_pair, 1);
|
||||
}
|
||||
|
||||
let FullBucket { raw: prev_raw, idx: prev_idx, .. } = self.full;
|
||||
let FullBucket { raw: prev_raw, .. } = self.full;
|
||||
|
||||
match self.full.next().peek() {
|
||||
Full(bucket) => {
|
||||
self.gap.raw = prev_raw;
|
||||
self.gap.idx = prev_idx;
|
||||
|
||||
self.full = bucket;
|
||||
|
||||
|
|
@ -761,7 +748,7 @@ impl<K, V> RawTable<K, V> {
|
|||
if capacity == 0 {
|
||||
return RawTable {
|
||||
size: 0,
|
||||
capacity: 0,
|
||||
capacity_mask: capacity.wrapping_sub(1),
|
||||
hashes: TaggedHashUintPtr::new(EMPTY as *mut HashUint),
|
||||
marker: marker::PhantomData,
|
||||
};
|
||||
|
|
@ -801,25 +788,27 @@ impl<K, V> RawTable<K, V> {
|
|||
let hashes = buffer.offset(hash_offset as isize) as *mut HashUint;
|
||||
|
||||
RawTable {
|
||||
capacity: capacity,
|
||||
capacity_mask: capacity.wrapping_sub(1),
|
||||
size: 0,
|
||||
hashes: TaggedHashUintPtr::new(hashes),
|
||||
marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn first_bucket_raw(&self) -> RawBucket<K, V> {
|
||||
let hashes_size = self.capacity * size_of::<HashUint>();
|
||||
let pairs_size = self.capacity * size_of::<(K, V)>();
|
||||
fn raw_bucket_at(&self, index: usize) -> RawBucket<K, V> {
|
||||
let hashes_size = self.capacity() * size_of::<HashUint>();
|
||||
let pairs_size = self.capacity() * size_of::<(K, V)>();
|
||||
|
||||
let buffer = self.hashes.ptr() as *mut u8;
|
||||
let (pairs_offset, _, oflo) =
|
||||
calculate_offsets(hashes_size, pairs_size, align_of::<(K, V)>());
|
||||
debug_assert!(!oflo, "capacity overflow");
|
||||
|
||||
let buffer = self.hashes.ptr() as *mut u8;
|
||||
unsafe {
|
||||
RawBucket {
|
||||
hash: self.hashes.ptr(),
|
||||
pair: buffer.offset(pairs_offset as isize) as *const _,
|
||||
hash_start: buffer as *mut HashUint,
|
||||
pair_start: buffer.offset(pairs_offset as isize) as *const (K, V),
|
||||
idx: index,
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
@ -837,7 +826,7 @@ impl<K, V> RawTable<K, V> {
|
|||
|
||||
/// The hashtable's capacity, similar to a vector's.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
self.capacity_mask.wrapping_add(1)
|
||||
}
|
||||
|
||||
/// The number of elements ever `put` in the hashtable, minus the number
|
||||
|
|
@ -848,8 +837,8 @@ impl<K, V> RawTable<K, V> {
|
|||
|
||||
fn raw_buckets(&self) -> RawBuckets<K, V> {
|
||||
RawBuckets {
|
||||
raw: self.first_bucket_raw(),
|
||||
hashes_end: unsafe { self.hashes.ptr().offset(self.capacity as isize) },
|
||||
raw: self.raw_bucket_at(0),
|
||||
elems_left: self.size,
|
||||
marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
@ -857,25 +846,23 @@ impl<K, V> RawTable<K, V> {
|
|||
pub fn iter(&self) -> Iter<K, V> {
|
||||
Iter {
|
||||
iter: self.raw_buckets(),
|
||||
elems_left: self.size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<K, V> {
|
||||
IterMut {
|
||||
iter: self.raw_buckets(),
|
||||
elems_left: self.size(),
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> IntoIter<K, V> {
|
||||
let RawBuckets { raw, hashes_end, .. } = self.raw_buckets();
|
||||
let RawBuckets { raw, elems_left, .. } = self.raw_buckets();
|
||||
// Replace the marker regardless of lifetime bounds on parameters.
|
||||
IntoIter {
|
||||
iter: RawBuckets {
|
||||
raw: raw,
|
||||
hashes_end: hashes_end,
|
||||
elems_left: elems_left,
|
||||
marker: marker::PhantomData,
|
||||
},
|
||||
table: self,
|
||||
|
|
@ -883,12 +870,12 @@ impl<K, V> RawTable<K, V> {
|
|||
}
|
||||
|
||||
pub fn drain(&mut self) -> Drain<K, V> {
|
||||
let RawBuckets { raw, hashes_end, .. } = self.raw_buckets();
|
||||
let RawBuckets { raw, elems_left, .. } = self.raw_buckets();
|
||||
// Replace the marker regardless of lifetime bounds on parameters.
|
||||
Drain {
|
||||
iter: RawBuckets {
|
||||
raw: raw,
|
||||
hashes_end: hashes_end,
|
||||
elems_left: elems_left,
|
||||
marker: marker::PhantomData,
|
||||
},
|
||||
table: unsafe { Shared::new(self) },
|
||||
|
|
@ -900,18 +887,16 @@ impl<K, V> RawTable<K, V> {
|
|||
/// state and should only be used for dropping the table's remaining
|
||||
/// entries. It's used in the implementation of Drop.
|
||||
unsafe fn rev_drop_buckets(&mut self) {
|
||||
let first_raw = self.first_bucket_raw();
|
||||
let mut raw = first_raw.offset(self.capacity as isize);
|
||||
// initialize the raw bucket past the end of the table
|
||||
let mut raw = self.raw_bucket_at(self.capacity());
|
||||
let mut elems_left = self.size;
|
||||
|
||||
while elems_left != 0 {
|
||||
debug_assert!(raw.hash != first_raw.hash);
|
||||
raw.idx -= 1;
|
||||
|
||||
raw = raw.offset(-1);
|
||||
|
||||
if *raw.hash != EMPTY_BUCKET {
|
||||
if *raw.hash() != EMPTY_BUCKET {
|
||||
elems_left -= 1;
|
||||
ptr::drop_in_place(raw.pair as *mut (K, V));
|
||||
ptr::drop_in_place(raw.pair());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -931,7 +916,7 @@ impl<K, V> RawTable<K, V> {
|
|||
/// this interface is safe, it's not used outside this module.
|
||||
struct RawBuckets<'a, K, V> {
|
||||
raw: RawBucket<K, V>,
|
||||
hashes_end: *mut HashUint,
|
||||
elems_left: usize,
|
||||
|
||||
// Strictly speaking, this should be &'a (K,V), but that would
|
||||
// require that K:'a, and we often use RawBuckets<'static...> for
|
||||
|
|
@ -946,7 +931,7 @@ impl<'a, K, V> Clone for RawBuckets<'a, K, V> {
|
|||
fn clone(&self) -> RawBuckets<'a, K, V> {
|
||||
RawBuckets {
|
||||
raw: self.raw,
|
||||
hashes_end: self.hashes_end,
|
||||
elems_left: self.elems_left,
|
||||
marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
@ -957,25 +942,36 @@ impl<'a, K, V> Iterator for RawBuckets<'a, K, V> {
|
|||
type Item = RawBucket<K, V>;
|
||||
|
||||
fn next(&mut self) -> Option<RawBucket<K, V>> {
|
||||
while self.raw.hash != self.hashes_end {
|
||||
if self.elems_left == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
// We are swapping out the pointer to a bucket and replacing
|
||||
// it with the pointer to the next one.
|
||||
let prev = ptr::replace(&mut self.raw, self.raw.offset(1));
|
||||
if *prev.hash != EMPTY_BUCKET {
|
||||
return Some(prev);
|
||||
let item = self.raw;
|
||||
self.raw.idx += 1;
|
||||
if *item.hash() != EMPTY_BUCKET {
|
||||
self.elems_left -= 1;
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.elems_left, Some(self.elems_left))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for RawBuckets<'a, K, V> {
|
||||
fn len(&self) -> usize {
|
||||
self.elems_left
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over shared references to entries in a table.
|
||||
pub struct Iter<'a, K: 'a, V: 'a> {
|
||||
iter: RawBuckets<'a, K, V>,
|
||||
elems_left: usize,
|
||||
}
|
||||
|
||||
unsafe impl<'a, K: Sync, V: Sync> Sync for Iter<'a, K, V> {}
|
||||
|
|
@ -986,16 +982,13 @@ impl<'a, K, V> Clone for Iter<'a, K, V> {
|
|||
fn clone(&self) -> Iter<'a, K, V> {
|
||||
Iter {
|
||||
iter: self.iter.clone(),
|
||||
elems_left: self.elems_left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Iterator over mutable references to entries in a table.
|
||||
pub struct IterMut<'a, K: 'a, V: 'a> {
|
||||
iter: RawBuckets<'a, K, V>,
|
||||
elems_left: usize,
|
||||
// To ensure invariance with respect to V
|
||||
_marker: marker::PhantomData<&'a mut V>,
|
||||
}
|
||||
|
|
@ -1009,7 +1002,6 @@ impl<'a, K: 'a, V: 'a> IterMut<'a, K, V> {
|
|||
pub fn iter(&self) -> Iter<K, V> {
|
||||
Iter {
|
||||
iter: self.iter.clone(),
|
||||
elems_left: self.elems_left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1027,7 +1019,6 @@ impl<K, V> IntoIter<K, V> {
|
|||
pub fn iter(&self) -> Iter<K, V> {
|
||||
Iter {
|
||||
iter: self.iter.clone(),
|
||||
elems_left: self.table.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1044,11 +1035,8 @@ unsafe impl<'a, K: Send, V: Send> Send for Drain<'a, K, V> {}
|
|||
|
||||
impl<'a, K, V> Drain<'a, K, V> {
|
||||
pub fn iter(&self) -> Iter<K, V> {
|
||||
unsafe {
|
||||
Iter {
|
||||
iter: self.iter.clone(),
|
||||
elems_left: (**self.table).size,
|
||||
}
|
||||
Iter {
|
||||
iter: self.iter.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1057,19 +1045,20 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> {
|
|||
type Item = (&'a K, &'a V);
|
||||
|
||||
fn next(&mut self) -> Option<(&'a K, &'a V)> {
|
||||
self.iter.next().map(|bucket| {
|
||||
self.elems_left -= 1;
|
||||
unsafe { (&(*bucket.pair).0, &(*bucket.pair).1) }
|
||||
self.iter.next().map(|raw| unsafe {
|
||||
let pair_ptr = raw.pair();
|
||||
(&(*pair_ptr).0, &(*pair_ptr).1)
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.elems_left, Some(self.elems_left))
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
|
||||
fn len(&self) -> usize {
|
||||
self.elems_left
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1077,20 +1066,20 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> {
|
|||
type Item = (&'a K, &'a mut V);
|
||||
|
||||
fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
|
||||
self.iter.next().map(|bucket| {
|
||||
self.elems_left -= 1;
|
||||
let pair_mut = bucket.pair as *mut (K, V);
|
||||
unsafe { (&(*pair_mut).0, &mut (*pair_mut).1) }
|
||||
self.iter.next().map(|raw| unsafe {
|
||||
let pair_ptr = raw.pair();
|
||||
(&(*pair_ptr).0, &mut (*pair_ptr).1)
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.elems_left, Some(self.elems_left))
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {
|
||||
fn len(&self) -> usize {
|
||||
self.elems_left
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1098,23 +1087,23 @@ impl<K, V> Iterator for IntoIter<K, V> {
|
|||
type Item = (SafeHash, K, V);
|
||||
|
||||
fn next(&mut self) -> Option<(SafeHash, K, V)> {
|
||||
self.iter.next().map(|bucket| {
|
||||
self.iter.next().map(|raw| {
|
||||
self.table.size -= 1;
|
||||
unsafe {
|
||||
let (k, v) = ptr::read(bucket.pair);
|
||||
(SafeHash { hash: *bucket.hash }, k, v)
|
||||
let (k, v) = ptr::read(raw.pair());
|
||||
(SafeHash { hash: *raw.hash() }, k, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let size = self.table.size();
|
||||
(size, Some(size))
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> ExactSizeIterator for IntoIter<K, V> {
|
||||
fn len(&self) -> usize {
|
||||
self.table.size()
|
||||
self.iter().len()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1123,23 +1112,21 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<(SafeHash, K, V)> {
|
||||
self.iter.next().map(|bucket| {
|
||||
unsafe {
|
||||
(*self.table.as_mut_ptr()).size -= 1;
|
||||
let (k, v) = ptr::read(bucket.pair);
|
||||
(SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET) }, k, v)
|
||||
}
|
||||
self.iter.next().map(|raw| unsafe {
|
||||
(*self.table.as_mut_ptr()).size -= 1;
|
||||
let (k, v) = ptr::read(raw.pair());
|
||||
(SafeHash { hash: ptr::replace(&mut *raw.hash(), EMPTY_BUCKET) }, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let size = unsafe { (**self.table).size() };
|
||||
(size, Some(size))
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> {
|
||||
fn len(&self) -> usize {
|
||||
unsafe { (**self.table).size() }
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1152,30 +1139,21 @@ impl<'a, K: 'a, V: 'a> Drop for Drain<'a, K, V> {
|
|||
impl<K: Clone, V: Clone> Clone for RawTable<K, V> {
|
||||
fn clone(&self) -> RawTable<K, V> {
|
||||
unsafe {
|
||||
let mut new_ht = RawTable::new_uninitialized(self.capacity());
|
||||
let cap = self.capacity();
|
||||
let mut new_ht = RawTable::new_uninitialized(cap);
|
||||
|
||||
{
|
||||
let cap = self.capacity();
|
||||
let mut new_buckets = Bucket::first(&mut new_ht);
|
||||
let mut buckets = Bucket::first(self);
|
||||
while buckets.index() != cap {
|
||||
match buckets.peek() {
|
||||
Full(full) => {
|
||||
let (h, k, v) = {
|
||||
let (k, v) = full.read();
|
||||
(full.hash(), k.clone(), v.clone())
|
||||
};
|
||||
*new_buckets.raw.hash = h.inspect();
|
||||
ptr::write(new_buckets.raw.pair as *mut (K, V), (k, v));
|
||||
}
|
||||
Empty(..) => {
|
||||
*new_buckets.raw.hash = EMPTY_BUCKET;
|
||||
}
|
||||
}
|
||||
new_buckets.next();
|
||||
buckets.next();
|
||||
let mut new_buckets = new_ht.raw_bucket_at(0);
|
||||
let mut buckets = self.raw_bucket_at(0);
|
||||
while buckets.idx < cap {
|
||||
*new_buckets.hash() = *buckets.hash();
|
||||
if *new_buckets.hash() != EMPTY_BUCKET {
|
||||
let pair_ptr = buckets.pair();
|
||||
let kv = ((*pair_ptr).0.clone(), (*pair_ptr).1.clone());
|
||||
ptr::write(new_buckets.pair(), kv);
|
||||
}
|
||||
};
|
||||
buckets.idx += 1;
|
||||
new_buckets.idx += 1;
|
||||
}
|
||||
|
||||
new_ht.size = self.size();
|
||||
|
||||
|
|
@ -1186,7 +1164,7 @@ impl<K: Clone, V: Clone> Clone for RawTable<K, V> {
|
|||
|
||||
unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
|
||||
fn drop(&mut self) {
|
||||
if self.capacity == 0 {
|
||||
if self.capacity() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1202,8 +1180,8 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
let hashes_size = self.capacity * size_of::<HashUint>();
|
||||
let pairs_size = self.capacity * size_of::<(K, V)>();
|
||||
let hashes_size = self.capacity() * size_of::<HashUint>();
|
||||
let pairs_size = self.capacity() * size_of::<(K, V)>();
|
||||
let (align, _, size, oflo) = calculate_allocation(hashes_size,
|
||||
align_of::<HashUint>(),
|
||||
pairs_size,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue