1f980defeSChris Fallin //! A hashmap with "external hashing": nodes are hashed or compared for
2f980defeSChris Fallin //! equality only with some external context provided on lookup/insert.
3f980defeSChris Fallin //! This allows very memory-efficient data structures where
4f980defeSChris Fallin //! node-internal data references some other storage (e.g., offsets into
5f980defeSChris Fallin //! an array or pool of shared data).
6f980defeSChris Fallin
7*0889323aSSSD use core::hash::{Hash, Hasher};
8f2fb00ddSJonas Kruckenberg use hashbrown::hash_table::HashTable;
9f980defeSChris Fallin
10f980defeSChris Fallin /// Trait that allows for equality comparison given some external
11f980defeSChris Fallin /// context.
12f980defeSChris Fallin ///
13f980defeSChris Fallin /// Note that this trait is implemented by the *context*, rather than
14f980defeSChris Fallin /// the item type, for somewhat complex lifetime reasons (lack of GATs
15f980defeSChris Fallin /// to allow `for<'ctx> Ctx<'ctx>`-like associated types in traits on
16f980defeSChris Fallin /// the value type).
17f980defeSChris Fallin pub trait CtxEq<V1: ?Sized, V2: ?Sized> {
18f980defeSChris Fallin /// Determine whether `a` and `b` are equal, given the context in
19f980defeSChris Fallin /// `self` and the union-find data structure `uf`.
ctx_eq(&self, a: &V1, b: &V2) -> bool20f980defeSChris Fallin fn ctx_eq(&self, a: &V1, b: &V2) -> bool;
21f980defeSChris Fallin }
22f980defeSChris Fallin
23f980defeSChris Fallin /// Trait that allows for hashing given some external context.
24f980defeSChris Fallin pub trait CtxHash<Value: ?Sized>: CtxEq<Value, Value> {
25f980defeSChris Fallin /// Compute the hash of `value`, given the context in `self` and
26f980defeSChris Fallin /// the union-find data structure `uf`.
ctx_hash<H: Hasher>(&self, state: &mut H, value: &Value)27f980defeSChris Fallin fn ctx_hash<H: Hasher>(&self, state: &mut H, value: &Value);
28f980defeSChris Fallin }
29f980defeSChris Fallin
30f980defeSChris Fallin /// A null-comparator context type for underlying value types that
31f980defeSChris Fallin /// already have `Eq` and `Hash`.
32f980defeSChris Fallin #[derive(Default)]
33f980defeSChris Fallin pub struct NullCtx;
34f980defeSChris Fallin
35f980defeSChris Fallin impl<V: Eq + Hash> CtxEq<V, V> for NullCtx {
ctx_eq(&self, a: &V, b: &V) -> bool36f980defeSChris Fallin fn ctx_eq(&self, a: &V, b: &V) -> bool {
37f980defeSChris Fallin a.eq(b)
38f980defeSChris Fallin }
39f980defeSChris Fallin }
40f980defeSChris Fallin impl<V: Eq + Hash> CtxHash<V> for NullCtx {
ctx_hash<H: Hasher>(&self, state: &mut H, value: &V)41f980defeSChris Fallin fn ctx_hash<H: Hasher>(&self, state: &mut H, value: &V) {
42f980defeSChris Fallin value.hash(state);
43f980defeSChris Fallin }
44f980defeSChris Fallin }
45f980defeSChris Fallin
46f980defeSChris Fallin /// A bucket in the hash table.
47f980defeSChris Fallin ///
48f980defeSChris Fallin /// Some performance-related design notes: we cache the hashcode for
49f980defeSChris Fallin /// speed, as this often buys a few percent speed in
50f980defeSChris Fallin /// interning-table-heavy workloads. We only keep the low 32 bits of
51f980defeSChris Fallin /// the hashcode, for memory efficiency: in common use, `K` and `V`
52f980defeSChris Fallin /// are often 32 bits also, and a 12-byte bucket is measurably better
53f980defeSChris Fallin /// than a 16-byte bucket.
54f980defeSChris Fallin struct BucketData<K, V> {
55f980defeSChris Fallin hash: u32,
56f980defeSChris Fallin k: K,
57f980defeSChris Fallin v: V,
58f980defeSChris Fallin }
59f980defeSChris Fallin
60f980defeSChris Fallin /// A HashMap that takes external context for all operations.
61f980defeSChris Fallin pub struct CtxHashMap<K, V> {
62f2fb00ddSJonas Kruckenberg raw: HashTable<BucketData<K, V>>,
63f980defeSChris Fallin }
64f980defeSChris Fallin
65f980defeSChris Fallin impl<K, V> CtxHashMap<K, V> {
66f980defeSChris Fallin /// Create an empty hashmap with pre-allocated space for the given
67f980defeSChris Fallin /// capacity.
with_capacity(capacity: usize) -> Self68f980defeSChris Fallin pub fn with_capacity(capacity: usize) -> Self {
69f980defeSChris Fallin Self {
70f2fb00ddSJonas Kruckenberg raw: HashTable::with_capacity(capacity),
71f980defeSChris Fallin }
72f980defeSChris Fallin }
73f980defeSChris Fallin }
74f980defeSChris Fallin
compute_hash<Ctx, K>(ctx: &Ctx, k: &K) -> u32 where Ctx: CtxHash<K>,75f980defeSChris Fallin fn compute_hash<Ctx, K>(ctx: &Ctx, k: &K) -> u32
76f980defeSChris Fallin where
77f980defeSChris Fallin Ctx: CtxHash<K>,
78f980defeSChris Fallin {
79132ef1e4SKirpal Grewal let mut hasher = rustc_hash::FxHasher::default();
80f980defeSChris Fallin ctx.ctx_hash(&mut hasher, k);
81f980defeSChris Fallin hasher.finish() as u32
82f980defeSChris Fallin }
83f980defeSChris Fallin
84f980defeSChris Fallin impl<K, V> CtxHashMap<K, V> {
85f980defeSChris Fallin /// Insert a new key-value pair, returning the old value associated
86f980defeSChris Fallin /// with this key (if any).
insert<Ctx>(&mut self, k: K, v: V, ctx: &Ctx) -> Option<V> where Ctx: CtxEq<K, K> + CtxHash<K>,87f980defeSChris Fallin pub fn insert<Ctx>(&mut self, k: K, v: V, ctx: &Ctx) -> Option<V>
88f980defeSChris Fallin where
89f980defeSChris Fallin Ctx: CtxEq<K, K> + CtxHash<K>,
90f980defeSChris Fallin {
91f980defeSChris Fallin let hash = compute_hash(ctx, &k);
92f2fb00ddSJonas Kruckenberg match self.raw.find_mut(hash as u64, |bucket| {
93f980defeSChris Fallin hash == bucket.hash && ctx.ctx_eq(&bucket.k, &k)
94f980defeSChris Fallin }) {
95*0889323aSSSD Some(bucket) => Some(core::mem::replace(&mut bucket.v, v)),
96f980defeSChris Fallin None => {
97f980defeSChris Fallin let data = BucketData { hash, k, v };
98f980defeSChris Fallin self.raw
99f2fb00ddSJonas Kruckenberg .insert_unique(hash as u64, data, |bucket| bucket.hash as u64);
100f980defeSChris Fallin None
101f980defeSChris Fallin }
102f980defeSChris Fallin }
103f980defeSChris Fallin }
104f980defeSChris Fallin
105f980defeSChris Fallin /// Look up a key, returning a borrow of the value if present.
get<'a, Q, Ctx>(&'a self, k: &Q, ctx: &Ctx) -> Option<&'a V> where Ctx: CtxEq<K, Q> + CtxHash<Q> + CtxHash<K>,106f980defeSChris Fallin pub fn get<'a, Q, Ctx>(&'a self, k: &Q, ctx: &Ctx) -> Option<&'a V>
107f980defeSChris Fallin where
108f980defeSChris Fallin Ctx: CtxEq<K, Q> + CtxHash<Q> + CtxHash<K>,
109f980defeSChris Fallin {
110f980defeSChris Fallin let hash = compute_hash(ctx, k);
111f980defeSChris Fallin self.raw
112f980defeSChris Fallin .find(hash as u64, |bucket| {
113f980defeSChris Fallin hash == bucket.hash && ctx.ctx_eq(&bucket.k, k)
114f980defeSChris Fallin })
115f2fb00ddSJonas Kruckenberg .map(|bucket| &bucket.v)
116f980defeSChris Fallin }
11782c0a09bSChris Fallin
11882c0a09bSChris Fallin /// Look up a key, returning an `Entry` that refers to an existing
11982c0a09bSChris Fallin /// value or allows inserting a new one.
entry<'a, Ctx>(&'a mut self, k: K, ctx: &Ctx) -> Entry<'a, K, V> where Ctx: CtxEq<K, K> + CtxHash<K>,12082c0a09bSChris Fallin pub fn entry<'a, Ctx>(&'a mut self, k: K, ctx: &Ctx) -> Entry<'a, K, V>
12182c0a09bSChris Fallin where
12282c0a09bSChris Fallin Ctx: CtxEq<K, K> + CtxHash<K>,
12382c0a09bSChris Fallin {
12482c0a09bSChris Fallin let hash = compute_hash(ctx, &k);
12582c0a09bSChris Fallin let raw = self.raw.entry(
12682c0a09bSChris Fallin hash as u64,
12782c0a09bSChris Fallin |bucket| hash == bucket.hash && ctx.ctx_eq(&bucket.k, &k),
12882c0a09bSChris Fallin |bucket| compute_hash(ctx, &bucket.k) as u64,
12982c0a09bSChris Fallin );
13082c0a09bSChris Fallin match raw {
13182c0a09bSChris Fallin hashbrown::hash_table::Entry::Occupied(o) => Entry::Occupied(OccupiedEntry { raw: o }),
13282c0a09bSChris Fallin hashbrown::hash_table::Entry::Vacant(v) => Entry::Vacant(VacantEntry {
13382c0a09bSChris Fallin hash,
13482c0a09bSChris Fallin key: k,
13582c0a09bSChris Fallin raw: v,
13682c0a09bSChris Fallin }),
13782c0a09bSChris Fallin }
13882c0a09bSChris Fallin }
13982c0a09bSChris Fallin }
14082c0a09bSChris Fallin
14182c0a09bSChris Fallin /// A reference to an existing or vacant entry in the hash table.
14282c0a09bSChris Fallin pub enum Entry<'a, K, V> {
14382c0a09bSChris Fallin Occupied(OccupiedEntry<'a, K, V>),
14482c0a09bSChris Fallin Vacant(VacantEntry<'a, K, V>),
14582c0a09bSChris Fallin }
14682c0a09bSChris Fallin
14782c0a09bSChris Fallin /// A reference to an occupied entry in the hash table.
14882c0a09bSChris Fallin pub struct OccupiedEntry<'a, K, V> {
14982c0a09bSChris Fallin raw: hashbrown::hash_table::OccupiedEntry<'a, BucketData<K, V>>,
15082c0a09bSChris Fallin }
15182c0a09bSChris Fallin
15282c0a09bSChris Fallin /// A reference to a vacant entry in the hash table.
15382c0a09bSChris Fallin pub struct VacantEntry<'a, K, V> {
15482c0a09bSChris Fallin hash: u32,
15582c0a09bSChris Fallin key: K,
15682c0a09bSChris Fallin raw: hashbrown::hash_table::VacantEntry<'a, BucketData<K, V>>,
15782c0a09bSChris Fallin }
15882c0a09bSChris Fallin
15982c0a09bSChris Fallin impl<'a, K, V> OccupiedEntry<'a, K, V> {
16082c0a09bSChris Fallin /// Get the existing value.
get(&self) -> &V16182c0a09bSChris Fallin pub fn get(&self) -> &V {
16282c0a09bSChris Fallin &self.raw.get().v
16382c0a09bSChris Fallin }
16482c0a09bSChris Fallin
16582c0a09bSChris Fallin /// Get the existing value, mutably.
get_mut(&mut self) -> &mut V16682c0a09bSChris Fallin pub fn get_mut(&mut self) -> &mut V {
16782c0a09bSChris Fallin &mut self.raw.get_mut().v
16882c0a09bSChris Fallin }
16982c0a09bSChris Fallin }
17082c0a09bSChris Fallin
17182c0a09bSChris Fallin impl<'a, K, V> VacantEntry<'a, K, V> {
17282c0a09bSChris Fallin /// Insert a new value.
insert(self, v: V)17382c0a09bSChris Fallin pub fn insert(self, v: V) {
17482c0a09bSChris Fallin self.raw.insert(BucketData {
17582c0a09bSChris Fallin hash: self.hash,
17682c0a09bSChris Fallin k: self.key,
17782c0a09bSChris Fallin v,
17882c0a09bSChris Fallin });
17982c0a09bSChris Fallin }
180f980defeSChris Fallin }
181f980defeSChris Fallin
182f980defeSChris Fallin #[cfg(test)]
183f980defeSChris Fallin mod test {
184f980defeSChris Fallin use super::*;
185f980defeSChris Fallin
186f980defeSChris Fallin #[derive(Clone, Copy, Debug)]
187f980defeSChris Fallin struct Key {
188f980defeSChris Fallin index: u32,
189f980defeSChris Fallin }
190f980defeSChris Fallin struct Ctx {
191f980defeSChris Fallin vals: &'static [&'static str],
192f980defeSChris Fallin }
193f980defeSChris Fallin impl CtxEq<Key, Key> for Ctx {
ctx_eq(&self, a: &Key, b: &Key) -> bool194f980defeSChris Fallin fn ctx_eq(&self, a: &Key, b: &Key) -> bool {
195f980defeSChris Fallin self.vals[a.index as usize].eq(self.vals[b.index as usize])
196f980defeSChris Fallin }
197f980defeSChris Fallin }
198f980defeSChris Fallin impl CtxHash<Key> for Ctx {
ctx_hash<H: Hasher>(&self, state: &mut H, value: &Key)199f980defeSChris Fallin fn ctx_hash<H: Hasher>(&self, state: &mut H, value: &Key) {
200f980defeSChris Fallin self.vals[value.index as usize].hash(state);
201f980defeSChris Fallin }
202f980defeSChris Fallin }
203f980defeSChris Fallin
204f980defeSChris Fallin #[test]
test_basic()205f980defeSChris Fallin fn test_basic() {
206f980defeSChris Fallin let ctx = Ctx {
207f980defeSChris Fallin vals: &["a", "b", "a"],
208f980defeSChris Fallin };
209f980defeSChris Fallin
210f980defeSChris Fallin let k0 = Key { index: 0 };
211f980defeSChris Fallin let k1 = Key { index: 1 };
212f980defeSChris Fallin let k2 = Key { index: 2 };
213f980defeSChris Fallin
214f980defeSChris Fallin assert!(ctx.ctx_eq(&k0, &k2));
215f980defeSChris Fallin assert!(!ctx.ctx_eq(&k0, &k1));
216f980defeSChris Fallin assert!(!ctx.ctx_eq(&k2, &k1));
217f980defeSChris Fallin
218f980defeSChris Fallin let mut map: CtxHashMap<Key, u64> = CtxHashMap::with_capacity(4);
219f980defeSChris Fallin assert_eq!(map.insert(k0, 42, &ctx), None);
220f980defeSChris Fallin assert_eq!(map.insert(k2, 84, &ctx), Some(42));
221f980defeSChris Fallin assert_eq!(map.get(&k1, &ctx), None);
222f980defeSChris Fallin assert_eq!(*map.get(&k0, &ctx).unwrap(), 84);
223f980defeSChris Fallin }
224f980defeSChris Fallin }
225