pub struct HashIndex<K, V, H = RandomState>where
H: BuildHasher,{ /* private fields */ }
Expand description
Scalable concurrent hash index.
HashIndex
is a concurrent and asynchronous hash map data structure that is optimized for
parallel read operations. The key characteristics of HashIndex
are similar to that of
HashMap
except that its read operations are lock-free.
§The key differences between HashIndex
and HashMap
.
- Lock-free-read: read and scan operations do not modify shared data and are never blocked.
- Immutability: the data in the container is immutable until it becomes unreachable.
- Linearizability:
HashIndex
read operations are not linearizable.
§The key statistics for HashIndex
- The expected size of metadata for a single key-value pair: 2-byte.
- 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 on 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(i, i).is_ok());
}
drop(reserved);
assert_eq!(hashindex.capacity(), 1024);
Sourcepub fn entry(&self, key: K) -> Entry<'_, K, V, H>
pub fn entry(&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(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 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.
It is an asynchronous method returning an impl Future
for the caller to await.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<char, u32> = HashIndex::default();
let future_entry = hashindex.entry_async('b');
Sourcepub fn first_entry(&self) -> Option<OccupiedEntry<'_, K, V, H>>
pub fn first_entry(&self) -> Option<OccupiedEntry<'_, K, V, H>>
Gets the first occupied entry for in-place manipulation.
The returned OccupiedEntry
in combination with OccupiedEntry::next
or
OccupiedEntry::next_async
can act as a mutable iterator over entries.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert(1, 0).is_ok());
let mut first_entry = hashindex.first_entry().unwrap();
unsafe {
*first_entry.get_mut() = 2;
}
assert!(first_entry.next().is_none());
assert_eq!(hashindex.peek_with(&1, |_, v| *v), Some(2));
Sourcepub async fn first_entry_async(&self) -> Option<OccupiedEntry<'_, K, V, H>>
pub async fn first_entry_async(&self) -> Option<OccupiedEntry<'_, K, V, H>>
Gets the first occupied entry for in-place manipulation.
The returned OccupiedEntry
in combination with OccupiedEntry::next
or
OccupiedEntry::next_async
can act as a mutable iterator over entries.
It is an asynchronous method returning an impl Future
for the caller to await.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<char, u32> = HashIndex::default();
let future_entry = hashindex.first_entry_async();
Sourcepub fn any_entry<P: FnMut(&K, &V) -> bool>(
&self,
pred: P,
) -> Option<OccupiedEntry<'_, K, V, H>>
pub fn any_entry<P: FnMut(&K, &V) -> bool>( &self, pred: P, ) -> Option<OccupiedEntry<'_, K, V, H>>
Finds any entry satisfying the supplied predicate for in-place manipulation.
The returned OccupiedEntry
in combination with OccupiedEntry::next
or
OccupiedEntry::next_async
can act as a mutable iterator over entries.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert(1, 0).is_ok());
assert!(hashindex.insert(2, 3).is_ok());
let mut entry = hashindex.any_entry(|k, _| *k == 2).unwrap();
assert_eq!(*entry.get(), 3);
Sourcepub async fn any_entry_async<P: FnMut(&K, &V) -> bool>(
&self,
pred: P,
) -> Option<OccupiedEntry<'_, K, V, H>>
pub async fn any_entry_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.
The returned OccupiedEntry
in combination with OccupiedEntry::next
or
OccupiedEntry::next_async
can act as a mutable iterator over entries.
It is an asynchronous method returning an impl Future
for the caller to await.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_entry = hashindex.any_entry_async(|k, _| *k == 2);
Sourcepub fn insert(&self, key: K, val: V) -> Result<(), (K, V)>
pub fn insert(&self, key: K, val: V) -> Result<(), (K, V)>
Inserts a key-value pair into the HashIndex
.
§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(1, 0).is_ok());
assert_eq!(hashindex.insert(1, 1).unwrap_err(), (1, 1));
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
.
It is an asynchronous method returning an impl Future
for the caller to await.
§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();
let future_insert = hashindex.insert_async(11, 17);
Sourcepub fn remove<Q>(&self, key: &Q) -> bool
pub fn remove<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(&1));
assert!(hashindex.insert(1, 0).is_ok());
assert!(hashindex.remove(&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. It is an asynchronous method returning an
impl Future
for the caller to await.
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_if<Q, F: FnOnce(&V) -> bool>(&self, key: &Q, condition: F) -> bool
pub fn remove_if<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(1, 0).is_ok());
assert!(!hashindex.remove_if(&1, |v| *v == 1));
assert!(hashindex.remove_if(&1, |v| *v == 0));
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. It is an
asynchronous method returning an impl Future
for the caller to await.
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 get<Q>(&self, key: &Q) -> Option<OccupiedEntry<'_, K, V, H>>
pub fn get<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(&1).is_none());
assert!(hashindex.insert(1, 10).is_ok());
assert_eq!(*hashindex.get(&1).unwrap().get(), 10);
assert_eq!(*hashindex.get(&1).unwrap(), 10);
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. It is an asynchronous method returning an
impl Future
for the caller to await.
§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 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.
This method is not linearizable since the entry can be removed while being read.
§Examples
use scc::ebr::Guard;
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert(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.
This method is not linearizable since the entry can be removed while being read.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.peek_with(&1, |_, v| *v).is_none());
assert!(hashindex.insert(1, 10).is_ok());
assert_eq!(hashindex.peek_with(&1, |_, v| *v).unwrap(), 10);
Sourcepub fn retain<F: FnMut(&K, &V) -> bool>(&self, pred: F)
pub fn retain<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(1, 0).is_ok());
assert!(hashindex.insert(2, 1).is_ok());
assert!(hashindex.insert(3, 2).is_ok());
hashindex.retain(|k, v| *k == 1 && *v == 0);
assert!(hashindex.contains(&1));
assert!(!hashindex.contains(&2));
assert!(!hashindex.contains(&3));
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.
It is an asynchronous method returning an impl Future
for the caller to await.
§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 async fn clear_async(&self)
pub async fn clear_async(&self)
Clears the HashIndex
by removing all key-value pairs.
It is an asynchronous method returning an impl Future
for the caller to await.
§Examples
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
let future_insert = hashindex.insert_async(1, 0);
let future_retain = hashindex.clear_async();
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(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(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 32
into the capacity.
§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::ebr::Guard;
use scc::HashIndex;
let hashindex: HashIndex<u64, u32> = HashIndex::default();
assert!(hashindex.insert(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));