mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Add fallible HashMap APIs
This commit is contained in:
parent
9706f9a0f0
commit
acc70380b3
2 changed files with 83 additions and 50 deletions
|
@ -25,6 +25,8 @@ use super::table::BucketState::{Empty, Full};
|
||||||
|
|
||||||
const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two
|
const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two
|
||||||
|
|
||||||
|
static OOM_STR: &str = "out of memory whilst allocating hashmap";
|
||||||
|
|
||||||
/// The default behavior of HashMap implements a maximum load factor of 90.9%.
|
/// The default behavior of HashMap implements a maximum load factor of 90.9%.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DefaultResizePolicy;
|
struct DefaultResizePolicy;
|
||||||
|
@ -610,6 +612,11 @@ impl<K: Hash + Eq, V> HashMap<K, V, RandomState> {
|
||||||
pub fn with_capacity(capacity: usize) -> HashMap<K, V, RandomState> {
|
pub fn with_capacity(capacity: usize) -> HashMap<K, V, RandomState> {
|
||||||
HashMap::with_capacity_and_hasher(capacity, Default::default())
|
HashMap::with_capacity_and_hasher(capacity, Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_capacity_fallible(capacity: usize) -> Result<HashMap<K, V, RandomState>, ()> {
|
||||||
|
HashMap::with_capacity_and_hasher_fallible(capacity, Default::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V, S> HashMap<K, V, S>
|
impl<K, V, S> HashMap<K, V, S>
|
||||||
|
@ -637,12 +644,17 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
/// map.insert(1, 2);
|
/// map.insert(1, 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
|
pub fn with_hasher_fallible(hash_builder: S) -> Result<HashMap<K, V, S>, ()> {
|
||||||
HashMap {
|
Ok(HashMap {
|
||||||
hash_builder,
|
hash_builder,
|
||||||
resize_policy: DefaultResizePolicy::new(),
|
resize_policy: DefaultResizePolicy::new(),
|
||||||
table: RawTable::new(0),
|
table: RawTable::new(0)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_hasher(hash_builder: S) -> HashMap<K, V, S> {
|
||||||
|
Self::with_hasher_fallible(hash_builder).expect(OOM_STR)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty `HashMap` with the specified capacity, using `hash_builder`
|
/// Creates an empty `HashMap` with the specified capacity, using `hash_builder`
|
||||||
|
@ -667,14 +679,18 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
/// map.insert(1, 2);
|
/// map.insert(1, 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap<K, V, S> {
|
pub fn with_capacity_and_hasher_fallible(capacity: usize, hash_builder: S) -> Result<HashMap<K, V, S>, ()> {
|
||||||
let resize_policy = DefaultResizePolicy::new();
|
let resize_policy = DefaultResizePolicy::new();
|
||||||
let raw_cap = resize_policy.raw_capacity(capacity);
|
let raw_cap = resize_policy.raw_capacity(capacity);
|
||||||
HashMap {
|
Ok(HashMap {
|
||||||
hash_builder,
|
hash_builder,
|
||||||
resize_policy,
|
resize_policy,
|
||||||
table: RawTable::new(raw_cap),
|
table: RawTable::new(raw_cap)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap<K, V, S> {
|
||||||
|
Self::with_capacity_and_hasher_fallible(capacity, hash_builder).expect(OOM_STR)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the map's [`BuildHasher`].
|
/// Returns a reference to the map's [`BuildHasher`].
|
||||||
|
@ -725,35 +741,37 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
/// map.reserve(10);
|
/// map.reserve(10);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn reserve(&mut self, additional: usize) {
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
|
self.reserve_fallible(additional).expect(OOM_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reserve_fallible(&mut self, additional: usize) -> Result<(), ()> {
|
||||||
let remaining = self.capacity() - self.len(); // this can't overflow
|
let remaining = self.capacity() - self.len(); // this can't overflow
|
||||||
if remaining < additional {
|
if remaining < additional {
|
||||||
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_fallible(raw_cap)?;
|
||||||
} else if self.table.tag() && remaining <= self.len() {
|
} else if self.table.tag() && remaining <= self.len() {
|
||||||
// Probe sequence is too long and table is half full,
|
// Probe sequence is too long and table is half full,
|
||||||
// resize early to reduce probing length.
|
// resize early to reduce probing length.
|
||||||
let new_capacity = self.table.capacity() * 2;
|
let new_capacity = self.table.capacity() * 2;
|
||||||
self.resize(new_capacity);
|
self.resize_fallible(new_capacity)?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resizes the internal vectors to a new capacity. It's your
|
|
||||||
/// responsibility to:
|
|
||||||
/// 1) Ensure `new_raw_cap` is enough for all the elements, accounting
|
|
||||||
/// for the load factor.
|
|
||||||
/// 2) Ensure `new_raw_cap` is a power of two or zero.
|
|
||||||
#[inline(never)]
|
|
||||||
#[cold]
|
#[cold]
|
||||||
fn resize(&mut self, new_raw_cap: usize) {
|
#[inline(never)]
|
||||||
|
fn resize_fallible(&mut self, new_raw_cap: usize) -> Result<(), ()> {
|
||||||
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);
|
||||||
|
|
||||||
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.size() == 0 {
|
if old_table.size() == 0 {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bucket = Bucket::head_bucket(&mut old_table);
|
let mut bucket = Bucket::head_bucket(&mut old_table);
|
||||||
|
@ -788,6 +806,7 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(self.table.size(), old_size);
|
assert_eq!(self.table.size(), old_size);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shrinks the capacity of the map as much as possible. It will drop
|
/// Shrinks the capacity of the map as much as possible. It will drop
|
||||||
|
@ -807,9 +826,13 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
/// assert!(map.capacity() >= 2);
|
/// assert!(map.capacity() >= 2);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn shrink_to_fit(&mut self) {
|
pub fn shrink_to_fit(&mut self) {
|
||||||
|
self.shrink_to_fit_fallible().expect(OOM_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrink_to_fit_fallible(&mut self) -> Result<(), ()> {
|
||||||
let new_raw_cap = self.resize_policy.raw_capacity(self.len());
|
let new_raw_cap = self.resize_policy.raw_capacity(self.len());
|
||||||
if self.raw_capacity() != new_raw_cap {
|
if self.raw_capacity() != new_raw_cap {
|
||||||
let old_table = replace(&mut self.table, RawTable::new(new_raw_cap));
|
let old_table = replace(&mut self.table, RawTable::new(new_raw_cap)?);
|
||||||
let old_size = old_table.size();
|
let old_size = old_table.size();
|
||||||
|
|
||||||
// Shrink the table. Naive algorithm for resizing:
|
// Shrink the table. Naive algorithm for resizing:
|
||||||
|
@ -819,6 +842,7 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
|
|
||||||
debug_assert_eq!(self.table.size(), old_size);
|
debug_assert_eq!(self.table.size(), old_size);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a pre-hashed key-value pair, without first checking
|
/// Insert a pre-hashed key-value pair, without first checking
|
||||||
|
@ -975,11 +999,15 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
/// assert_eq!(letters.get(&'y'), None);
|
/// assert_eq!(letters.get(&'y'), None);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn entry(&mut self, key: K) -> Entry<K, V> {
|
pub fn entry(&mut self, key: K) -> Entry<K, V> {
|
||||||
|
self.entry_fallible(key).expect(OOM_STR)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entry_fallible(&mut self, key: K) -> Result<Entry<K, V>, ()> {
|
||||||
// Gotta resize now.
|
// Gotta resize now.
|
||||||
self.reserve(1);
|
self.reserve_fallible(1)?;
|
||||||
let hash = self.make_hash(&key);
|
let hash = self.make_hash(&key);
|
||||||
search_hashed(&mut self.table, hash, |q| q.eq(&key))
|
Ok(search_hashed(&mut self.table, hash, |q| q.eq(&key))
|
||||||
.into_entry(key).expect("unreachable")
|
.into_entry(key).expect("unreachable"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of elements in the map.
|
/// Returns the number of elements in the map.
|
||||||
|
@ -1163,9 +1191,14 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
/// assert_eq!(map[&37], "c");
|
/// assert_eq!(map[&37], "c");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||||
|
self.insert_fallible(k, v).expect(OOM_STR)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn insert_fallible(&mut self, k: K, v: V) -> Result<Option<V>, ()> {
|
||||||
let hash = self.make_hash(&k);
|
let hash = self.make_hash(&k);
|
||||||
self.reserve(1);
|
self.reserve_fallible(1)?;
|
||||||
self.insert_hashed_nocheck(hash, k, v)
|
Ok(self.insert_hashed_nocheck(hash, k, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a key from the map, returning the value at the key if the key
|
/// Removes a key from the map, returning the value at the key if the key
|
||||||
|
|
|
@ -802,11 +802,11 @@ impl<K, V> RawTable<K, V> {
|
||||||
|
|
||||||
/// Creates a new raw table from a given capacity. All buckets are
|
/// Creates a new raw table from a given capacity. All buckets are
|
||||||
/// initially empty.
|
/// initially empty.
|
||||||
pub fn new(capacity: usize) -> RawTable<K, V> {
|
pub fn new(capacity: usize) -> Result<RawTable<K, V>, ()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = RawTable::new_uninitialized(capacity);
|
let ret = RawTable::new_uninitialized_fallible(capacity)?;
|
||||||
ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
|
ptr::write_bytes(ret.hashes.ptr(), 0, capacity);
|
||||||
ret
|
Ok(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue