Skip to main content

axhash_dashmap/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use core::fmt;
4use core::hash::{BuildHasher, BuildHasherDefault, Hash};
5use core::ops::{Deref, DerefMut};
6
7// ── Re-exports ────────────────────────────────────────────────────────────────
8
9/// The AES-NI accelerated hasher.  Re-exported so callers don't need a direct
10/// `axhash-core` dependency.
11pub use axhash_core::{AxBuildHasher, AxHasher};
12
13/// Raw `dashmap::DashMap` — available without a direct `dashmap` dep.
14pub use dashmap::DashMap as RawDashMap;
15
16/// Raw `dashmap::DashSet` — available without a direct `dashmap` dep.
17pub use dashmap::DashSet as RawDashSet;
18
19/// Dashmap entry types — re-exported for ergonomic `match` arms.
20pub use dashmap::Entry as DashEntry;
21pub use dashmap::OccupiedEntry as DashOccupiedEntry;
22pub use dashmap::VacantEntry as DashVacantEntry;
23
24// ── Compatibility type aliases ────────────────────────────────────────────────
25//
26// These expose raw `dashmap` types with `BuildHasherDefault<AxHasher>` baked
27// in.  Because they are plain type aliases (no wrapper struct), third-party
28// crates such as Serde can derive `Serialize` / `Deserialize` on structs that
29// contain them without any extra configuration.
30//
31// Note: `DashMap::new()` uses `RandomState` — it does NOT accept a generic S.
32// To create these aliases you must call `DashMap::with_hasher(...)` directly,
33// or use the `AxDashMap` / `AxDashSet` branded newtypes below which provide
34// ergonomic `::new()` constructors.
35
36/// Drop-in `dashmap::DashMap` with [`AxHasher`] as the default hasher.
37///
38/// Use this alias when maximum third-party compatibility matters (e.g. Serde
39/// `#[derive]`).  All `DashMap` methods are available directly.
40///
41/// ```
42/// use axhash_dashmap::{DashMap, AxHasher};
43/// use core::hash::BuildHasherDefault;
44///
45/// let map: DashMap<&str, u32> =
46///     dashmap::DashMap::with_hasher(BuildHasherDefault::<AxHasher>::default());
47/// map.insert("hello", 42);
48/// assert_eq!(*map.get("hello").unwrap(), 42);
49/// ```
50pub type DashMap<K, V> = RawDashMap<K, V, BuildHasherDefault<AxHasher>>;
51
52/// Drop-in `dashmap::DashSet` with [`AxHasher`] as the default hasher.
53///
54/// Use this alias when maximum third-party compatibility matters (e.g. Serde
55/// `#[derive]`).  All `DashSet` methods are available directly.
56///
57/// ```
58/// use axhash_dashmap::{DashSet, AxHasher};
59/// use core::hash::BuildHasherDefault;
60///
61/// let set: DashSet<u32> =
62///     dashmap::DashSet::with_hasher(BuildHasherDefault::<AxHasher>::default());
63/// set.insert(1);
64/// set.insert(2);
65/// assert_eq!(set.len(), 2);
66/// ```
67pub type DashSet<T> = RawDashSet<T, BuildHasherDefault<AxHasher>>;
68
69// ── AxDashMap ─────────────────────────────────────────────────────────────────
70pub struct AxDashMap<K, V, S = BuildHasherDefault<AxHasher>>(RawDashMap<K, V, S>);
71
72impl<K, V> AxDashMap<K, V, BuildHasherDefault<AxHasher>>
73where
74    K: Hash + Eq,
75{
76    /// Creates an empty map with the default [`AxHasher`] and the default shard
77    /// count (number of shards = logical CPUs × 4, clamped to a power of two).
78    #[inline(always)]
79    pub fn new() -> Self {
80        Self(RawDashMap::with_hasher(BuildHasherDefault::default()))
81    }
82
83    /// Creates an empty map pre-allocated for at least `capacity` entries,
84    /// using the default [`AxHasher`] and the default shard count.
85    #[inline(always)]
86    pub fn with_capacity(capacity: usize) -> Self {
87        Self(RawDashMap::with_capacity_and_hasher(
88            capacity,
89            BuildHasherDefault::default(),
90        ))
91    }
92
93    /// Creates an empty map with the default [`AxHasher`] and an explicit shard
94    /// count.  `shard_amount` **must** be a power of two; dashmap panics
95    /// otherwise.
96    ///
97    /// More shards → less lock contention under high concurrency.
98    /// Fewer shards → lower memory overhead.
99    #[inline(always)]
100    pub fn with_shard_amount(shard_amount: usize) -> Self {
101        Self(RawDashMap::with_hasher_and_shard_amount(
102            BuildHasherDefault::default(),
103            shard_amount,
104        ))
105    }
106
107    /// Creates an empty map with the default [`AxHasher`], a pre-allocated
108    /// capacity, and an explicit shard count.
109    #[inline(always)]
110    pub fn with_capacity_and_shard_amount(capacity: usize, shard_amount: usize) -> Self {
111        Self(RawDashMap::with_capacity_and_hasher_and_shard_amount(
112            capacity,
113            BuildHasherDefault::default(),
114            shard_amount,
115        ))
116    }
117}
118
119impl<K, V, S> AxDashMap<K, V, S>
120where
121    K: Hash + Eq,
122    S: BuildHasher + Clone,
123{
124    /// Creates an empty map using a custom [`BuildHasher`].
125    ///
126    /// Use this when you need a seeded hasher for hash-flooding resistance:
127    ///
128    /// ```
129    /// use axhash_dashmap::{AxDashMap, AxBuildHasher};
130    ///
131    /// let seed: u64 = 0xdeadbeef_cafebabe;
132    /// let map: AxDashMap<String, u32, AxBuildHasher> =
133    ///     AxDashMap::with_hasher(AxBuildHasher::with_seed(seed));
134    /// ```
135    #[inline(always)]
136    pub fn with_hasher(hasher: S) -> Self {
137        Self(RawDashMap::with_hasher(hasher))
138    }
139
140    /// Creates an empty map with at least `capacity` entries and a custom
141    /// [`BuildHasher`].
142    #[inline(always)]
143    pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self {
144        Self(RawDashMap::with_capacity_and_hasher(capacity, hasher))
145    }
146
147    /// Creates an empty map with a custom [`BuildHasher`] and an explicit shard
148    /// count.
149    #[inline(always)]
150    pub fn with_hasher_and_shard_amount(hasher: S, shard_amount: usize) -> Self {
151        Self(RawDashMap::with_hasher_and_shard_amount(
152            hasher,
153            shard_amount,
154        ))
155    }
156
157    /// Creates an empty map with a pre-allocated capacity, a custom
158    /// [`BuildHasher`], and an explicit shard count.
159    #[inline(always)]
160    pub fn with_capacity_and_hasher_and_shard_amount(
161        capacity: usize,
162        hasher: S,
163        shard_amount: usize,
164    ) -> Self {
165        Self(RawDashMap::with_capacity_and_hasher_and_shard_amount(
166            capacity,
167            hasher,
168            shard_amount,
169        ))
170    }
171
172    /// Consumes the wrapper and returns the underlying [`RawDashMap`].
173    #[inline(always)]
174    pub fn into_inner(self) -> RawDashMap<K, V, S> {
175        self.0
176    }
177}
178
179// ── Deref / DerefMut ─────────────────────────────────────────────────────────
180
181impl<K, V, S> Deref for AxDashMap<K, V, S> {
182    type Target = RawDashMap<K, V, S>;
183
184    #[inline(always)]
185    fn deref(&self) -> &Self::Target {
186        &self.0
187    }
188}
189
190impl<K, V, S> DerefMut for AxDashMap<K, V, S> {
191    #[inline(always)]
192    fn deref_mut(&mut self) -> &mut Self::Target {
193        &mut self.0
194    }
195}
196
197// ── Standard traits ───────────────────────────────────────────────────────────
198
199impl<K, V> Default for AxDashMap<K, V, BuildHasherDefault<AxHasher>>
200where
201    K: Hash + Eq,
202{
203    #[inline(always)]
204    fn default() -> Self {
205        Self::new()
206    }
207}
208
209impl<K, V, S> fmt::Debug for AxDashMap<K, V, S>
210where
211    K: Hash + Eq + fmt::Debug,
212    V: fmt::Debug,
213    S: BuildHasher + Clone,
214{
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        self.0.fmt(f)
217    }
218}
219
220// ── FromIterator / Extend ─────────────────────────────────────────────────────
221
222impl<K, V> FromIterator<(K, V)> for AxDashMap<K, V, BuildHasherDefault<AxHasher>>
223where
224    K: Hash + Eq,
225{
226    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
227        let iter = iter.into_iter();
228        let (lower, _) = iter.size_hint();
229        let map = Self::with_capacity(lower);
230        iter.for_each(|(k, v)| {
231            map.insert(k, v);
232        });
233        map
234    }
235}
236
237impl<K, V, S> Extend<(K, V)> for AxDashMap<K, V, S>
238where
239    K: Hash + Eq,
240    S: BuildHasher + Clone,
241{
242    #[inline]
243    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
244        iter.into_iter().for_each(|(k, v)| {
245            self.0.insert(k, v);
246        });
247    }
248}
249
250// ── From conversions ──────────────────────────────────────────────────────────
251
252impl<K, V, S> From<RawDashMap<K, V, S>> for AxDashMap<K, V, S> {
253    #[inline(always)]
254    fn from(inner: RawDashMap<K, V, S>) -> Self {
255        Self(inner)
256    }
257}
258
259impl<K, V, S> From<AxDashMap<K, V, S>> for RawDashMap<K, V, S> {
260    #[inline(always)]
261    fn from(wrapper: AxDashMap<K, V, S>) -> Self {
262        wrapper.0
263    }
264}
265
266// ── AxDashSet ─────────────────────────────────────────────────────────────────
267
268/// Concurrent hash set backed by [dashmap] (multi-shard `RwLock`) with
269/// [`AxHasher`] (AES-NI accelerated hashing) as the default hasher.
270///
271/// `AxDashSet<T>` is a thin newtype wrapper around [`DashSet<T>`] that adds
272/// ergonomic `::new()` / `::with_capacity()` / `::with_shard_amount()`
273/// constructors.  Every `DashSet` method is accessible transparently via
274/// `Deref`.
275///
276/// [dashmap]: https://crates.io/crates/dashmap
277pub struct AxDashSet<T, S = BuildHasherDefault<AxHasher>>(RawDashSet<T, S>);
278
279impl<T> AxDashSet<T, BuildHasherDefault<AxHasher>>
280where
281    T: Hash + Eq,
282{
283    /// Creates an empty set with the default [`AxHasher`] and the default shard
284    /// count.
285    #[inline(always)]
286    pub fn new() -> Self {
287        Self(RawDashSet::with_hasher(BuildHasherDefault::default()))
288    }
289
290    /// Creates an empty set pre-allocated for at least `capacity` entries.
291    #[inline(always)]
292    pub fn with_capacity(capacity: usize) -> Self {
293        Self(RawDashSet::with_capacity_and_hasher(
294            capacity,
295            BuildHasherDefault::default(),
296        ))
297    }
298}
299
300impl<T, S> AxDashSet<T, S>
301where
302    T: Hash + Eq,
303    S: BuildHasher + Clone,
304{
305    /// Creates an empty set using a custom [`BuildHasher`].
306    #[inline(always)]
307    pub fn with_hasher(hasher: S) -> Self {
308        Self(RawDashSet::with_hasher(hasher))
309    }
310
311    /// Creates an empty set with at least `capacity` entries and a custom
312    /// [`BuildHasher`].
313    #[inline(always)]
314    pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self {
315        Self(RawDashSet::with_capacity_and_hasher(capacity, hasher))
316    }
317
318    /// Consumes the wrapper and returns the underlying [`RawDashSet`].
319    #[inline(always)]
320    pub fn into_inner(self) -> RawDashSet<T, S> {
321        self.0
322    }
323}
324
325// ── Deref / DerefMut ─────────────────────────────────────────────────────────
326
327impl<T, S> Deref for AxDashSet<T, S> {
328    type Target = RawDashSet<T, S>;
329
330    #[inline(always)]
331    fn deref(&self) -> &Self::Target {
332        &self.0
333    }
334}
335
336impl<T, S> DerefMut for AxDashSet<T, S> {
337    #[inline(always)]
338    fn deref_mut(&mut self) -> &mut Self::Target {
339        &mut self.0
340    }
341}
342
343// ── Standard traits ───────────────────────────────────────────────────────────
344
345impl<T> Default for AxDashSet<T, BuildHasherDefault<AxHasher>>
346where
347    T: Hash + Eq,
348{
349    #[inline(always)]
350    fn default() -> Self {
351        Self::new()
352    }
353}
354
355impl<T, S> fmt::Debug for AxDashSet<T, S>
356where
357    T: Hash + Eq + fmt::Debug,
358    S: BuildHasher + Clone,
359{
360    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361        self.0.fmt(f)
362    }
363}
364
365// ── FromIterator / Extend ─────────────────────────────────────────────────────
366
367impl<T> FromIterator<T> for AxDashSet<T, BuildHasherDefault<AxHasher>>
368where
369    T: Hash + Eq,
370{
371    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
372        let iter = iter.into_iter();
373        let (lower, _) = iter.size_hint();
374        let set = Self::with_capacity(lower);
375        iter.for_each(|v| {
376            set.insert(v);
377        });
378        set
379    }
380}
381
382impl<T, S> Extend<T> for AxDashSet<T, S>
383where
384    T: Hash + Eq,
385    S: BuildHasher + Clone,
386{
387    #[inline]
388    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
389        iter.into_iter().for_each(|v| {
390            self.0.insert(v);
391        });
392    }
393}
394
395// ── From conversions ──────────────────────────────────────────────────────────
396
397impl<T, S> From<RawDashSet<T, S>> for AxDashSet<T, S> {
398    #[inline(always)]
399    fn from(inner: RawDashSet<T, S>) -> Self {
400        Self(inner)
401    }
402}
403
404impl<T, S> From<AxDashSet<T, S>> for RawDashSet<T, S> {
405    #[inline(always)]
406    fn from(wrapper: AxDashSet<T, S>) -> Self {
407        wrapper.0
408    }
409}
410
411// ── Tests ─────────────────────────────────────────────────────────────────────
412
413#[cfg(test)]
414mod tests {
415    use super::*;
416    use core::hash::BuildHasherDefault;
417
418    // ── AxDashMap ─────────────────────────────────────────────────────────────
419
420    #[test]
421    fn map_basic_operations() {
422        let map: AxDashMap<&str, u32> = AxDashMap::new();
423        assert!(map.is_empty());
424
425        map.insert("one", 1);
426        map.insert("two", 2);
427        map.insert("three", 3);
428
429        assert_eq!(map.len(), 3);
430        assert_eq!(*map.get("one").unwrap(), 1);
431        assert!(map.get("missing").is_none());
432
433        map.remove("two");
434        assert_eq!(map.len(), 2);
435    }
436
437    #[test]
438    fn map_with_capacity() {
439        let map: AxDashMap<u32, u32> = AxDashMap::with_capacity(1024);
440        assert!(map.is_empty());
441    }
442
443    #[test]
444    fn map_with_shard_amount() {
445        let map: AxDashMap<u32, u32> = AxDashMap::with_shard_amount(16);
446        map.insert(1, 10);
447        assert_eq!(*map.get(&1).unwrap(), 10);
448    }
449
450    #[test]
451    fn map_with_capacity_and_shard_amount() {
452        let map: AxDashMap<u32, u32> = AxDashMap::with_capacity_and_shard_amount(512, 8);
453        assert!(map.is_empty());
454    }
455
456    #[test]
457    fn map_default() {
458        let map: AxDashMap<u64, u64> = AxDashMap::default();
459        assert!(map.is_empty());
460    }
461
462    #[test]
463    fn map_seeded_hasher() {
464        let seed: u64 = 0xc0ffee_deadbeef;
465        let map: AxDashMap<&str, i32, AxBuildHasher> =
466            AxDashMap::with_hasher(AxBuildHasher::with_seed(seed));
467        map.insert("seeded", -1);
468        assert_eq!(*map.get("seeded").unwrap(), -1);
469    }
470
471    #[test]
472    fn map_entry_api() {
473        let map: AxDashMap<&str, u32> = AxDashMap::new();
474        for _ in 0..5 {
475            map.entry("counter").and_modify(|n| *n += 1).or_insert(0);
476        }
477        assert_eq!(*map.get("counter").unwrap(), 4);
478    }
479
480    #[test]
481    fn map_from_iterator() {
482        let map: AxDashMap<&str, u32> = [("a", 1u32), ("b", 2), ("c", 3)].into_iter().collect();
483        assert_eq!(map.len(), 3);
484        assert_eq!(*map.get("b").unwrap(), 2);
485    }
486
487    #[test]
488    fn map_extend() {
489        let mut map: AxDashMap<u32, u32> = AxDashMap::new();
490        map.extend([(1u32, 10), (2, 20), (3, 30)]);
491        assert_eq!(map.len(), 3);
492        assert_eq!(*map.get(&2u32).unwrap(), 20);
493    }
494
495    #[test]
496    fn map_iter() {
497        let map: AxDashMap<u32, u32> = [(1u32, 10), (2, 20), (3, 30)].into_iter().collect();
498        let sum: u32 = map.iter().map(|r| *r.value()).sum();
499        assert_eq!(sum, 60);
500    }
501
502    #[test]
503    fn map_alter() {
504        let map: AxDashMap<u32, u32> = [(1u32, 1), (2, 2), (3, 3)].into_iter().collect();
505        map.alter_all(|_, v| v * 10);
506        assert_eq!(*map.get(&1u32).unwrap(), 10);
507        assert_eq!(*map.get(&3u32).unwrap(), 30);
508    }
509
510    #[test]
511    fn map_retain() {
512        let map: AxDashMap<u32, u32> = (0u32..10).map(|i| (i, i * i)).collect();
513        map.retain(|_, v| *v > 25);
514        assert!(map.iter().all(|r| *r.value() > 25));
515    }
516
517    #[test]
518    fn map_into_read_only() {
519        let map: AxDashMap<&str, u32> = AxDashMap::new();
520        map.insert("ro", 99);
521        let ro = map.into_inner().into_read_only();
522        assert_eq!(*ro.get("ro").unwrap(), 99);
523    }
524
525    #[test]
526    fn map_into_inner_roundtrip() {
527        let map: AxDashMap<&str, i32> = AxDashMap::new();
528        map.insert("x", 99);
529        let raw: RawDashMap<&str, i32, BuildHasherDefault<AxHasher>> = map.into_inner();
530        assert_eq!(*raw.get("x").unwrap(), 99);
531        let wrapped: AxDashMap<&str, i32> = raw.into();
532        assert_eq!(*wrapped.get("x").unwrap(), 99);
533    }
534
535    #[test]
536    fn map_concurrent_insert() {
537        use std::sync::Arc;
538        let map: Arc<AxDashMap<u32, u32>> = Arc::new(AxDashMap::with_capacity(1_000));
539        let handles: Vec<_> = (0..8)
540            .map(|t| {
541                let m = Arc::clone(&map);
542                std::thread::spawn(move || {
543                    for i in 0u32..125 {
544                        m.insert(t * 125 + i, i);
545                    }
546                })
547            })
548            .collect();
549        for h in handles {
550            h.join().unwrap();
551        }
552        assert_eq!(map.len(), 1_000);
553    }
554
555    // ── Type alias: DashMap ───────────────────────────────────────────────────
556
557    #[test]
558    fn alias_dashmap_basic() {
559        let map: DashMap<&str, u32> = dashmap::DashMap::with_hasher(BuildHasherDefault::default());
560        map.insert("hello", 42);
561        assert_eq!(*map.get("hello").unwrap(), 42);
562    }
563
564    // ── AxDashSet ─────────────────────────────────────────────────────────────
565
566    #[test]
567    fn set_basic_operations() {
568        let set: AxDashSet<u32> = AxDashSet::new();
569        assert!(set.is_empty());
570
571        set.insert(1);
572        set.insert(2);
573        set.insert(2); // duplicate
574        set.insert(3);
575
576        assert_eq!(set.len(), 3);
577        assert!(set.contains(&1));
578        assert!(!set.contains(&99));
579
580        set.remove(&2);
581        assert_eq!(set.len(), 2);
582    }
583
584    #[test]
585    fn set_default() {
586        let set: AxDashSet<u64> = AxDashSet::default();
587        assert!(set.is_empty());
588    }
589
590    #[test]
591    fn set_from_iterator() {
592        let set: AxDashSet<u32> = [1u32, 2, 3, 2, 1].into_iter().collect();
593        assert_eq!(set.len(), 3);
594    }
595
596    #[test]
597    fn set_extend() {
598        let mut set: AxDashSet<u32> = AxDashSet::new();
599        set.extend([1u32, 2, 3]);
600        set.extend([3u32, 4, 5]);
601        assert_eq!(set.len(), 5);
602    }
603
604    #[test]
605    fn set_iter() {
606        let set: AxDashSet<u32> = [1u32, 2, 3].into_iter().collect();
607        let sum: u32 = set.iter().map(|r| *r).sum();
608        assert_eq!(sum, 6);
609    }
610
611    #[test]
612    fn set_into_inner_roundtrip() {
613        let set: AxDashSet<i32> = AxDashSet::new();
614        set.insert(42);
615        let raw: RawDashSet<i32, BuildHasherDefault<AxHasher>> = set.into_inner();
616        assert!(raw.contains(&42));
617        let wrapped: AxDashSet<i32> = raw.into();
618        assert!(wrapped.contains(&42));
619    }
620
621    #[test]
622    fn set_concurrent_insert() {
623        use std::sync::Arc;
624        let set: Arc<AxDashSet<u32>> = Arc::new(AxDashSet::with_capacity(1_000));
625        let handles: Vec<_> = (0..8)
626            .map(|t| {
627                let s = Arc::clone(&set);
628                std::thread::spawn(move || {
629                    for i in 0u32..125 {
630                        s.insert(t * 125 + i);
631                    }
632                })
633            })
634            .collect();
635        for h in handles {
636            h.join().unwrap();
637        }
638        assert_eq!(set.len(), 1_000);
639    }
640
641    // ── Type alias: DashSet ───────────────────────────────────────────────────
642
643    #[test]
644    fn alias_dashset_basic() {
645        let set: DashSet<u32> = dashmap::DashSet::with_hasher(BuildHasherDefault::default());
646        set.insert(1);
647        set.insert(2);
648        set.insert(2);
649        assert_eq!(set.len(), 2);
650    }
651}