|
|
@ -19,8 +19,9 @@ use fmt::{self, Debug}; |
|
|
|
use hash::{Hash, Hasher, BuildHasher, SipHasher13}; |
|
|
|
use hash::{Hash, Hasher, BuildHasher, SipHasher13}; |
|
|
|
use iter::{FromIterator, FusedIterator}; |
|
|
|
use iter::{FromIterator, FusedIterator}; |
|
|
|
use mem::{self, replace}; |
|
|
|
use mem::{self, replace}; |
|
|
|
use ops::{Deref, Index}; |
|
|
|
use ops::{Deref, Index, InPlace, Place, Placer}; |
|
|
|
use rand::{self, Rng}; |
|
|
|
use rand::{self, Rng}; |
|
|
|
|
|
|
|
use ptr; |
|
|
|
|
|
|
|
|
|
|
|
use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; |
|
|
|
use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; |
|
|
|
use super::table::BucketState::{Empty, Full}; |
|
|
|
use super::table::BucketState::{Empty, Full}; |
|
|
@ -177,16 +178,51 @@ impl DefaultResizePolicy { |
|
|
|
// element.
|
|
|
|
// element.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// FIXME(Gankro, pczarn): review the proof and put it all in a separate README.md
|
|
|
|
// FIXME(Gankro, pczarn): review the proof and put it all in a separate README.md
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Adaptive early resizing
|
|
|
|
|
|
|
|
// ----------------------
|
|
|
|
|
|
|
|
// To protect against degenerate performance scenarios (including DOS attacks),
|
|
|
|
|
|
|
|
// the implementation includes an adaptive behavior that can resize the map
|
|
|
|
|
|
|
|
// early (before its capacity is exceeded) when suspiciously long probe sequences
|
|
|
|
|
|
|
|
// are encountered.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// With this algorithm in place it would be possible to turn a CPU attack into
|
|
|
|
|
|
|
|
// a memory attack due to the aggressive resizing. To prevent that the
|
|
|
|
|
|
|
|
// adaptive behavior only triggers when the map is at least half full.
|
|
|
|
|
|
|
|
// This reduces the effectiveness of the algorithm but also makes it completely safe.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// The previous safety measure also prevents degenerate interactions with
|
|
|
|
|
|
|
|
// really bad quality hash algorithms that can make normal inputs look like a
|
|
|
|
|
|
|
|
// DOS attack.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
const DISPLACEMENT_THRESHOLD: usize = 128; |
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// The threshold of 128 is chosen to minimize the chance of exceeding it.
|
|
|
|
|
|
|
|
// In particular, we want that chance to be less than 10^-8 with a load of 90%.
|
|
|
|
|
|
|
|
// For displacement, the smallest constant that fits our needs is 90,
|
|
|
|
|
|
|
|
// so we round that up to 128.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// At a load factor of α, the odds of finding the target bucket after exactly n
|
|
|
|
|
|
|
|
// unsuccesful probes[1] are
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Pr_α{displacement = n} =
|
|
|
|
|
|
|
|
// (1 - α) / α * ∑_{k≥1} e^(-kα) * (kα)^(k+n) / (k + n)! * (1 - kα / (k + n + 1))
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// We use this formula to find the probability of triggering the adaptive behavior
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// Pr_0.909{displacement > 128} = 1.601 * 10^-11
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// 1. Alfredo Viola (2005). Distributional analysis of Robin Hood linear probing
|
|
|
|
|
|
|
|
// hashing with buckets.
|
|
|
|
|
|
|
|
|
|
|
|
/// A hash map implementation which uses linear probing with Robin Hood bucket
|
|
|
|
/// A hash map implemented with linear probing and Robin Hood bucket stealing.
|
|
|
|
/// stealing.
|
|
|
|
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// By default, `HashMap` uses a hashing algorithm selected to provide
|
|
|
|
/// By default, `HashMap` uses a hashing algorithm selected to provide
|
|
|
|
/// resistance against HashDoS attacks. The algorithm is randomly seeded, and a
|
|
|
|
/// resistance against HashDoS attacks. The algorithm is randomly seeded, and a
|
|
|
|
/// reasonable best-effort is made to generate this seed from a high quality,
|
|
|
|
/// reasonable best-effort is made to generate this seed from a high quality,
|
|
|
|
/// secure source of randomness provided by the host without blocking the
|
|
|
|
/// secure source of randomness provided by the host without blocking the
|
|
|
|
/// program. Because of this, the randomness of the seed is dependant on the
|
|
|
|
/// program. Because of this, the randomness of the seed depends on the output
|
|
|
|
/// quality of the system's random number generator at the time it is created.
|
|
|
|
/// quality of the system's random number generator when the seed is created.
|
|
|
|
/// In particular, seeds generated when the system's entropy pool is abnormally
|
|
|
|
/// In particular, seeds generated when the system's entropy pool is abnormally
|
|
|
|
/// low such as during system boot may be of a lower quality.
|
|
|
|
/// low such as during system boot may be of a lower quality.
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -198,9 +234,8 @@ impl DefaultResizePolicy { |
|
|
|
/// attacks such as HashDoS.
|
|
|
|
/// attacks such as HashDoS.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// The hashing algorithm can be replaced on a per-`HashMap` basis using the
|
|
|
|
/// The hashing algorithm can be replaced on a per-`HashMap` basis using the
|
|
|
|
/// `HashMap::default`, `HashMap::with_hasher`, and
|
|
|
|
/// [`default`], [`with_hasher`], and [`with_capacity_and_hasher`] methods. Many
|
|
|
|
/// `HashMap::with_capacity_and_hasher` methods. Many alternative algorithms
|
|
|
|
/// alternative algorithms are available on crates.io, such as the [`fnv`] crate.
|
|
|
|
/// are available on crates.io, such as the `fnv` crate.
|
|
|
|
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although
|
|
|
|
/// It is required that the keys implement the [`Eq`] and [`Hash`] traits, although
|
|
|
|
/// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`.
|
|
|
|
/// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`.
|
|
|
@ -302,6 +337,10 @@ impl DefaultResizePolicy { |
|
|
|
/// [`PartialEq`]: ../../std/cmp/trait.PartialEq.html
|
|
|
|
/// [`PartialEq`]: ../../std/cmp/trait.PartialEq.html
|
|
|
|
/// [`RefCell`]: ../../std/cell/struct.RefCell.html
|
|
|
|
/// [`RefCell`]: ../../std/cell/struct.RefCell.html
|
|
|
|
/// [`Cell`]: ../../std/cell/struct.Cell.html
|
|
|
|
/// [`Cell`]: ../../std/cell/struct.Cell.html
|
|
|
|
|
|
|
|
/// [`default`]: #method.default
|
|
|
|
|
|
|
|
/// [`with_hasher`]: #method.with_hasher
|
|
|
|
|
|
|
|
/// [`with_capacity_and_hasher`]: #method.with_capacity_and_hasher
|
|
|
|
|
|
|
|
/// [`fnv`]: https://crates.io/crates/fnv
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
/// use std::collections::HashMap;
|
|
|
|
/// use std::collections::HashMap;
|
|
|
@ -332,7 +371,7 @@ impl DefaultResizePolicy { |
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// A HashMap with fixed list of elements can be initialized from an array:
|
|
|
|
/// A `HashMap` with fixed list of elements can be initialized from an array:
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
/// use std::collections::HashMap;
|
|
|
|
/// use std::collections::HashMap;
|
|
|
@ -381,7 +420,7 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter |
|
|
|
// Found a hole!
|
|
|
|
// Found a hole!
|
|
|
|
return InternalEntry::Vacant { |
|
|
|
return InternalEntry::Vacant { |
|
|
|
hash: hash, |
|
|
|
hash: hash, |
|
|
|
elem: NoElem(bucket), |
|
|
|
elem: NoElem(bucket, displacement), |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
Full(bucket) => bucket, |
|
|
|
Full(bucket) => bucket, |
|
|
@ -412,42 +451,46 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) { |
|
|
|
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) |
|
|
|
|
|
|
|
-> (K, V, &mut RawTable<K, V>) |
|
|
|
|
|
|
|
{ |
|
|
|
let (empty, retkey, retval) = starting_bucket.take(); |
|
|
|
let (empty, retkey, retval) = starting_bucket.take(); |
|
|
|
let mut gap = match empty.gap_peek() { |
|
|
|
let mut gap = match empty.gap_peek() { |
|
|
|
Some(b) => b, |
|
|
|
Ok(b) => b, |
|
|
|
None => return (retkey, retval), |
|
|
|
Err(b) => return (retkey, retval, b.into_table()), |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
while gap.full().displacement() != 0 { |
|
|
|
while gap.full().displacement() != 0 { |
|
|
|
gap = match gap.shift() { |
|
|
|
gap = match gap.shift() { |
|
|
|
Some(b) => b, |
|
|
|
Ok(b) => b, |
|
|
|
None => break, |
|
|
|
Err(b) => { |
|
|
|
|
|
|
|
return (retkey, retval, b.into_table()); |
|
|
|
|
|
|
|
}, |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Now we've done all our shifting. Return the value we grabbed earlier.
|
|
|
|
// Now we've done all our shifting. Return the value we grabbed earlier.
|
|
|
|
(retkey, retval) |
|
|
|
(retkey, retval, gap.into_table()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Perform robin hood bucket stealing at the given `bucket`. You must
|
|
|
|
/// Perform robin hood bucket stealing at the given `bucket`. You must
|
|
|
|
/// also pass that bucket's displacement so we don't have to recalculate it.
|
|
|
|
/// also pass that bucket's displacement so we don't have to recalculate it.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// `hash`, `k`, and `v` are the elements to "robin hood" into the hashtable.
|
|
|
|
/// `hash`, `key`, and `val` are the elements to "robin hood" into the hashtable.
|
|
|
|
fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, |
|
|
|
fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, |
|
|
|
mut displacement: usize, |
|
|
|
mut displacement: usize, |
|
|
|
mut hash: SafeHash, |
|
|
|
mut hash: SafeHash, |
|
|
|
mut key: K, |
|
|
|
mut key: K, |
|
|
|
mut val: V) |
|
|
|
mut val: V) |
|
|
|
-> &'a mut V { |
|
|
|
-> FullBucketMut<'a, K, V> { |
|
|
|
let starting_index = bucket.index(); |
|
|
|
|
|
|
|
let size = bucket.table().size(); |
|
|
|
let size = bucket.table().size(); |
|
|
|
// Save the *starting point*.
|
|
|
|
let raw_capacity = bucket.table().capacity(); |
|
|
|
let mut bucket = bucket.stash(); |
|
|
|
|
|
|
|
// There can be at most `size - dib` buckets to displace, because
|
|
|
|
// There can be at most `size - dib` buckets to displace, because
|
|
|
|
// in the worst case, there are `size` elements and we already are
|
|
|
|
// in the worst case, there are `size` elements and we already are
|
|
|
|
// `displacement` buckets away from the initial one.
|
|
|
|
// `displacement` buckets away from the initial one.
|
|
|
|
let idx_end = starting_index + size - bucket.displacement(); |
|
|
|
let idx_end = (bucket.index() + size - bucket.displacement()) % raw_capacity; |
|
|
|
|
|
|
|
// Save the *starting point*.
|
|
|
|
|
|
|
|
let mut bucket = bucket.stash(); |
|
|
|
|
|
|
|
|
|
|
|
loop { |
|
|
|
loop { |
|
|
|
let (old_hash, old_key, old_val) = bucket.replace(hash, key, val); |
|
|
|
let (old_hash, old_key, old_val) = bucket.replace(hash, key, val); |
|
|
@ -471,7 +514,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, |
|
|
|
// bucket, which is a FullBucket on top of a
|
|
|
|
// bucket, which is a FullBucket on top of a
|
|
|
|
// FullBucketMut, into just one FullBucketMut. The "table"
|
|
|
|
// FullBucketMut, into just one FullBucketMut. The "table"
|
|
|
|
// refers to the inner FullBucketMut in this context.
|
|
|
|
// refers to the inner FullBucketMut in this context.
|
|
|
|
return bucket.into_table().into_mut_refs().1; |
|
|
|
return bucket.into_table(); |
|
|
|
} |
|
|
|
} |
|
|
|
Full(bucket) => bucket, |
|
|
|
Full(bucket) => bucket, |
|
|
|
}; |
|
|
|
}; |
|
|
@ -523,11 +566,8 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
// The caller should ensure that invariants by Robin Hood Hashing hold
|
|
|
|
// The caller should ensure that invariants by Robin Hood Hashing hold
|
|
|
|
// and that there's space in the underlying table.
|
|
|
|
// and that there's space in the underlying table.
|
|
|
|
fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) { |
|
|
|
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); |
|
|
|
let mut buckets = Bucket::new(&mut self.table, hash); |
|
|
|
// note that buckets.index() keeps increasing
|
|
|
|
let start_index = buckets.index(); |
|
|
|
// even if the pointer wraps back to the first bucket.
|
|
|
|
|
|
|
|
let limit_bucket = buckets.index() + raw_cap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loop { |
|
|
|
loop { |
|
|
|
// We don't need to compare hashes for value swap.
|
|
|
|
// We don't need to compare hashes for value swap.
|
|
|
@ -540,7 +580,7 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
Full(b) => b.into_bucket(), |
|
|
|
Full(b) => b.into_bucket(), |
|
|
|
}; |
|
|
|
}; |
|
|
|
buckets.next(); |
|
|
|
buckets.next(); |
|
|
|
debug_assert!(buckets.index() < limit_bucket); |
|
|
|
debug_assert!(buckets.index() != start_index); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -612,12 +652,13 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Creates an empty `HashMap` with the specified capacity, using `hasher`
|
|
|
|
/// Creates an empty `HashMap` with the specified capacity, using `hash_builder`
|
|
|
|
/// to hash the keys.
|
|
|
|
/// to hash the keys.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// The hash map will be able to hold at least `capacity` elements without
|
|
|
|
/// The hash map will be able to hold at least `capacity` elements without
|
|
|
|
/// reallocating. If `capacity` is 0, the hash map will not allocate.
|
|
|
|
/// reallocating. If `capacity` is 0, the hash map will not allocate.
|
|
|
|
/// Warning: `hasher` is normally randomly generated, and
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Warning: `hash_builder` is normally randomly generated, and
|
|
|
|
/// is designed to allow HashMaps to be resistant to attacks that
|
|
|
|
/// is designed to allow HashMaps to be resistant to attacks that
|
|
|
|
/// cause many collisions and very poor performance. Setting it
|
|
|
|
/// cause many collisions and very poor performance. Setting it
|
|
|
|
/// manually using this function can expose a DoS attack vector.
|
|
|
|
/// manually using this function can expose a DoS attack vector.
|
|
|
@ -644,7 +685,9 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns a reference to the map's hasher.
|
|
|
|
/// Returns a reference to the map's [`BuildHasher`].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`BuildHasher`]: ../../std/hash/trait.BuildHasher.html
|
|
|
|
#[stable(feature = "hashmap_public_hasher", since = "1.9.0")] |
|
|
|
#[stable(feature = "hashmap_public_hasher", since = "1.9.0")] |
|
|
|
pub fn hasher(&self) -> &S { |
|
|
|
pub fn hasher(&self) -> &S { |
|
|
|
&self.hash_builder |
|
|
|
&self.hash_builder |
|
|
@ -680,7 +723,9 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// Panics if the new allocation size overflows `usize`.
|
|
|
|
/// Panics if the new allocation size overflows [`usize`].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`usize`]: ../../std/primitive.usize.html
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -696,6 +741,11 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
let min_cap = self.len().checked_add(additional).expect("reserve overflow"); |
|
|
|
let min_cap = self.len().checked_add(additional).expect("reserve overflow"); |
|
|
|
let raw_cap = self.resize_policy.raw_capacity(min_cap); |
|
|
|
let raw_cap = self.resize_policy.raw_capacity(min_cap); |
|
|
|
self.resize(raw_cap); |
|
|
|
self.resize(raw_cap); |
|
|
|
|
|
|
|
} else if self.table.tag() && remaining <= self.len() { |
|
|
|
|
|
|
|
// Probe sequence is too long and table is half full,
|
|
|
|
|
|
|
|
// resize early to reduce probing length.
|
|
|
|
|
|
|
|
let new_capacity = self.table.capacity() * 2; |
|
|
|
|
|
|
|
self.resize(new_capacity); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -704,6 +754,8 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
/// 1) Ensure `new_raw_cap` is enough for all the elements, accounting
|
|
|
|
/// 1) Ensure `new_raw_cap` is enough for all the elements, accounting
|
|
|
|
/// for the load factor.
|
|
|
|
/// for the load factor.
|
|
|
|
/// 2) Ensure `new_raw_cap` is a power of two or zero.
|
|
|
|
/// 2) Ensure `new_raw_cap` is a power of two or zero.
|
|
|
|
|
|
|
|
#[inline(never)] |
|
|
|
|
|
|
|
#[cold] |
|
|
|
fn resize(&mut self, new_raw_cap: usize) { |
|
|
|
fn resize(&mut self, new_raw_cap: usize) { |
|
|
|
assert!(self.table.size() <= new_raw_cap); |
|
|
|
assert!(self.table.size() <= new_raw_cap); |
|
|
|
assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0); |
|
|
|
assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0); |
|
|
@ -711,42 +763,11 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
let mut old_table = replace(&mut self.table, RawTable::new(new_raw_cap)); |
|
|
|
let mut old_table = replace(&mut self.table, RawTable::new(new_raw_cap)); |
|
|
|
let old_size = old_table.size(); |
|
|
|
let old_size = old_table.size(); |
|
|
|
|
|
|
|
|
|
|
|
if old_table.capacity() == 0 || old_table.size() == 0 { |
|
|
|
if old_table.size() == 0 { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Grow the table.
|
|
|
|
let mut bucket = Bucket::head_bucket(&mut old_table); |
|
|
|
// Specialization of the other branch.
|
|
|
|
|
|
|
|
let mut bucket = Bucket::first(&mut old_table); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// "So a few of the first shall be last: for many be called,
|
|
|
|
|
|
|
|
// but few chosen."
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// We'll most likely encounter a few buckets at the beginning that
|
|
|
|
|
|
|
|
// have their initial buckets near the end of the table. They were
|
|
|
|
|
|
|
|
// placed at the beginning as the probe wrapped around the table
|
|
|
|
|
|
|
|
// during insertion. We must skip forward to a bucket that won't
|
|
|
|
|
|
|
|
// get reinserted too early and won't unfairly steal others spot.
|
|
|
|
|
|
|
|
// This eliminates the need for robin hood.
|
|
|
|
|
|
|
|
loop { |
|
|
|
|
|
|
|
bucket = match bucket.peek() { |
|
|
|
|
|
|
|
Full(full) => { |
|
|
|
|
|
|
|
if full.displacement() == 0 { |
|
|
|
|
|
|
|
// This bucket occupies its ideal spot.
|
|
|
|
|
|
|
|
// It indicates the start of another "cluster".
|
|
|
|
|
|
|
|
bucket = full.into_bucket(); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Leaving this bucket in the last cluster for later.
|
|
|
|
|
|
|
|
full.into_bucket() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Empty(b) => { |
|
|
|
|
|
|
|
// Encountered a hole between clusters.
|
|
|
|
|
|
|
|
b.into_bucket() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
bucket.next(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This is how the buckets might be laid out in memory:
|
|
|
|
// This is how the buckets might be laid out in memory:
|
|
|
|
// ($ marks an initialized bucket)
|
|
|
|
// ($ marks an initialized bucket)
|
|
|
@ -831,7 +852,7 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// An iterator visiting all keys in arbitrary order.
|
|
|
|
/// An iterator visiting all keys in arbitrary order.
|
|
|
|
/// Iterator element type is `&'a K`.
|
|
|
|
/// The iterator element type is `&'a K`.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -853,7 +874,7 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// An iterator visiting all values in arbitrary order.
|
|
|
|
/// An iterator visiting all values in arbitrary order.
|
|
|
|
/// Iterator element type is `&'a V`.
|
|
|
|
/// The iterator element type is `&'a V`.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -875,7 +896,7 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// An iterator visiting all values mutably in arbitrary order.
|
|
|
|
/// An iterator visiting all values mutably in arbitrary order.
|
|
|
|
/// Iterator element type is `&'a mut V`.
|
|
|
|
/// The iterator element type is `&'a mut V`.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -902,7 +923,7 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// An iterator visiting all key-value pairs in arbitrary order.
|
|
|
|
/// An iterator visiting all key-value pairs in arbitrary order.
|
|
|
|
/// Iterator element type is `(&'a K, &'a V)`.
|
|
|
|
/// The iterator element type is `(&'a K, &'a V)`.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -925,7 +946,7 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
|
|
|
|
|
|
|
|
/// An iterator visiting all key-value pairs in arbitrary order,
|
|
|
|
/// An iterator visiting all key-value pairs in arbitrary order,
|
|
|
|
/// with mutable references to the values.
|
|
|
|
/// with mutable references to the values.
|
|
|
|
/// Iterator element type is `(&'a K, &'a mut V)`.
|
|
|
|
/// The iterator element type is `(&'a K, &'a mut V)`.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -974,7 +995,9 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
pub fn entry(&mut self, key: K) -> Entry<K, V> { |
|
|
|
pub fn entry(&mut self, key: K) -> Entry<K, V> { |
|
|
|
// Gotta resize now.
|
|
|
|
// Gotta resize now.
|
|
|
|
self.reserve(1); |
|
|
|
self.reserve(1); |
|
|
|
self.search_mut(&key).into_entry(key).expect("unreachable") |
|
|
|
let hash = self.make_hash(&key); |
|
|
|
|
|
|
|
search_hashed(&mut self.table, hash, |q| q.eq(&key)) |
|
|
|
|
|
|
|
.into_entry(key).expect("unreachable") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns the number of elements in the map.
|
|
|
|
/// Returns the number of elements in the map.
|
|
|
@ -1141,13 +1164,14 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
|
|
|
|
|
|
|
|
/// Inserts a key-value pair into the map.
|
|
|
|
/// Inserts a key-value pair into the map.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// If the map did not have this key present, `None` is returned.
|
|
|
|
/// If the map did not have this key present, [`None`] is returned.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// If the map did have this key present, the value is updated, and the old
|
|
|
|
/// If the map did have this key present, the value is updated, and the old
|
|
|
|
/// value is returned. The key is not updated, though; this matters for
|
|
|
|
/// value is returned. The key is not updated, though; this matters for
|
|
|
|
/// types that can be `==` without being identical. See the [module-level
|
|
|
|
/// types that can be `==` without being identical. See the [module-level
|
|
|
|
/// documentation] for more.
|
|
|
|
/// documentation] for more.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`None`]: ../../std/option/enum.Option.html#variant.None
|
|
|
|
/// [module-level documentation]: index.html#insert-and-complex-keys
|
|
|
|
/// [module-level documentation]: index.html#insert-and-complex-keys
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
@ -1201,6 +1225,55 @@ impl<K, V, S> HashMap<K, V, S> |
|
|
|
|
|
|
|
|
|
|
|
self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1) |
|
|
|
self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Retains only the elements specified by the predicate.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// In other words, remove all pairs `(k, v)` such that `f(&k,&mut v)` returns `false`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Examples
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
|
|
/// use std::collections::HashMap;
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// let mut map: HashMap<isize, isize> = (0..8).map(|x|(x, x*10)).collect();
|
|
|
|
|
|
|
|
/// map.retain(|&k, _| k % 2 == 0);
|
|
|
|
|
|
|
|
/// assert_eq!(map.len(), 4);
|
|
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
|
|
#[stable(feature = "retain_hash_collection", since = "1.18.0")] |
|
|
|
|
|
|
|
pub fn retain<F>(&mut self, mut f: F) |
|
|
|
|
|
|
|
where F: FnMut(&K, &mut V) -> bool |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
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 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_raw = full.raw(); |
|
|
|
|
|
|
|
let (_, _, t) = pop_internal(full); |
|
|
|
|
|
|
|
Bucket::new_from(prev_raw, t) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
full.into_bucket() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
Empty(b) => { |
|
|
|
|
|
|
|
b.into_bucket() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
bucket.prev(); // reverse iteration
|
|
|
|
|
|
|
|
debug_assert!(elems_left == 0 || bucket.index() != start_index); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
@ -1262,7 +1335,13 @@ impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap<K, V, S> |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// HashMap iterator.
|
|
|
|
/// An iterator over the entries of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`iter`] method on [`HashMap`]. See its
|
|
|
|
|
|
|
|
/// documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`iter`]: struct.HashMap.html#method.iter
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub struct Iter<'a, K: 'a, V: 'a> { |
|
|
|
pub struct Iter<'a, K: 'a, V: 'a> { |
|
|
|
inner: table::Iter<'a, K, V>, |
|
|
|
inner: table::Iter<'a, K, V>, |
|
|
@ -1276,19 +1355,46 @@ impl<'a, K, V> Clone for Iter<'a, K, V> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// HashMap mutable values iterator.
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<'a, K: Debug, V: Debug> fmt::Debug for Iter<'a, K, V> { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.clone()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// A mutable iterator over the entries of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`iter_mut`] method on [`HashMap`]. See its
|
|
|
|
|
|
|
|
/// documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`iter_mut`]: struct.HashMap.html#method.iter_mut
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub struct IterMut<'a, K: 'a, V: 'a> { |
|
|
|
pub struct IterMut<'a, K: 'a, V: 'a> { |
|
|
|
inner: table::IterMut<'a, K, V>, |
|
|
|
inner: table::IterMut<'a, K, V>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// HashMap move iterator.
|
|
|
|
/// An owning iterator over the entries of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`into_iter`] method on [`HashMap`][`HashMap`]
|
|
|
|
|
|
|
|
/// (provided by the `IntoIterator` trait). See its documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`into_iter`]: struct.HashMap.html#method.into_iter
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub struct IntoIter<K, V> { |
|
|
|
pub struct IntoIter<K, V> { |
|
|
|
inner: table::IntoIter<K, V>, |
|
|
|
pub(super) inner: table::IntoIter<K, V>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// HashMap keys iterator.
|
|
|
|
/// An iterator over the keys of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`keys`] method on [`HashMap`]. See its
|
|
|
|
|
|
|
|
/// documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`keys`]: struct.HashMap.html#method.keys
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub struct Keys<'a, K: 'a, V: 'a> { |
|
|
|
pub struct Keys<'a, K: 'a, V: 'a> { |
|
|
|
inner: Iter<'a, K, V>, |
|
|
|
inner: Iter<'a, K, V>, |
|
|
@ -1302,7 +1408,22 @@ impl<'a, K, V> Clone for Keys<'a, K, V> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// HashMap values iterator.
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<'a, K: Debug, V> fmt::Debug for Keys<'a, K, V> { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.clone()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// An iterator over the values of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`values`] method on [`HashMap`]. See its
|
|
|
|
|
|
|
|
/// documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`values`]: struct.HashMap.html#method.values
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub struct Values<'a, K: 'a, V: 'a> { |
|
|
|
pub struct Values<'a, K: 'a, V: 'a> { |
|
|
|
inner: Iter<'a, K, V>, |
|
|
|
inner: Iter<'a, K, V>, |
|
|
@ -1316,13 +1437,34 @@ impl<'a, K, V> Clone for Values<'a, K, V> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// HashMap drain iterator.
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<'a, K, V: Debug> fmt::Debug for Values<'a, K, V> { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.clone()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// A draining iterator over the entries of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`drain`] method on [`HashMap`]. See its
|
|
|
|
|
|
|
|
/// documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`drain`]: struct.HashMap.html#method.drain
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "drain", since = "1.6.0")] |
|
|
|
#[stable(feature = "drain", since = "1.6.0")] |
|
|
|
pub struct Drain<'a, K: 'a, V: 'a> { |
|
|
|
pub struct Drain<'a, K: 'a, V: 'a> { |
|
|
|
inner: table::Drain<'a, K, V>, |
|
|
|
pub(super) inner: table::Drain<'a, K, V>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Mutable HashMap values iterator.
|
|
|
|
/// A mutable iterator over the values of a `HashMap`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its
|
|
|
|
|
|
|
|
/// documentation for more.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// [`values_mut`]: struct.HashMap.html#method.values_mut
|
|
|
|
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
#[stable(feature = "map_values_mut", since = "1.10.0")] |
|
|
|
#[stable(feature = "map_values_mut", since = "1.10.0")] |
|
|
|
pub struct ValuesMut<'a, K: 'a, V: 'a> { |
|
|
|
pub struct ValuesMut<'a, K: 'a, V: 'a> { |
|
|
|
inner: IterMut<'a, K, V>, |
|
|
|
inner: IterMut<'a, K, V>, |
|
|
@ -1369,19 +1511,20 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// A view into a single location in a map, which may be vacant or occupied.
|
|
|
|
/// A view into a single entry in a map, which may either be vacant or occupied.
|
|
|
|
/// This enum is constructed from the [`entry`] method on [`HashMap`].
|
|
|
|
///
|
|
|
|
|
|
|
|
/// This `enum` is constructed from the [`entry`] method on [`HashMap`].
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
/// [`HashMap`]: struct.HashMap.html
|
|
|
|
/// [`entry`]: struct.HashMap.html#method.entry
|
|
|
|
/// [`entry`]: struct.HashMap.html#method.entry
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub enum Entry<'a, K: 'a, V: 'a> { |
|
|
|
pub enum Entry<'a, K: 'a, V: 'a> { |
|
|
|
/// An occupied Entry.
|
|
|
|
/// An occupied entry.
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
Occupied(#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
Occupied(#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
OccupiedEntry<'a, K, V>), |
|
|
|
OccupiedEntry<'a, K, V>), |
|
|
|
|
|
|
|
|
|
|
|
/// A vacant Entry.
|
|
|
|
/// A vacant entry.
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
Vacant(#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
Vacant(#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
VacantEntry<'a, K, V>), |
|
|
|
VacantEntry<'a, K, V>), |
|
|
@ -1405,7 +1548,7 @@ impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for Entry<'a, K, V> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// A view into a single occupied location in a HashMap.
|
|
|
|
/// A view into an occupied entry in a `HashMap`.
|
|
|
|
/// It is part of the [`Entry`] enum.
|
|
|
|
/// It is part of the [`Entry`] enum.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [`Entry`]: enum.Entry.html
|
|
|
|
/// [`Entry`]: enum.Entry.html
|
|
|
@ -1425,7 +1568,7 @@ impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// A view into a single empty location in a HashMap.
|
|
|
|
/// A view into a vacant entry in a `HashMap`.
|
|
|
|
/// It is part of the [`Entry`] enum.
|
|
|
|
/// It is part of the [`Entry`] enum.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [`Entry`]: enum.Entry.html
|
|
|
|
/// [`Entry`]: enum.Entry.html
|
|
|
@ -1451,7 +1594,7 @@ enum VacantEntryState<K, V, M> { |
|
|
|
/// and will kick the current one out on insertion.
|
|
|
|
/// and will kick the current one out on insertion.
|
|
|
|
NeqElem(FullBucket<K, V, M>, usize), |
|
|
|
NeqElem(FullBucket<K, V, M>, usize), |
|
|
|
/// The index is genuinely vacant.
|
|
|
|
/// The index is genuinely vacant.
|
|
|
|
NoElem(EmptyBucket<K, V, M>), |
|
|
|
NoElem(EmptyBucket<K, V, M>, usize), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
@ -1557,6 +1700,18 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} |
|
|
|
impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<'a, K, V> fmt::Debug for IterMut<'a, K, V> |
|
|
|
|
|
|
|
where K: fmt::Debug, |
|
|
|
|
|
|
|
V: fmt::Debug, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.inner.iter()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
impl<K, V> Iterator for IntoIter<K, V> { |
|
|
|
impl<K, V> Iterator for IntoIter<K, V> { |
|
|
|
type Item = (K, V); |
|
|
|
type Item = (K, V); |
|
|
@ -1580,6 +1735,15 @@ impl<K, V> ExactSizeIterator for IntoIter<K, V> { |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
impl<K, V> FusedIterator for IntoIter<K, V> {} |
|
|
|
impl<K, V> FusedIterator for IntoIter<K, V> {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<K: Debug, V: Debug> fmt::Debug for IntoIter<K, V> { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.inner.iter()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
impl<'a, K, V> Iterator for Keys<'a, K, V> { |
|
|
|
impl<'a, K, V> Iterator for Keys<'a, K, V> { |
|
|
|
type Item = &'a K; |
|
|
|
type Item = &'a K; |
|
|
@ -1649,6 +1813,18 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} |
|
|
|
impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<'a, K, V> fmt::Debug for ValuesMut<'a, K, V> |
|
|
|
|
|
|
|
where K: fmt::Debug, |
|
|
|
|
|
|
|
V: fmt::Debug, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.inner.inner.iter()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "drain", since = "1.6.0")] |
|
|
|
#[stable(feature = "drain", since = "1.6.0")] |
|
|
|
impl<'a, K, V> Iterator for Drain<'a, K, V> { |
|
|
|
impl<'a, K, V> Iterator for Drain<'a, K, V> { |
|
|
|
type Item = (K, V); |
|
|
|
type Item = (K, V); |
|
|
@ -1672,6 +1848,92 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
#[unstable(feature = "fused", issue = "35602")] |
|
|
|
impl<'a, K, V> FusedIterator for Drain<'a, K, V> {} |
|
|
|
impl<'a, K, V> FusedIterator for Drain<'a, K, V> {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl<'a, K, V> fmt::Debug for Drain<'a, K, V> |
|
|
|
|
|
|
|
where K: fmt::Debug, |
|
|
|
|
|
|
|
V: fmt::Debug, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_list() |
|
|
|
|
|
|
|
.entries(self.inner.iter()) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// A place for insertion to a `Entry`.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// See [`HashMap::entry`](struct.HashMap.html#method.entry) for details.
|
|
|
|
|
|
|
|
#[must_use = "places do nothing unless written to with `<-` syntax"] |
|
|
|
|
|
|
|
#[unstable(feature = "collection_placement",
|
|
|
|
|
|
|
|
reason = "struct name and placement protocol is subject to change", |
|
|
|
|
|
|
|
issue = "30172")] |
|
|
|
|
|
|
|
pub struct EntryPlace<'a, K: 'a, V: 'a> { |
|
|
|
|
|
|
|
bucket: FullBucketMut<'a, K, V>, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[unstable(feature = "collection_placement",
|
|
|
|
|
|
|
|
reason = "struct name and placement protocol is subject to change", |
|
|
|
|
|
|
|
issue = "30172")] |
|
|
|
|
|
|
|
impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for EntryPlace<'a, K, V> { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.debug_struct("EntryPlace") |
|
|
|
|
|
|
|
.field("key", self.bucket.read().0) |
|
|
|
|
|
|
|
.field("value", self.bucket.read().1) |
|
|
|
|
|
|
|
.finish() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[unstable(feature = "collection_placement",
|
|
|
|
|
|
|
|
reason = "struct name and placement protocol is subject to change", |
|
|
|
|
|
|
|
issue = "30172")] |
|
|
|
|
|
|
|
impl<'a, K, V> Drop for EntryPlace<'a, K, V> { |
|
|
|
|
|
|
|
fn drop(&mut self) { |
|
|
|
|
|
|
|
// Inplacement insertion failed. Only key need to drop.
|
|
|
|
|
|
|
|
// The value is failed to insert into map.
|
|
|
|
|
|
|
|
unsafe { self.bucket.remove_key() }; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[unstable(feature = "collection_placement",
|
|
|
|
|
|
|
|
reason = "placement protocol is subject to change", |
|
|
|
|
|
|
|
issue = "30172")] |
|
|
|
|
|
|
|
impl<'a, K, V> Placer<V> for Entry<'a, K, V> { |
|
|
|
|
|
|
|
type Place = EntryPlace<'a, K, V>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn make_place(self) -> EntryPlace<'a, K, V> { |
|
|
|
|
|
|
|
let b = match self { |
|
|
|
|
|
|
|
Occupied(mut o) => { |
|
|
|
|
|
|
|
unsafe { ptr::drop_in_place(o.elem.read_mut().1); } |
|
|
|
|
|
|
|
o.elem |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Vacant(v) => { |
|
|
|
|
|
|
|
unsafe { v.insert_key() } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
EntryPlace { bucket: b } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[unstable(feature = "collection_placement",
|
|
|
|
|
|
|
|
reason = "placement protocol is subject to change", |
|
|
|
|
|
|
|
issue = "30172")] |
|
|
|
|
|
|
|
impl<'a, K, V> Place<V> for EntryPlace<'a, K, V> { |
|
|
|
|
|
|
|
fn pointer(&mut self) -> *mut V { |
|
|
|
|
|
|
|
self.bucket.read_mut().1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[unstable(feature = "collection_placement",
|
|
|
|
|
|
|
|
reason = "placement protocol is subject to change", |
|
|
|
|
|
|
|
issue = "30172")] |
|
|
|
|
|
|
|
impl<'a, K, V> InPlace<V> for EntryPlace<'a, K, V> { |
|
|
|
|
|
|
|
type Owner = (); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe fn finalize(self) { |
|
|
|
|
|
|
|
mem::forget(self); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl<'a, K, V> Entry<'a, K, V> { |
|
|
|
impl<'a, K, V> Entry<'a, K, V> { |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
/// Ensures a value is in the entry by inserting the default if empty, and returns
|
|
|
|
/// Ensures a value is in the entry by inserting the default if empty, and returns
|
|
|
@ -1707,11 +1969,11 @@ impl<'a, K, V> Entry<'a, K, V> { |
|
|
|
/// use std::collections::HashMap;
|
|
|
|
/// use std::collections::HashMap;
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// let mut map: HashMap<&str, String> = HashMap::new();
|
|
|
|
/// let mut map: HashMap<&str, String> = HashMap::new();
|
|
|
|
/// let s = "hoho".to_owned();
|
|
|
|
/// let s = "hoho".to_string();
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// map.entry("poneyland").or_insert_with(|| s);
|
|
|
|
/// map.entry("poneyland").or_insert_with(|| s);
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// assert_eq!(map["poneyland"], "hoho".to_owned());
|
|
|
|
/// assert_eq!(map["poneyland"], "hoho".to_string());
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V { |
|
|
|
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V { |
|
|
|
match self { |
|
|
|
match self { |
|
|
@ -1756,13 +2018,6 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { |
|
|
|
self.elem.read().0 |
|
|
|
self.elem.read().0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Deprecated, renamed to `remove_entry`
|
|
|
|
|
|
|
|
#[unstable(feature = "map_entry_recover_keys", issue = "34285")] |
|
|
|
|
|
|
|
#[rustc_deprecated(since = "1.12.0", reason = "renamed to `remove_entry`")] |
|
|
|
|
|
|
|
pub fn remove_pair(self) -> (K, V) { |
|
|
|
|
|
|
|
self.remove_entry() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Take the ownership of the key and value from the map.
|
|
|
|
/// Take the ownership of the key and value from the map.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
@ -1783,7 +2038,8 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { |
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
#[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] |
|
|
|
#[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] |
|
|
|
pub fn remove_entry(self) -> (K, V) { |
|
|
|
pub fn remove_entry(self) -> (K, V) { |
|
|
|
pop_internal(self.elem) |
|
|
|
let (k, v, _) = pop_internal(self.elem); |
|
|
|
|
|
|
|
(k, v) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Gets a reference to the value in the entry.
|
|
|
|
/// Gets a reference to the value in the entry.
|
|
|
@ -1961,9 +2217,40 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { |
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
#[stable(feature = "rust1", since = "1.0.0")] |
|
|
|
pub fn insert(self, value: V) -> &'a mut V { |
|
|
|
pub fn insert(self, value: V) -> &'a mut V { |
|
|
|
|
|
|
|
let b = match self.elem { |
|
|
|
|
|
|
|
NeqElem(mut bucket, disp) => { |
|
|
|
|
|
|
|
if disp >= DISPLACEMENT_THRESHOLD { |
|
|
|
|
|
|
|
bucket.table_mut().set_tag(true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
robin_hood(bucket, disp, self.hash, self.key, value) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
NoElem(mut bucket, disp) => { |
|
|
|
|
|
|
|
if disp >= DISPLACEMENT_THRESHOLD { |
|
|
|
|
|
|
|
bucket.table_mut().set_tag(true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bucket.put(self.hash, self.key, value) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
b.into_mut_refs().1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Only used for InPlacement insert. Avoid unnecessary value copy.
|
|
|
|
|
|
|
|
// The value remains uninitialized.
|
|
|
|
|
|
|
|
unsafe fn insert_key(self) -> FullBucketMut<'a, K, V> { |
|
|
|
match self.elem { |
|
|
|
match self.elem { |
|
|
|
NeqElem(bucket, disp) => robin_hood(bucket, disp, self.hash, self.key, value), |
|
|
|
NeqElem(mut bucket, disp) => { |
|
|
|
NoElem(bucket) => bucket.put(self.hash, self.key, value).into_mut_refs().1, |
|
|
|
if disp >= DISPLACEMENT_THRESHOLD { |
|
|
|
|
|
|
|
bucket.table_mut().set_tag(true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
let uninit = mem::uninitialized(); |
|
|
|
|
|
|
|
robin_hood(bucket, disp, self.hash, self.key, uninit) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
NoElem(mut bucket, disp) => { |
|
|
|
|
|
|
|
if disp >= DISPLACEMENT_THRESHOLD { |
|
|
|
|
|
|
|
bucket.table_mut().set_tag(true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bucket.put_key(self.hash, self.key) |
|
|
|
|
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -2099,7 +2386,7 @@ impl BuildHasher for RandomState { |
|
|
|
/// [`Hasher`]: ../../hash/trait.Hasher.html
|
|
|
|
/// [`Hasher`]: ../../hash/trait.Hasher.html
|
|
|
|
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] |
|
|
|
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] |
|
|
|
#[allow(deprecated)] |
|
|
|
#[allow(deprecated)] |
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub struct DefaultHasher(SipHasher13); |
|
|
|
pub struct DefaultHasher(SipHasher13); |
|
|
|
|
|
|
|
|
|
|
|
impl DefaultHasher { |
|
|
|
impl DefaultHasher { |
|
|
@ -2117,10 +2404,9 @@ impl DefaultHasher { |
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] |
|
|
|
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")] |
|
|
|
impl Default for DefaultHasher { |
|
|
|
impl Default for DefaultHasher { |
|
|
|
/// Creates a new `DefaultHasher` using [`DefaultHasher::new`]. See
|
|
|
|
/// Creates a new `DefaultHasher` using [`new`]. See its documentation for more.
|
|
|
|
/// [`DefaultHasher::new`] documentation for more information.
|
|
|
|
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [`DefaultHasher::new`]: #method.new
|
|
|
|
/// [`new`]: #method.new
|
|
|
|
fn default() -> DefaultHasher { |
|
|
|
fn default() -> DefaultHasher { |
|
|
|
DefaultHasher::new() |
|
|
|
DefaultHasher::new() |
|
|
|
} |
|
|
|
} |
|
|
@ -2148,6 +2434,13 @@ impl Default for RandomState { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[stable(feature = "std_debug", since = "1.16.0")] |
|
|
|
|
|
|
|
impl fmt::Debug for RandomState { |
|
|
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
|
|
|
f.pad("RandomState { .. }") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> |
|
|
|
impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S> |
|
|
|
where K: Eq + Hash + Borrow<Q>, |
|
|
|
where K: Eq + Hash + Borrow<Q>, |
|
|
|
S: BuildHasher, |
|
|
|
S: BuildHasher, |
|
|
@ -2228,6 +2521,7 @@ mod test_map { |
|
|
|
use super::RandomState; |
|
|
|
use super::RandomState; |
|
|
|
use cell::RefCell; |
|
|
|
use cell::RefCell; |
|
|
|
use rand::{thread_rng, Rng}; |
|
|
|
use rand::{thread_rng, Rng}; |
|
|
|
|
|
|
|
use panic; |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
fn test_zero_capacities() { |
|
|
|
fn test_zero_capacities() { |
|
|
@ -3070,4 +3364,88 @@ mod test_map { |
|
|
|
assert_eq!(a.len(), 1); |
|
|
|
assert_eq!(a.len(), 1); |
|
|
|
assert_eq!(a[key], value); |
|
|
|
assert_eq!(a[key], value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_retain() { |
|
|
|
|
|
|
|
let mut map: HashMap<isize, isize> = (0..100).map(|x|(x, x*10)).collect(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
map.retain(|&k, _| k % 2 == 0); |
|
|
|
|
|
|
|
assert_eq!(map.len(), 50); |
|
|
|
|
|
|
|
assert_eq!(map[&2], 20); |
|
|
|
|
|
|
|
assert_eq!(map[&4], 40); |
|
|
|
|
|
|
|
assert_eq!(map[&6], 60); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_adaptive() { |
|
|
|
|
|
|
|
const TEST_LEN: usize = 5000; |
|
|
|
|
|
|
|
// by cloning we get maps with the same hasher seed
|
|
|
|
|
|
|
|
let mut first = HashMap::new(); |
|
|
|
|
|
|
|
let mut second = first.clone(); |
|
|
|
|
|
|
|
first.extend((0..TEST_LEN).map(|i| (i, i))); |
|
|
|
|
|
|
|
second.extend((TEST_LEN..TEST_LEN * 2).map(|i| (i, i))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (&k, &v) in &second { |
|
|
|
|
|
|
|
let prev_cap = first.capacity(); |
|
|
|
|
|
|
|
let expect_grow = first.len() == prev_cap; |
|
|
|
|
|
|
|
first.insert(k, v); |
|
|
|
|
|
|
|
if !expect_grow && first.capacity() != prev_cap { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
panic!("Adaptive early resize failed"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_placement_in() { |
|
|
|
|
|
|
|
let mut map = HashMap::new(); |
|
|
|
|
|
|
|
map.extend((0..10).map(|i| (i, i))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
map.entry(100) <- 100; |
|
|
|
|
|
|
|
assert_eq!(map[&100], 100); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
map.entry(0) <- 10; |
|
|
|
|
|
|
|
assert_eq!(map[&0], 10); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(map.len(), 11); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_placement_panic() { |
|
|
|
|
|
|
|
let mut map = HashMap::new(); |
|
|
|
|
|
|
|
map.extend((0..10).map(|i| (i, i))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn mkpanic() -> usize { panic!() } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// modify existing key
|
|
|
|
|
|
|
|
// when panic happens, previous key is removed.
|
|
|
|
|
|
|
|
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { map.entry(0) <- mkpanic(); })); |
|
|
|
|
|
|
|
assert_eq!(map.len(), 9); |
|
|
|
|
|
|
|
assert!(!map.contains_key(&0)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add new key
|
|
|
|
|
|
|
|
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { map.entry(100) <- mkpanic(); })); |
|
|
|
|
|
|
|
assert_eq!(map.len(), 9); |
|
|
|
|
|
|
|
assert!(!map.contains_key(&100)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn test_placement_drop() { |
|
|
|
|
|
|
|
// correctly drop
|
|
|
|
|
|
|
|
struct TestV<'a>(&'a mut bool); |
|
|
|
|
|
|
|
impl<'a> Drop for TestV<'a> { |
|
|
|
|
|
|
|
fn drop(&mut self) { |
|
|
|
|
|
|
|
if !*self.0 { panic!("value double drop!"); } // no double drop
|
|
|
|
|
|
|
|
*self.0 = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn makepanic<'a>() -> TestV<'a> { panic!() } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut can_drop = true; |
|
|
|
|
|
|
|
let mut hm = HashMap::new(); |
|
|
|
|
|
|
|
hm.insert(0, TestV(&mut can_drop)); |
|
|
|
|
|
|
|
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { hm.entry(0) <- makepanic(); })); |
|
|
|
|
|
|
|
assert_eq!(hm.len(), 0); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|