Expand description
§axhash-dashmap
Concurrent DashMap and DashSet collections for Rust.
Powered by dashmap (multi-shard RwLock) · Fueled by axhash (AES-NI accelerated hashing)
§Why axhash-dashmap?
dashmap::DashMap defaults to RandomState — a secure but slower hasher that
re-seeds on every process start. axhash-dashmap swaps the hasher for
axhash, which exploits hardware AES instructions (AES-NI on x86-64, AES on
ARMv8) to produce hashes at near-memory-bandwidth speed.
In a sharded concurrent map, every shard lookup begins with a hash to determine which shard to lock. A faster hasher therefore reduces latency at the point of highest contention.
§Ecosystem
| Crate | Description |
|---|---|
axhash | High-performance hashing engine |
axhash-map | Fast HashMap/HashSet powered by hashbrown |
axhash-indexmap | Ordered maps with AxHash |
axhash-dashmap | Concurrent DashMap powered by AxHash |
┌─────────────────────────────────────────────────────────────────┐
│ axhash-dashmap │
│ │
│ Type aliases (Serde-compatible) │
│ DashMap<K, V> DashSet<T> │
│ │
│ Branded newtypes (ergonomic constructors) │
│ AxDashMap<K, V> AxDashSet<T> │
│ │ │ │
│ dashmap::DashMap dashmap::DashSet │
│ ┌──────────┬─── ··· ───┬──────────┐ │
│ │ shard 0 │ shard 1 │ shard N │ ← parking_lot::RwLock │
│ └──────────┴─── ··· ───┴──────────┘ │
│ │ │
│ BuildHasherDefault<AxHasher> │
│ (AES-NI — shard selection + bucket probing) │
└─────────────────────────────────────────────────────────────────┘Key points:
- Only the hash step changes. All concurrency guarantees, shard-locking
semantics, and deadlock-safety properties of
dashmapare fully preserved. - Zero overhead.
AxDashMapis a#[inline(always)]newtype; the compiler sees through it entirely. - AES is detected at runtime. A portable software fallback is used automatically on CPUs without AES instructions — you ship one binary.
§Two usage modes
§Mode 1 — Type alias (DashMap / DashSet)
Plain type aliases over dashmap with BuildHasherDefault<AxHasher> baked in.
Because there is no wrapper struct, Serde and other #[derive]-based crates
work out of the box.
use axhash_dashmap::{DashMap, AxHasher};
use core::hash::BuildHasherDefault;
// Serde-compatible — no custom wrapper type in the type hierarchy.
let map: DashMap<&str, u32> =
dashmap::DashMap::with_hasher(BuildHasherDefault::<AxHasher>::default());
map.insert("hello", 42);§Mode 2 — Branded newtype (AxDashMap / AxDashSet)
A thin newtype wrapper that adds ::new() / ::with_capacity() /
::with_shard_amount() constructors (dashmap::DashMap::new() is only defined
for RandomState — not for generic S). Every DashMap method is accessible
transparently via Deref.
use axhash_dashmap::AxDashMap;
let map: AxDashMap<&str, u32> = AxDashMap::new();
map.insert("hello", 42);
assert_eq!(*map.get("hello").unwrap(), 42);| Need | Use |
|---|---|
::new() / ::with_capacity() / ::with_shard_amount() | AxDashMap / AxDashSet |
Serde #[derive(Serialize, Deserialize)] | DashMap / DashSet |
| Custom / seeded hasher | AxDashMap::with_hasher(AxBuildHasher::with_seed(s)) |
Raw dashmap access | RawDashMap / RawDashSet |
§Installation
[dependencies]
axhash-dashmap = "0.1"§Optional features
# Enable serde::Serialize / Deserialize on DashMap / DashSet
axhash-dashmap = { version = "0.1", features = ["serde"] }
# Enable rayon parallel iterators
axhash-dashmap = { version = "0.1", features = ["rayon"] }§Quick start
§AxDashMap
use axhash_dashmap::AxDashMap;
// Create — zero-overhead newtype wrapper.
let scores: AxDashMap<&str, u32> = AxDashMap::new();
scores.insert("alice", 42);
scores.insert("bob", 17);
scores.insert("carol", 99);
// Lookup (returns a Ref guard — no clone needed).
assert_eq!(*scores.get("alice").unwrap(), 42);
// Entry API — increment atomically within a shard lock.
scores.entry("alice").and_modify(|v| *v += 1);
assert_eq!(*scores.get("alice").unwrap(), 43);
// Concurrent iteration.
let total: u32 = scores.iter().map(|r| *r.value()).sum();
assert_eq!(total, 159);
// Bulk mutation without extra allocation.
scores.alter_all(|_, v| v * 2);
assert_eq!(*scores.get("alice").unwrap(), 86);
// Predicate-based removal.
scores.retain(|_, v| *v > 50);§AxDashSet
use axhash_dashmap::AxDashSet;
let seen: AxDashSet<u64> = AxDashSet::new();
seen.insert(1);
seen.insert(2);
seen.insert(2); // duplicate — silently ignored
assert_eq!(seen.len(), 2);
assert!(seen.contains(&1));§Concurrent usage with Arc
use std::sync::Arc;
use axhash_dashmap::AxDashMap;
let map: Arc<AxDashMap<u32, u32>> = Arc::new(AxDashMap::with_capacity(10_000));
let handles: Vec<_> = (0..8)
.map(|t| {
let m = Arc::clone(&map);
std::thread::spawn(move || {
for i in 0u32..125 {
m.insert(t * 125 + i, i * i);
}
})
})
.collect();
for h in handles { h.join().unwrap(); }
assert_eq!(map.len(), 1_000);§Constructors
§AxDashMap
| Goal | Call |
|---|---|
| Empty map, default shards | AxDashMap::new() |
| Pre-allocated map | AxDashMap::with_capacity(n) |
| Custom shard count | AxDashMap::with_shard_amount(shards) |
| Capacity + shard count | AxDashMap::with_capacity_and_shard_amount(n, shards) |
| Default (zero seed) | AxDashMap::default() |
| Seeded hasher | AxDashMap::with_hasher(AxBuildHasher::with_seed(s)) |
| Seeded + capacity | AxDashMap::with_capacity_and_hasher(n, AxBuildHasher::with_seed(s)) |
| Seeded + shard count | AxDashMap::with_hasher_and_shard_amount(AxBuildHasher::with_seed(s), shards) |
Shard count tip:
shard_amountmust be a power of two. Default = logical CPUs × 4, rounded up to the next power of two. More shards → less contention under high thread counts. Fewer shards → lower memory overhead for small maps.
§AxDashSet
| Goal | Call |
|---|---|
| Empty set, default shards | AxDashSet::new() |
| Pre-allocated set | AxDashSet::with_capacity(n) |
| Default (zero seed) | AxDashSet::default() |
| Seeded hasher | AxDashSet::with_hasher(AxBuildHasher::with_seed(s)) |
| Seeded + capacity | AxDashSet::with_capacity_and_hasher(n, AxBuildHasher::with_seed(s)) |
§Seeded construction
Supply a random seed to harden against hash-flooding attacks when keys come from untrusted input (e.g. HTTP parameters, user-supplied JSON):
use axhash_dashmap::{AxDashMap, AxBuildHasher};
// In production: derive seed from OS entropy (rand, getrandom, etc.).
let seed: u64 = 0xdeadbeef_cafebabe;
let map: AxDashMap<String, u32, AxBuildHasher> =
AxDashMap::with_hasher(AxBuildHasher::with_seed(seed));
map.insert("untrusted_key".to_string(), 1);§All dashmap methods are available
AxDashMap and AxDashSet implement Deref / DerefMut to the underlying
dashmap::DashMap / dashmap::DashSet, so every method is directly accessible
without extra imports — entry, iter, alter, alter_all, retain,
into_read_only, shrink_to_fit, get_mut, and more.
use axhash_dashmap::AxDashMap;
let map: AxDashMap<&str, u32> = AxDashMap::new();
map.insert("hits", 0);
// entry — modify-or-insert within a single shard lock.
map.entry("hits").and_modify(|n| *n += 1);
// retain — predicate-based removal.
map.insert("temp", 0);
map.retain(|_, v| *v > 0);
assert!(!map.contains_key("temp"));
// into_read_only — zero-copy promotion to a read-only view.
let map: AxDashMap<&str, u32> = [("a", 1), ("b", 2)].into_iter().collect();
let ro = map.into_inner().into_read_only();
assert_eq!(*ro.get("a").unwrap(), 1);§Interoperability with raw dashmap types
The crate re-exports RawDashMap and RawDashSet and provides From
conversions in both directions so you can cross the boundary without a direct
dashmap dependency in your own Cargo.toml.
use core::hash::BuildHasherDefault;
use axhash_dashmap::{AxDashMap, RawDashMap, AxHasher};
// Wrap a raw dashmap.
let raw: RawDashMap<&str, u32, BuildHasherDefault<AxHasher>> =
dashmap::DashMap::with_hasher(BuildHasherDefault::default());
raw.insert("x", 99);
let ax: AxDashMap<&str, u32> = raw.into();
assert_eq!(*ax.get("x").unwrap(), 99);
// Unwrap back to dashmap.
let raw: RawDashMap<&str, u32, BuildHasherDefault<AxHasher>> = ax.into();
assert_eq!(*raw.get("x").unwrap(), 99);§Feature flags
| Flag | Default | Effect |
|---|---|---|
serde | ❌ off | Enables serde::Serialize / Deserialize on DashMap / DashSet. |
rayon | ❌ off | Enables rayon parallel iterators on DashMap / DashSet. |
§Dependency footprint
axhash-dashmap
├── axhash-core (AxHasher + AxBuildHasher — AES hash engine)
└── dashmap (multi-shard RwLock concurrent map, no default features)§License
MIT — see LICENSE.
Structs§
- AxBuild
Hasher - The AES-NI accelerated hasher. Re-exported so callers don’t need a direct
axhash-coredependency. - AxDash
Map - AxDash
Set - Concurrent hash set backed by dashmap (multi-shard
RwLock) withAxHasher(AES-NI accelerated hashing) as the default hasher. - AxHasher
- The AES-NI accelerated hasher. Re-exported so callers don’t need a direct
axhash-coredependency. - Dash
Occupied Entry - Dash
Vacant Entry - RawDash
Map - Raw
dashmap::DashMap— available without a directdashmapdep. DashMap is an implementation of a concurrent associative array/hashmap in Rust. - RawDash
Set - Raw
dashmap::DashSet— available without a directdashmapdep. DashSet is a thin wrapper aroundDashMapusing()as the value type. It uses methods and types which are more convenient to work with on a set.
Enums§
- Dash
Entry - Dashmap entry types — re-exported for ergonomic
matcharms.