axhash-map 0.1.0

High-performance HashMap and HashSet backed by hashbrown (SwissTable) and fueled by axhash.
Documentation

axhash-map

High-performance HashMap and HashSet collections for Rust.

Powered by hashbrown (SwissTable layout) · Fueled by axhash (AES-NI accelerated hashing)

Crates.io Docs.rs License: MIT


Why axhash-map?

std::collections::HashMap uses SipHash-1-3 by default — a secure but comparatively slow hash function. For most workloads you don't need cryptographic resistance; you need throughput.

axhash-map 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. The underlying table is hashbrown (SwissTable), the same implementation that backs std::collections::HashMap in Rust's standard library — so the table operations are identical; only the hashing step is faster.

┌─────────────────────────────────────────┐
│              axhash-map                 │
│                                         │
│   AxHashMap<K, V>    AxHashSet<T>       │
│         │                  │            │
│   hashbrown::HashMap  hashbrown::HashSet│
│   (SwissTable layout)                   │
│         │                  │            │
│       axhash::AxBuildHasher             │
│   (AES-NI accelerated hash engine)      │
└─────────────────────────────────────────┘

Benchmark Results

Measured on Apple Silicon (release build, N = 100,000 items).

Hasher comparison:

  • AxHashMap (hashbrown + axhash)
  • std::collections::HashMap (SipHash-1-3)
Scenario AxHashMap std HashMap Speedup
Insert — u64 keys 379 µs 1,032 µs 2.7×
Insert — String keys 896 µs 1,673 µs 1.9×
Lookup — all hits 200 µs 748 µs 3.7×
Lookup — 50% hit / 50% miss 767 µs 1,994 µs 2.6×
Iteration (full scan) 130 µs 124 µs ~equal

Iteration performance is effectively identical because iteration does not invoke the hasher. Both maps use the same SwissTable layout provided by hashbrown.

Run the benchmarks yourself:

cargo bench --bench map_comparison
# HTML reports → target/criterion/

Installation

# Cargo.toml
[dependencies]
axhash-map = "0.1"

No feature flags required. The AES acceleration is detected at runtime; a portable fallback is used automatically on CPUs without AES instructions.


Quick start

HashMap

use axhash_map::AxHashMap;

// Create an empty map.
let mut scores: AxHashMap<&str, u32> = AxHashMap::new();

scores.insert("alice", 42);
scores.insert("bob",   17);
scores.insert("carol", 99);

// Index operator
println!("{}", scores["alice"]); // 42

// Safe lookup
if let Some(&s) = scores.get("bob") {
    println!("bob scored {s}");
}

// Iteration
for (name, score) in &scores {
    println!("{name}: {score}");
}

HashSet

use axhash_map::AxHashSet;

let mut seen: AxHashSet<u64> = AxHashSet::new();

seen.insert(1);
seen.insert(2);
seen.insert(2); // duplicate — ignored

assert_eq!(seen.len(), 2);
assert!(seen.contains(&1));

// Set operations (via Deref to hashbrown::HashSet)
let a: AxHashSet<u32> = [1, 2, 3].into_iter().collect();
let b: AxHashSet<u32> = [2, 3, 4].into_iter().collect();

let union:        AxHashSet<u32> = a.union(&b).copied().collect();
let intersection: AxHashSet<u32> = a.intersection(&b).copied().collect();

Constructors

Both AxHashMap and AxHashSet expose the same constructor family:

use axhash_map::{AxHashMap, AxHashSet, AxBuildHasher};

// Default seed (fastest, deterministic within a process)
let map: AxHashMap<String, i32> = AxHashMap::new();
let set: AxHashSet<String>      = AxHashSet::new();

// Pre-allocate to avoid rehashing
let map = AxHashMap::<String, i32>::with_capacity(10_000);
let set = AxHashSet::<String>::with_capacity(10_000);

// Custom seed — use a random value for hash-flooding resistance
let hasher = AxBuildHasher::with_seed(0xdeadbeef_cafebabe);
let map: AxHashMap<String, i32> = AxHashMap::with_hasher(hasher);

// Custom seed + pre-allocated capacity
let hasher = AxBuildHasher::with_seed(0xdeadbeef_cafebabe);
let map: AxHashMap<String, i32> = AxHashMap::with_capacity_and_hasher(10_000, hasher);

Collecting from iterators

use axhash_map::{AxHashMap, AxHashSet};

// FromIterator for AxHashMap
let map: AxHashMap<&str, usize> = vec![("a", 1), ("b", 2), ("c", 3)]
    .into_iter()
    .collect();

// FromIterator for AxHashSet
let set: AxHashSet<i32> = [1, 2, 3, 2, 1].into_iter().collect(); // len == 3

// Extend
let mut map: AxHashMap<u32, u32> = AxHashMap::new();
map.extend([(1, 10), (2, 20)]);
map.extend([(3, 30), (4, 40)]);

All hashbrown methods are available

AxHashMap and AxHashSet implement Deref / DerefMut to the underlying hashbrown::HashMap / hashbrown::HashSet, so every method on those types — entry, retain, drain, reserve, shrink_to_fit, raw_entry, and more — is directly accessible without any extra imports.

use axhash_map::AxHashMap;

let mut map: AxHashMap<&str, u32> = AxHashMap::new();

// entry API (from hashbrown, via Deref)
map.entry("hits").and_modify(|n| *n += 1).or_insert(1);
map.entry("hits").and_modify(|n| *n += 1).or_insert(1);
assert_eq!(map["hits"], 2);

// retain
map.insert("temp", 0);
map.retain(|_, v| *v > 0);
assert!(!map.contains_key("temp"));

Interoperability with raw hashbrown types

The crate re-exports RawHashMap and RawHashSet (the bare hashbrown types) and provides From conversions in both directions so you can cross the boundary without a direct hashbrown dependency in your own Cargo.toml.

use axhash_map::{AxHashMap, RawHashMap, AxBuildHasher};

// Wrap a raw hashbrown map
let raw: RawHashMap<&str, u32, AxBuildHasher> =
    RawHashMap::with_hasher(AxBuildHasher::new());
let wrapped: AxHashMap<&str, u32> = raw.into();

// Unwrap back to hashbrown
let raw: RawHashMap<&str, u32, AxBuildHasher> = wrapped.into_inner();

When to use a custom seed

The default AxBuildHasher::new() uses an internal constant as seed — the output is deterministic across runs. This is fine for most workloads.

If your map accepts keys from untrusted external input (e.g. HTTP request parameters) and you want to defend against hash-flooding attacks, supply a random seed derived from OS entropy:

use axhash_map::{AxHashMap, AxBuildHasher};

// In real code, generate this from `rand`, `getrandom`, or similar.
let seed: u64 = /* os_random_u64() */ 0x1234_5678_9abc_def0;

let mut map: AxHashMap<String, String> =
    AxHashMap::with_hasher(AxBuildHasher::with_seed(seed));

Feature flags

This crate has no feature flags of its own. The AES hardware path in axhash is selected at runtime via CPUID — you always ship a single binary.


Dependency footprint

axhash-map
├── axhash-core   (AxBuildHasher — AES hash engine)
└── hashbrown     (SwissTable, no default features — ahash excluded)

axhash itself is not a dependency; axhash-core is the crate that owns AxBuildHasher. The wrapper crate adds no utility functions we don't use.


License

MIT — see LICENSE.