pub struct HashIndex<K, V, H = RandomState>where
H: BuildHasher,{ /* private fields */ }Expand description
Scalable concurrent hash index.
HashIndex is a concurrent hash map data structure optimized for parallel read operations.
The key characteristics of HashIndex are similar to that of HashMap
except its read operations are lock-free.
§The key differences between HashIndex and HashMap.
- Lock-free read: read and scan operations are never blocked and do not modify shared data.
- Immutability: the data in the container is immutable until it becomes unreachable.
- Linearizability: linearizability of read operations relies on the CPU architecture.
§The key statistics for HashIndex
- The expected size of metadata for a single key-value pair: 2 bytes.
- The expected number of atomic write operations required for an operation on a single key: 2.
- The expected number of atomic variables accessed during a single key operation: 2.
- The number of entries managed by a single bucket without a linked list: 32.
- The expected maximum linked list length when resize is triggered: log(capacity) / 8.
§Unwind safety
HashIndex is impervious to out-of-memory errors and panics in user-specified code under one
condition: H::Hasher::hash, K::drop and V::drop must not panic.
Implementations§
Source§impl<K, V, H> HashIndex<K, V, H>where
H: BuildHasher,
impl<K, V, H> HashIndex<K, V, H>where
H: BuildHasher,
Sourcepub const fn with_hasher(build_hasher: H) -> Self
pub const fn with_hasher(build_hasher: H) -> Self
Creates an empty HashIndex with the given BuildHasher.
§Examples
use scc::HashIndex;
use std::collections::hash_map::RandomState;
let hashindex: HashIndex<u64, u32, RandomState> =
HashIndex::with_hasher(RandomState::new());Sourcepub fn with_capacity_and_hasher(capacity: usize, build_hasher: H) -> Self
pub fn with_capacity_and_hasher(capacity: usize, build_hasher: H) -> Self
Creates an empty HashIndex with the specified capacity and BuildHasher.
The actual capacity is equal to or greater than the specified capacity.
§Examples
use scc::HashIndex;
use std::collections::hash_map::RandomState;
let hashindex: HashIndex<u64, u32, RandomState> =
HashIndex::with_capacity_and_hasher(1000, RandomState::new());
let result = hashindex.capacity();
assert_eq!(result, 1024);Source§impl<K, V, H> HashIndex<K, V, H>
impl<K, V, H> HashIndex<K, V, H>
Sourcepub fn reserve(
&self,
additional_capacity: usize,
) -> Option<Reserve<'_, K, V, H>>
pub fn reserve( &self, additional_capacity: usize, ) -> Option<Reserve<'_, K, V, H>>
Temporarily increases the minimum capacity of the HashIndex.
A Reserve is returned if the HashIndex could increase the minimum capacity while
the increased capacity is not exclusively owned by the returned Reserve, allowing
others to benefit from it. The memory for the additional space may not be immediately
allocated if the HashIndex is empty or currently being resized, however once the memory
is reserved eventually, the capacity will not shrink below the additional capacity until
the returned Reserve is dropped.
§Errors
Returns None if a too large number is given.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<usize, usize> = HashIndex::with_capacity(1000);
assert_eq!(hashindex.capacity(), 1024);
let reserved = hashindex.reserve(10000);
assert!(reserved.is_some());
assert_eq!(hashindex.capacity(), 16384);
assert!(hashindex.reserve(usize::MAX).is_none());
assert_eq!(hashindex.capacity(), 16384);
for i in 0..16 {
assert!(hashindex.insert_sync(i, i).is_ok());
}
drop(reserved);
assert_eq!(hashindex.capacity(), 1024);Sourcepub async fn entry_async(&self, key: K) -> Entry<'_, K, V, H>
pub async fn entry_async(&self, key: K) -> Entry<'_, K, V, H>
Gets the entry associated with the given key in the map for in-place manipulation.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<char, u32> = HashIndex::default();
let future_entry = hashindex.entry_async('b');Sourcepub fn entry_sync(&self, key: K) -> Entry<'_, K, V, H>
pub fn entry_sync(&self, key: K) -> Entry<'_, K, V, H>
Gets the entry associated with the given key in the map for in-place manipulation.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<char, u32> = HashIndex::default();
for ch in "a short treatise on fungi".chars() {
unsafe {
hashindex.entry_sync(ch).and_modify(|counter| *counter += 1).or_insert(1);
}
}
assert_eq!(hashindex.peek_with(&'s', |_, v| *v), Some(2));
assert_eq!(hashindex.peek_with(&'t', |_, v| *v), Some(3));
assert!(hashindex.peek_with(&'y', |_, v| *v).is_none());Sourcepub fn try_entry(&self, key: K) -> Option<Entry<'_, K, V, H>>
pub fn try_entry(&self, key: K) -> Option<Entry<'_, K, V, H>>
Tries to get the entry associated with the given key in the map for in-place manipulation.
Returns None if the entry could not be locked.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<usize, usize> = HashIndex::default();
assert!(hashindex.insert_sync(0, 1).is_ok());
assert!(hashindex.try_entry(0).is_some());Sourcepub async fn begin_async(&self) -> Option<OccupiedEntry<'_, K, V, H>>
pub async fn begin_async(&self) -> Option<OccupiedEntry<'_, K, V, H>>
Begins iterating over entries by getting the first occupied entry.
The returned OccupiedEntry in combination with OccupiedEntry::next_async can act as
a mutable iterator over entries.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<char, u32> = HashIndex::default();
let future_entry = hashindex.begin_async();Sourcepub fn begin_sync(&self) -> Option<OccupiedEntry<'_, K, V, H>>
pub fn begin_sync(&self) -> Option<OccupiedEntry<'_, K, V, H>>
Begins iterating over entries by getting the first occupied entry.
The returned OccupiedEntry in combination with OccupiedEntry::next_sync can act as a
mutable iterator over entries.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
let mut first_entry = hashindex.begin_sync().unwrap();
unsafe {
*first_entry.get_mut() = 2;
}
assert!(first_entry.next_sync().is_none());
assert_eq!(hashindex.peek_with(&1, |_, v| *v), Some(2));Sourcepub async fn any_async<P: FnMut(&K, &V) -> bool>(
&self,
pred: P,
) -> Option<OccupiedEntry<'_, K, V, H>>
pub async fn any_async<P: FnMut(&K, &V) -> bool>( &self, pred: P, ) -> Option<OccupiedEntry<'_, K, V, H>>
Finds any entry satisfying the supplied predicate for in-place manipulation.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_entry = hashindex.any_async(|k, _| *k == 2);Sourcepub fn any_sync<P: FnMut(&K, &V) -> bool>(
&self,
pred: P,
) -> Option<OccupiedEntry<'_, K, V, H>>
pub fn any_sync<P: FnMut(&K, &V) -> bool>( &self, pred: P, ) -> Option<OccupiedEntry<'_, K, V, H>>
Finds any entry satisfying the supplied predicate for in-place manipulation.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
assert!(hashindex.insert_sync(2, 3).is_ok());
let mut entry = hashindex.any_sync(|k, _| *k == 2).unwrap();
assert_eq!(*entry.get(), 3);Sourcepub async fn insert_async(&self, key: K, val: V) -> Result<(), (K, V)>
pub async fn insert_async(&self, key: K, val: V) -> Result<(), (K, V)>
Inserts a key-value pair into the HashIndex.
§Note
If the key exists, the value is not updated.
§Errors
Returns an error containing the supplied key-value pair if the key exists.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_insert = hashindex.insert_async(11, 17);Sourcepub fn insert_sync(&self, key: K, val: V) -> Result<(), (K, V)>
pub fn insert_sync(&self, key: K, val: V) -> Result<(), (K, V)>
Inserts a key-value pair into the HashIndex.
§Note
If the key exists, the value is not updated.
§Errors
Returns an error along with the supplied key-value pair if the key exists.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
assert_eq!(hashindex.insert_sync(1, 1).unwrap_err(), (1, 1));Sourcepub async fn remove_async<Q>(&self, key: &Q) -> bool
pub async fn remove_async<Q>(&self, key: &Q) -> bool
Removes a key-value pair if the key exists.
Returns false if the key does not exist.
Returns true if the key existed and the condition was met after marking the entry
unreachable; the memory will be reclaimed later.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_insert = hashindex.insert_async(11, 17);
let future_remove = hashindex.remove_async(&11);Sourcepub fn remove_sync<Q>(&self, key: &Q) -> bool
pub fn remove_sync<Q>(&self, key: &Q) -> bool
Removes a key-value pair if the key exists.
Returns false if the key does not exist.
Returns true if the key existed and the condition was met after marking the entry
unreachable; the memory will be reclaimed later.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(!hashindex.remove_sync(&1));
assert!(hashindex.insert_sync(1, 0).is_ok());
assert!(hashindex.remove_sync(&1));Sourcepub async fn remove_if_async<Q, F: FnOnce(&V) -> bool>(
&self,
key: &Q,
condition: F,
) -> bool
pub async fn remove_if_async<Q, F: FnOnce(&V) -> bool>( &self, key: &Q, condition: F, ) -> bool
Removes a key-value pair if the key exists and the given condition is met.
Returns false if the key does not exist or the condition was not met.
Returns true if the key existed and the condition was met after marking the entry
unreachable; the memory will be reclaimed later.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_insert = hashindex.insert_async(11, 17);
let future_remove = hashindex.remove_if_async(&11, |_| true);Sourcepub fn remove_if_sync<Q, F: FnOnce(&V) -> bool>(
&self,
key: &Q,
condition: F,
) -> bool
pub fn remove_if_sync<Q, F: FnOnce(&V) -> bool>( &self, key: &Q, condition: F, ) -> bool
Removes a key-value pair if the key exists and the given condition is met.
Returns false if the key does not exist or the condition was not met.
Returns true if the key existed and the condition was met after marking the entry
unreachable; the memory will be reclaimed later.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
assert!(!hashindex.remove_if_sync(&1, |v| *v == 1));
assert!(hashindex.remove_if_sync(&1, |v| *v == 0));Sourcepub async fn get_async<Q>(&self, key: &Q) -> Option<OccupiedEntry<'_, K, V, H>>
pub async fn get_async<Q>(&self, key: &Q) -> Option<OccupiedEntry<'_, K, V, H>>
Gets an OccupiedEntry corresponding to the key for in-place modification.
OccupiedEntry exclusively owns the entry, preventing others from gaining access to it:
use peek if read-only access is sufficient.
Returns None if the key does not exist.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_insert = hashindex.insert_async(11, 17);
let future_get = hashindex.get_async(&11);Sourcepub fn get_sync<Q>(&self, key: &Q) -> Option<OccupiedEntry<'_, K, V, H>>
pub fn get_sync<Q>(&self, key: &Q) -> Option<OccupiedEntry<'_, K, V, H>>
Gets an OccupiedEntry corresponding to the key for in-place modification.
OccupiedEntry exclusively owns the entry, preventing others from gaining access to it:
use peek if read-only access is sufficient.
Returns None if the key does not exist.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.get_sync(&1).is_none());
assert!(hashindex.insert_sync(1, 10).is_ok());
assert_eq!(*hashindex.get_sync(&1).unwrap().get(), 10);
assert_eq!(*hashindex.get_sync(&1).unwrap(), 10);Sourcepub fn peek<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<&'g V>
pub fn peek<'g, Q>(&self, key: &Q, guard: &'g Guard) -> Option<&'g V>
Returns a guarded reference to the value for the specified key without acquiring locks.
Returns None if the key does not exist. The returned reference can survive as long as the
associated Guard is alive.
§Note
The closure may see an old snapshot of the value if the entry has recently been relocated
due to resizing. This means that the effects of interior mutability, e.g., Mutex<T> or
UnsafeCell<T>, may not be observable in the closure.
§Safety
This method is safe to use if the value is not modified or if V is a pointer type such as
Box<T> or Arc<T>. However, it is unsafe if V contains a pointer that can be modified
through interior mutability.
§Examples
use scc::{Guard, HashIndex};
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 10).is_ok());
let guard = Guard::new();
let value_ref = hashindex.peek(&1, &guard).unwrap();
assert_eq!(*value_ref, 10);Sourcepub fn peek_with<Q, R, F: FnOnce(&K, &V) -> R>(
&self,
key: &Q,
reader: F,
) -> Option<R>
pub fn peek_with<Q, R, F: FnOnce(&K, &V) -> R>( &self, key: &Q, reader: F, ) -> Option<R>
Peeks a key-value pair without acquiring locks.
Returns None if the key does not exist.
§Note
The closure may see an old snapshot of the value if the entry has recently been relocated
due to resizing. This means that the effects of interior mutability, e.g., Mutex<T> or
UnsafeCell<T>, may not be observable in the closure.
§Safety
This method is safe to use if the value is not modified or if V is a pointer type such as
Box<T> or Arc<T>. However, it is unsafe if V contains a pointer that can be modified
through interior mutability.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.peek_with(&1, |_, v| *v).is_none());
assert!(hashindex.insert_sync(1, 10).is_ok());
assert_eq!(hashindex.peek_with(&1, |_, v| *v).unwrap(), 10);Sourcepub async fn iter_async<F: FnMut(&K, &V) -> bool>(&self, f: F) -> bool
pub async fn iter_async<F: FnMut(&K, &V) -> bool>(&self, f: F) -> bool
Iterates over entries asynchronously for reading.
Stops iterating when the closure returns false, and this method also returns false.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u64> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
async {
let result = hashindex.iter_async(|k, v| {
false
}).await;
assert!(!result);
};Sourcepub fn iter_sync<F: FnMut(&K, &V) -> bool>(&self, f: F) -> bool
pub fn iter_sync<F: FnMut(&K, &V) -> bool>(&self, f: F) -> bool
Iterates over entries synchronously for reading.
Stops iterating when the closure returns false, and this method also returns false.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u64> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
assert!(hashindex.insert_sync(2, 1).is_ok());
let mut acc = 0_u64;
let result = hashindex.iter_sync(|k, v| {
acc += *k;
acc += *v;
true
});
assert!(result);
assert_eq!(acc, 4);Sourcepub async fn retain_async<F: FnMut(&K, &V) -> bool>(&self, pred: F)
pub async fn retain_async<F: FnMut(&K, &V) -> bool>(&self, pred: F)
Retains the entries specified by the predicate.
Entries that have existed since the invocation of the method are guaranteed to be visited
if they are not removed, however the same entry can be visited more than once if the
HashIndex gets resized by another thread.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_insert = hashindex.insert_async(1, 0);
let future_retain = hashindex.retain_async(|k, v| *k == 1);Sourcepub fn retain_sync<F: FnMut(&K, &V) -> bool>(&self, pred: F)
pub fn retain_sync<F: FnMut(&K, &V) -> bool>(&self, pred: F)
Retains the entries specified by the predicate.
Entries that have existed since the invocation of the method are guaranteed to be visited
if they are not removed, however the same entry can be visited more than once if the
HashIndex gets resized by another thread.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
assert!(hashindex.insert_sync(2, 1).is_ok());
assert!(hashindex.insert_sync(3, 2).is_ok());
hashindex.retain_sync(|k, v| *k == 1 && *v == 0);
assert!(hashindex.contains(&1));
assert!(!hashindex.contains(&2));
assert!(!hashindex.contains(&3));Sourcepub async fn clear_async(&self)
pub async fn clear_async(&self)
Sourcepub fn clear_sync(&self)
pub fn clear_sync(&self)
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of entries in the HashIndex.
It reads the entire metadata area of the bucket array to calculate the number of valid
entries, making its time complexity O(N). Furthermore, it may overcount entries if an old
bucket array has yet to be dropped.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
assert_eq!(hashindex.len(), 1);Sourcepub fn capacity(&self) -> usize
pub fn capacity(&self) -> usize
Returns the capacity of the HashIndex.
§Examples
use scc::HashIndex;
let hashindex_default: HashIndex<u64, u32> = HashIndex::default();
assert_eq!(hashindex_default.capacity(), 0);
assert!(hashindex_default.insert_sync(1, 0).is_ok());
assert_eq!(hashindex_default.capacity(), 64);
let hashindex: HashIndex<u64, u32> = HashIndex::with_capacity(1000);
assert_eq!(hashindex.capacity(), 1024);Sourcepub fn capacity_range(&self) -> RangeInclusive<usize>
pub fn capacity_range(&self) -> RangeInclusive<usize>
Returns the current capacity range of the HashIndex.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert_eq!(hashindex.capacity_range(), 0..=(1_usize << (usize::BITS - 1)));
let reserved = hashindex.reserve(1000);
assert_eq!(hashindex.capacity_range(), 1000..=(1_usize << (usize::BITS - 1)));Sourcepub fn bucket_index<Q>(&self, key: &Q) -> usize
pub fn bucket_index<Q>(&self, key: &Q) -> usize
Returns the index of the bucket that may contain the key.
The method returns the index of the bucket associated with the key. The number of buckets
can be calculated by dividing the capacity by 32.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::with_capacity(1024);
let bucket_index = hashindex.bucket_index(&11);
assert!(bucket_index < hashindex.capacity() / 32);Sourcepub fn iter<'h, 'g>(&'h self, guard: &'g Guard) -> Iter<'h, 'g, K, V, H> ⓘ
pub fn iter<'h, 'g>(&'h self, guard: &'g Guard) -> Iter<'h, 'g, K, V, H> ⓘ
Returns an Iter.
It is guaranteed to go through all the key-value pairs pertaining in the HashIndex
at the moment, however the same key-value pair can be visited more than once if the
HashIndex is being resized.
It requires the user to supply a reference to a Guard.
§Examples
use scc::{Guard, HashIndex};
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert_sync(1, 0).is_ok());
let guard = Guard::new();
let mut iter = hashindex.iter(&guard);
let entry_ref = iter.next().unwrap();
assert_eq!(iter.next(), None);
for iter in hashindex.iter(&guard) {
assert_eq!(iter, (&1, &0));
}
drop(hashindex);
assert_eq!(entry_ref, (&1, &0));