1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
// Copyright (c) 2022 MASSA LABS <info@massa.net>
use std::collections::{HashMap, HashSet};
use std::hash::{BuildHasherDefault, Hasher};
use std::marker::PhantomData;
impl PreHashed for massa_hash::Hash {}
/// A trait indicating that its carrier is already a hash with at least 64 bits
/// and doesn't need to be re-hashed for hash-table purposes
pub trait PreHashed {}
/// A `Hasher` for `PreHashed` keys that is faster because it avoids re-hashing hashes but simply truncates them.
/// Note: when truncating, it takes the last 8 bytes of the key instead of the first 8 bytes.
/// This is done to circumvent biases induced by first-byte manipulations in addresses related to the thread assignment process
pub struct HashMapper<T: PreHashed> {
source: PhantomData<T>,
hash: u64,
}
/// Default implementation for `HashMapper` (zero hash)
impl<T: PreHashed> Default for HashMapper<T> {
fn default() -> Self {
HashMapper {
source: Default::default(),
hash: Default::default(),
}
}
}
/// `Hasher` implementation for `HashMapper`
impl<T: PreHashed> Hasher for HashMapper<T> {
/// finish the hashing process and return the truncated `u64` hash
#[inline]
fn finish(&self) -> u64 {
self.hash
}
/// write the bytes of a `PreHashed` key into the `HashMapper`
/// Panics if `bytes.len()` is strictly lower than 8
/// Note: the truncated `u64` is completely overwritten by the last 8 items of "bytes" at every call
#[inline]
fn write(&mut self, bytes: &[u8]) {
// assumes bytes.len() is at least 8, otherwise panics
self.hash = u64::from_ne_bytes(
bytes[bytes.len().checked_sub(8).unwrap()..]
.try_into()
.unwrap(),
);
}
}
/// `BuildHasherDefault` specialization for `HashMapper`
pub type BuildHashMapper<T> = BuildHasherDefault<HashMapper<T>>;
/// `HashMap` specialization for `PreHashed` keys
/// This hashmap is about 2x faster than the default `HashMap`
pub type PreHashMap<K, V> = HashMap<K, V, BuildHashMapper<K>>;
/// `HashSet` specialization for `PreHashed` keys
/// This hashset is about 2x faster than the default `HashSet`
pub type PreHashSet<T> = HashSet<T, BuildHashMapper<T>>;
/// Trait allowing pre-allocations
pub trait CapacityAllocator {
/// pre-allocate with a given capacity
fn with_capacity(capacity: usize) -> Self;
}
impl<K: PreHashed, V> CapacityAllocator for PreHashMap<K, V> {
/// pre-allocate with a given capacity
fn with_capacity(capacity: usize) -> Self {
PreHashMap::with_capacity_and_hasher(capacity, BuildHashMapper::default())
}
}
impl<K: PreHashed> CapacityAllocator for PreHashSet<K> {
/// pre-allocate with a given capacity
fn with_capacity(capacity: usize) -> Self {
PreHashSet::with_capacity_and_hasher(capacity, BuildHashMapper::default())
}
}