opthash
Rust implementations of Elastic Hashing and Funnel Hashing from Optimal Bounds for Open Addressing Without Reordering (Farach-Colton, Krapivin, Kuszmaul, 2025).
Both are open-addressing hash maps that achieve optimal expected probe complexity without reordering elements after insertion.
Data Structures
ElasticHashMap<K, V>— Multi-level table with geometrically halving levels. Each level is aRawTableplus probe budgets, group steps, and tombstone accounting.FunnelHashMap<K, V>— Multi-level bucketed table with per-bucket metadata and a split special array:primary(group-probed) plusfallback(two-choice buckets).
Both support insert, get, get_mut, contains_key, remove, and clear. Maps start with zero allocation (new()) and grow dynamically on demand. Advanced tuning is available through ElasticOptions, FunnelOptions, and with_options(...).
Usage
use ;
let mut map = new;
map.insert;
assert_eq!;
let tuned_funnel = with_options;
assert_eq!;
let tuned = with_options;
assert_eq!;
Layout Sketch
RawTable (shared by both maps)
==============================
Single allocation per table, slots first:
data_ptr ─► [kv][kv][ ][kv][ ][kv]... [pad] [fp][fp][__][xx][__][fp]...
└─── slots (T-aligned) ───┘ └── controls (16-aligned) ──┘
▲ ctrl_offset
fp = fingerprint (7-bit control byte), kv = key-value entry
__ = empty slot, xx = tombstone
No per-group metadata — occupancy is derived from SIMD scans of the
control bytes (eq_mask_16 for fingerprints, free_mask_16 for free slots).
ElasticHashMap
==============
levels: Vec<Level>
Level 0 RawTable (largest, ~half of total capacity)
Level 1 RawTable (geometrically halved)
Level 2 ...
per-level len, tombstones, half_reserve_slot_threshold
limited_probe_budgets, group_steps, salt
table-wide len, capacity, max_insertions, reserve_fraction, probe_scale
batch_plan, current_batch_index, batch_remaining
max_populated_level, hash_builder
FunnelHashMap
=============
levels: Vec<BucketLevel>
Level 0
RawTable [kv kv __ __ | kv __ __ __]... [fp fp __ __ | fp __ __ __]...
└─ bucket 0 ─┘ └─ bucket 1 ─┘
bucket_meta BucketMeta { summary, live_mask, search_len, live, tombstones }
Level 1 (same layout, smaller buckets)
...
special: SpecialArray
primary (paper B)
RawTable group-probed like elastic
per-primary len, group_summaries, group_tombstones, group_steps
fallback (paper C)
RawTable two-choice bucketed
per-fallback len, bucket_size, bucket_count
bucket_summaries, bucket_live, bucket_tombstones
table-wide len, capacity, max_insertions, reserve_fraction
primary_probe_limit, max_populated_level, hash_builder
Benchmarks
All benchmarks on Apple M1 (aarch64, NEON SIMD) via Criterion. std::HashMap uses the same RandomState (SipHash) hasher as both custom maps.
Throughput
Latency
Running benchmarks
Criterion also generates an interactive HTML report at target/criterion/report/index.html.