//! This is the core implementation that doesn't depend on the hasher at all. //! //! The methods of `IndexMapCore` don't use any Hash properties of K. //! //! It's cleaner to separate them out, then the compiler checks that we are not //! using Hash at all in these methods. //! //! However, we should probably not let this show in the public API or docs.
mod entry; mod raw;
pubmod raw_entry_v1;
use hashbrown::raw::RawTable;
usecrate::vec::{self, Vec}; usecrate::TryReserveError; use core::mem; use core::ops::RangeBounds;
/// Core of the map that does not depend on S pub(crate) struct IndexMapCore<K, V> { /// indices mapping from the entry hash to its index.
indices: RawTable<usize>, /// entries is a dense vec of entries in their order.
entries: Vec<Bucket<K, V>>,
}
#[inline] fn update_index(table: &mut RawTable<usize>, hash: HashValue, old: usize, new: usize) { let index = table
.get_mut(hash.get(), move |&i| i == old)
.expect("index not found");
*index = new;
}
impl<K, V> Clone for IndexMapCore<K, V> where
K: Clone,
V: Clone,
{ fn clone(&self) -> Self { letmut new = Self::new();
new.clone_from(self);
new
}
fn clone_from(&mutself, other: &Self) { let hasher = get_hash(&other.entries); self.indices.clone_from_with_hasher(&other.indices, hasher); ifself.entries.capacity() < other.entries.len() { // If we must resize, match the indices capacity. let additional = other.entries.len() - self.entries.len(); self.reserve_entries(additional);
} self.entries.clone_from(&other.entries);
}
}
impl<K, V> IndexMapCore<K, V> { /// The maximum capacity before the `entries` allocation would exceed `isize::MAX`. const MAX_ENTRIES_CAPACITY: usize = (isize::MAX as usize) / mem::size_of::<Bucket<K, V>>();
/// Append from another map without checking whether items already exist. pub(crate) fn append_unchecked(&mutself, other: &mutSelf) { self.reserve(other.len());
raw::insert_bulk_no_grow(&mutself.indices, &other.entries); self.entries.append(&mut other.entries);
other.indices.clear();
}
/// Reserve capacity for `additional` more key-value pairs. pub(crate) fn reserve(&mutself, additional: usize) { self.indices.reserve(additional, get_hash(&self.entries)); // Only grow entries if necessary, since we also round up capacity. if additional > self.entries.capacity() - self.entries.len() { self.reserve_entries(additional);
}
}
/// Reserve entries capacity, rounded up to match the indices fn reserve_entries(&mutself, additional: usize) { // Use a soft-limit on the maximum capacity, but if the caller explicitly // requested more, do it and let them have the resulting panic. let new_capacity = Ord::min(self.indices.capacity(), Self::MAX_ENTRIES_CAPACITY); let try_add = new_capacity - self.entries.len(); if try_add > additional && self.entries.try_reserve_exact(try_add).is_ok() { return;
} self.entries.reserve_exact(additional);
}
/// Reserve capacity for `additional` more key-value pairs, without over-allocating. pub(crate) fn reserve_exact(&mutself, additional: usize) { self.indices.reserve(additional, get_hash(&self.entries)); self.entries.reserve_exact(additional);
}
/// Try to reserve capacity for `additional` more key-value pairs. pub(crate) fn try_reserve(&mutself, additional: usize) -> Result<(), TryReserveError> { self.indices
.try_reserve(additional, get_hash(&self.entries))
.map_err(TryReserveError::from_hashbrown)?; // Only grow entries if necessary, since we also round up capacity. if additional > self.entries.capacity() - self.entries.len() { self.try_reserve_entries(additional)
} else {
Ok(())
}
}
/// Try to reserve entries capacity, rounded up to match the indices fn try_reserve_entries(&mutself, additional: usize) -> Result<(), TryReserveError> { // Use a soft-limit on the maximum capacity, but if the caller explicitly // requested more, do it and let them have the resulting error. let new_capacity = Ord::min(self.indices.capacity(), Self::MAX_ENTRIES_CAPACITY); let try_add = new_capacity - self.entries.len(); if try_add > additional && self.entries.try_reserve_exact(try_add).is_ok() { return Ok(());
} self.entries
.try_reserve_exact(additional)
.map_err(TryReserveError::from_alloc)
}
/// Try to reserve capacity for `additional` more key-value pairs, without over-allocating. pub(crate) fn try_reserve_exact(&mutself, additional: usize) -> Result<(), TryReserveError> { self.indices
.try_reserve(additional, get_hash(&self.entries))
.map_err(TryReserveError::from_hashbrown)?; self.entries
.try_reserve_exact(additional)
.map_err(TryReserveError::from_alloc)
}
/// Shrink the capacity of the map with a lower bound pub(crate) fn shrink_to(&mutself, min_capacity: usize) { self.indices
.shrink_to(min_capacity, get_hash(&self.entries)); self.entries.shrink_to(min_capacity);
}
/// Remove the last key-value pair pub(crate) fn pop(&mutself) -> Option<(K, V)> { iflet Some(entry) = self.entries.pop() { let last = self.entries.len();
erase_index(&mutself.indices, entry.hash, last);
Some((entry.key, entry.value))
} else {
None
}
}
/// Append a key-value pair to `entries`, *without* checking whether it already exists. fn push_entry(&mutself, hash: HashValue, key: K, value: V) { ifself.entries.len() == self.entries.capacity() { // Reserve our own capacity synced to the indices, // rather than letting `Vec::push` just double it. self.reserve_entries(1);
} self.entries.push(Bucket { hash, key, value });
}
/// Insert a key-value pair in `entries` at a particular index, /// *without* checking whether it already exists. fn insert_entry(&mutself, index: usize, hash: HashValue, key: K, value: V) { ifself.entries.len() == self.entries.capacity() { // Reserve our own capacity synced to the indices, // rather than letting `Vec::insert` just double it. self.reserve_entries(1);
} self.entries.insert(index, Bucket { hash, key, value });
}
/// Return the index in `entries` where an equivalent key can be found pub(crate) fn get_index_of<Q>(&self, hash: HashValue, key: &Q) -> Option<usize> where
Q: ?Sized + Equivalent<K>,
{ let eq = equivalent(key, &self.entries); self.indices.get(hash.get(), eq).copied()
}
/// Same as `insert_full`, except it also replaces the key pub(crate) fn replace_full(
&mutself,
hash: HashValue,
key: K,
value: V,
) -> (usize, Option<(K, V)>) where
K: Eq,
{ matchself.find_or_insert(hash, &key) {
Ok(i) => { let entry = &mutself.entries[i]; let kv = (
mem::replace(&mut entry.key, key),
mem::replace(&mut entry.value, value),
);
(i, Some(kv))
}
Err(i) => {
debug_assert_eq!(i, self.entries.len()); self.push_entry(hash, key, value);
(i, None)
}
}
}
fn insert_unique(&mutself, hash: HashValue, key: K, value: V) -> usize { let i = self.indices.len(); self.indices.insert(hash.get(), i, get_hash(&self.entries));
debug_assert_eq!(i, self.entries.len()); self.push_entry(hash, key, value);
i
}
fn shift_insert_unique(&mutself, index: usize, hash: HashValue, key: K, value: V) { let end = self.indices.len();
assert!(index <= end); // Increment others first so we don't have duplicate indices. self.increment_indices(index, end); let entries = &*self.entries; self.indices.insert(hash.get(), index, move |&i| { // Adjust for the incremented indices to find hashes.
debug_assert_ne!(i, index); let i = if i < index { i } else { i - 1 };
entries[i].hash.get()
}); self.insert_entry(index, hash, key, value);
}
/// Remove an entry by shifting all entries that follow it pub(crate) fn shift_remove_full<Q>(&mutself, hash: HashValue, key: &Q) -> Option<(usize, K, V)> where
Q: ?Sized + Equivalent<K>,
{ let eq = equivalent(key, &self.entries); matchself.indices.remove_entry(hash.get(), eq) {
Some(index) => { let (key, value) = self.shift_remove_finish(index);
Some((index, key, value))
}
None => None,
}
}
/// Remove an entry by shifting all entries that follow it pub(crate) fn shift_remove_index(&mutself, index: usize) -> Option<(K, V)> { matchself.entries.get(index) {
Some(entry) => {
erase_index(&mutself.indices, entry.hash, index);
Some(self.shift_remove_finish(index))
}
None => None,
}
}
/// Remove an entry by shifting all entries that follow it /// /// The index should already be removed from `self.indices`. fn shift_remove_finish(&mutself, index: usize) -> (K, V) { // Correct indices that point to the entries that followed the removed entry. self.decrement_indices(index + 1, self.entries.len());
// Use Vec::remove to actually remove the entry. let entry = self.entries.remove(index);
(entry.key, entry.value)
}
/// Decrement all indices in the range `start..end`. /// /// The index `start - 1` should not exist in `self.indices`. /// All entries should still be in their original positions. fn decrement_indices(&mutself, start: usize, end: usize) { // Use a heuristic between a full sweep vs. a `find()` for every shifted item. let shifted_entries = &self.entries[start..end]; if shifted_entries.len() > self.indices.buckets() / 2 { // Shift all indices in range. for i inself.indices_mut() { if start <= *i && *i < end {
*i -= 1;
}
}
} else { // Find each entry in range to shift its index. for (i, entry) in (start..end).zip(shifted_entries) {
update_index(&mutself.indices, entry.hash, i, i - 1);
}
}
}
/// Increment all indices in the range `start..end`. /// /// The index `end` should not exist in `self.indices`. /// All entries should still be in their original positions. fn increment_indices(&mutself, start: usize, end: usize) { // Use a heuristic between a full sweep vs. a `find()` for every shifted item. let shifted_entries = &self.entries[start..end]; if shifted_entries.len() > self.indices.buckets() / 2 { // Shift all indices in range. for i inself.indices_mut() { if start <= *i && *i < end {
*i += 1;
}
}
} else { // Find each entry in range to shift its index, updated in reverse so // we never have duplicated indices that might have a hash collision. for (i, entry) in (start..end).zip(shifted_entries).rev() {
update_index(&mutself.indices, entry.hash, i, i + 1);
}
}
}
pub(super) fn move_index(&mutself, from: usize, to: usize) { let from_hash = self.entries[from].hash; if from != to { // Use a sentinel index so other indices don't collide.
update_index(&mutself.indices, from_hash, from, usize::MAX);
// Update all other indices and rotate the entry positions. if from < to { self.decrement_indices(from + 1, to + 1); self.entries[from..=to].rotate_left(1);
} elseif to < from { self.increment_indices(to, from); self.entries[to..=from].rotate_right(1);
}
// Change the sentinel index to its final position.
update_index(&mutself.indices, from_hash, usize::MAX, to);
}
}
pub(crate) fn swap_indices(&mutself, a: usize, b: usize) { // If they're equal and in-bounds, there's nothing to do. if a == b && a < self.entries.len() { return;
}
// We'll get a "nice" bounds-check from indexing `self.entries`, // and then we expect to find it in the table as well. let [ref_a, ref_b] = self
.indices
.get_many_mut(
[self.entries[a].hash.get(), self.entries[b].hash.get()], move |i, &x| if i == 0 { x == a } else { x == b },
)
.expect("indices not found");
/// Remove an entry by swapping it with the last pub(crate) fn swap_remove_full<Q>(&mutself, hash: HashValue, key: &Q) -> Option<(usize, K, V)> where
Q: ?Sized + Equivalent<K>,
{ let eq = equivalent(key, &self.entries); matchself.indices.remove_entry(hash.get(), eq) {
Some(index) => { let (key, value) = self.swap_remove_finish(index);
Some((index, key, value))
}
None => None,
}
}
/// Remove an entry by swapping it with the last pub(crate) fn swap_remove_index(&mutself, index: usize) -> Option<(K, V)> { matchself.entries.get(index) {
Some(entry) => {
erase_index(&mutself.indices, entry.hash, index);
Some(self.swap_remove_finish(index))
}
None => None,
}
}
/// Finish removing an entry by swapping it with the last /// /// The index should already be removed from `self.indices`. fn swap_remove_finish(&mutself, index: usize) -> (K, V) { // use swap_remove, but then we need to update the index that points // to the other entry that has to move let entry = self.entries.swap_remove(index);
// correct index that points to the entry that had to swap places iflet Some(entry) = self.entries.get(index) { // was not last element // examine new element in `index` and find it in indices let last = self.entries.len();
update_index(&mutself.indices, entry.hash, last, index);
}
(entry.key, entry.value)
}
/// Erase `start..end` from `indices`, and shift `end..` indices down to `start..` /// /// All of these items should still be at their original location in `entries`. /// This is used by `drain`, which will let `Vec::drain` do the work on `entries`. fn erase_indices(&mutself, start: usize, end: usize) { let (init, shifted_entries) = self.entries.split_at(end); let (start_entries, erased_entries) = init.split_at(start);
let erased = erased_entries.len(); let shifted = shifted_entries.len(); let half_capacity = self.indices.buckets() / 2;
// Use a heuristic between different strategies if erased == 0 { // Degenerate case, nothing to do
} elseif start + shifted < half_capacity && start < erased { // Reinsert everything, as there are few kept indices self.indices.clear();
// Reinsert stable indices, then shifted indices
raw::insert_bulk_no_grow(&mutself.indices, start_entries);
raw::insert_bulk_no_grow(&mutself.indices, shifted_entries);
} elseif erased + shifted < half_capacity { // Find each affected index, as there are few to adjust
// Find erased indices for (i, entry) in (start..).zip(erased_entries) {
erase_index(&mutself.indices, entry.hash, i);
}
// Find shifted indices for ((new, old), entry) in (start..).zip(end..).zip(shifted_entries) {
update_index(&mutself.indices, entry.hash, old, new);
}
} else { // Sweep the whole table for adjustments self.erase_indices_sweep(start, end);
}
// No need to save hash indices, can easily calculate what they should // be, given that this is an in-place reversal. let len = self.entries.len(); for i inself.indices_mut() {
*i = len - *i - 1;
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.