{"id":50452598,"url":"https://github.com/aaron-ang/opthash-rs","last_synced_at":"2026-06-01T01:00:39.093Z","repository":{"id":338935548,"uuid":"1159765278","full_name":"aaron-ang/opthash-rs","owner":"aaron-ang","description":"Optimal open-addressing hash maps (Elastic Hashing \u0026 Funnel Hashing) in Rust, with Python bindings.","archived":false,"fork":false,"pushed_at":"2026-05-24T14:55:04.000Z","size":1170,"stargazers_count":6,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T16:26:46.435Z","etag":null,"topics":["data-structures","elastic-hashing","funnel-hashing","hashmap","open-addressing","pyo3","python","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aaron-ang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-17T06:03:14.000Z","updated_at":"2026-05-24T14:55:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/aaron-ang/opthash-rs","commit_stats":null,"previous_names":["aaron-ang/opthash","aaron-ang/opthash-rs"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/aaron-ang/opthash-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-ang%2Fopthash-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-ang%2Fopthash-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-ang%2Fopthash-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-ang%2Fopthash-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aaron-ang","download_url":"https://codeload.github.com/aaron-ang/opthash-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aaron-ang%2Fopthash-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33457339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-24T19:21:36.376Z","status":"online","status_checked_at":"2026-05-25T02:00:05.812Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-structures","elastic-hashing","funnel-hashing","hashmap","open-addressing","pyo3","python","rust"],"created_at":"2026-06-01T01:00:22.795Z","updated_at":"2026-06-01T01:00:39.066Z","avatar_url":"https://github.com/aaron-ang.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# opthash\n\n[![Crates.io](https://img.shields.io/crates/v/opthash?logo=rust\u0026label=crates.io)](https://crates.io/crates/opthash)\n[![PyPI](https://img.shields.io/pypi/v/opthash?logo=pypi\u0026logoColor=white\u0026label=pypi)](https://pypi.org/project/opthash/)\n[![MSRV](https://img.shields.io/crates/msrv/opthash?logo=rust)](https://crates.io/crates/opthash)\n[![Python](https://img.shields.io/pypi/pyversions/opthash?logo=python\u0026logoColor=white)](https://pypi.org/project/opthash/)\n[![CI](https://github.com/aaron-ang/opthash-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/aaron-ang/opthash-rs/actions/workflows/ci.yml)\n[![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/aaron-ang/opthash-rs?utm_source=badge)\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)\n\nRust implementations of **Elastic Hashing** and **Funnel Hashing** from _Optimal Bounds for Open Addressing Without Reordering_ (Farach-Colton, Krapivin, Kuszmaul, 2025) — see [References](#references) [^fkk2025].\n\nBoth are open-addressing hash maps that achieve optimal expected probe complexity without reordering elements after insertion.\n\n## Data Structures\n\nBoth maps share a common core: a single-`Arena` allocation per map indexed by per-level descriptors, 7-bit fingerprint control bytes, SIMD control-byte scans for occupancy + lookup, and tombstone accounting [^swisstable] [^cppcon2017] [^hashbrown]. Per-level salt re-randomization [^cw1979] decorrelates probe paths across levels. The default `BuildHasher` is [`foldhash`](https://crates.io/crates/foldhash) [^foldhash]. The two maps differ in how they probe within a level:\n\n- **`ElasticHashMap\u003cK, V\u003e`** — Levels with geometrically halving capacities, each probed by a SwissTable-style triangular sequence (`(idx + delta) \u0026 mask`); inserts follow a per-level probe budget.\n- **`FunnelHashMap\u003cK, V\u003e`** — Bucketed levels (paper §5): a key maps to one bucket per level; overflow spills to the next level, not other buckets. Plus a split special array: `primary` (odd-step group probe) and `fallback` (two-choice buckets).\n\nBoth maps mirror `std::collections::HashMap`'s API and support the same operations. Each map starts with zero allocation (`new()`) and grows dynamically on demand. The `reserve_fraction` headroom knob is exposed via dedicated constructors.\n\n## Usage\n\n### Rust\n\n```bash\ncargo add opthash\n```\n\n```rust\nuse opthash::{ElasticHashMap, FunnelHashMap};\n\nlet mut map = ElasticHashMap::new();\nmap.insert(\"key\", 42);\nassert_eq!(map.get(\"key\"), Some(\u002642));\n\nlet mut map = ElasticHashMap::with_capacity_and_reserve_fraction(1024, 0.10);\nmap.insert(\"key\", 42);\nassert_eq!(map.get(\"key\"), Some(\u002642));\n\nlet mut map = FunnelHashMap::with_capacity_and_reserve_fraction(1024, 0.10);\nmap.insert(\"key\", 42);\nassert_eq!(map.get(\"key\"), Some(\u002642));\n```\n\n### Python\n\n```bash\npip install opthash\n```\n\n```python\nfrom opthash import ElasticHashMap, FunnelHashMap\n\nm = ElasticHashMap()\nm[\"key\"] = 42\nassert m[\"key\"] == 42\nassert \"key\" in m and len(m) == 1\n\nm = ElasticHashMap.with_options(capacity=1024, reserve_fraction=0.10)\n\nm = FunnelHashMap.with_options(capacity=1024, reserve_fraction=0.10)\n```\n\n## Layout Sketch\n\n```text\nArena (one allocation per map)\n==============================\n\n  fp = fingerprint (7-bit control byte)\n  kv = key-value entry, __ = empty (CTRL_EMPTY = 0x00), xx = tombstone (CTRL_TOMBSTONE = 0x80)\n\n  All control bytes pack first, then alignment padding so the slot region\n  starts at `align_of::\u003cSlotEntry\u003cK, V\u003e\u003e()`, then all slots:\n\n  arena::ptr ► [fp fp xx __ ... ][fp xx fp __ ...][fp fp ...][  pad  ][kv kv kv ...][kv ... ]\n               └─── ctrl L0 ────┘└─── ctrl L1 ───┘└── ... ──┘         └─ slots L0 ─┘└─ ... ─┘\n               ▲ each descriptor caches `ctrl_ptr` + `data_ptr` into the arena.\n\n  Each descriptor stores cached `ctrl_ptr`, `data_ptr`, capacity, plus\n  per-shape metadata (salt, mask, etc). All slot/ctrl/SIMD ops live on\n  the `ArenaSlots` trait (`src/common/arena.rs`).\n\n\nElasticHashMap\n==============\n\n  levels: Box\u003c[Level]\u003e (descriptors only)\n\n    Level 0    ctrl_ptr, data_ptr, capacity (~half of total slots)\n    Level 1    geometrically halved\n    Level 2    ...\n\n    per-level  group_count, group_count_mask, salt, len, tombstones,\n               half_reserve_slot_threshold, budget_cap\n\n  arena:       Arena (the single allocation backing every level above)\n\n  map-wide     len, total_slots, max_insertions, reserve_fraction,\n               batch_plan, current_batch_index, batch_remaining,\n               max_populated_level, hash_builder, alloc\n\n\nFunnelHashMap\n=============\n\n  levels: Box\u003c[BucketLevel]\u003e (descriptors)\n\n    Level 0\n      ctrl region   fp xx fp __ ... fp fp xx __ ... fp ...\n      slot region   kv kv kv __ ... kv kv kv __ ... kv ...\n                    └── bucket 0 ──┘└── bucket 1 ──┘\n      (xx in ctrl marks a removed slot; the slot bytes may still hold\n       the stale kv physically, but are logically uninit — never read.)\n\n    Level 1    (same layout, smaller buckets)\n\n    per-level  bucket_count_mask, bucket_size_log2, salt, len, tombstones\n\n  special: SpecialArray\n\n    primary    group-probed (paper B)\n               group_count_mask, len, tombstones\n\n    fallback   two-choice bucketed (paper C)\n               bucket_count, bucket_size_log2, len, tombstones\n\n  arena:       Arena (covers every level + both special regions)\n\n  map-wide     len, total_slots, max_insertions, reserve_fraction,\n               primary_probe_limit, max_populated_level, hash_builder, alloc\n```\n\n## Benchmarks\n\nSee [benches/README.md](benches/README.md) for comparison charts.\n\n## References\n\n[^fkk2025]: Martín Farach-Colton, Andrew Krapivin, William Kuszmaul. _Optimal Bounds for Open Addressing Without Reordering_ (2025). arXiv: \u003chttps://arxiv.org/abs/2501.02305\u003e. Establishes the elastic and funnel hashing schemes implemented in [`src/elastic.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/elastic.rs) and [`src/funnel.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/funnel.rs); the funnel \"special array\" split into `primary` (group-probed, paper B) and `fallback` (two-choice, paper C) follows the paper's construction directly.\n\n[^cw1979]: J. Lawrence Carter, Mark N. Wegman. _Universal Classes of Hash Functions_ (STOC 1977 / JCSS 1979). DOI: \u003chttps://doi.org/10.1016/0022-0000(79)90044-8\u003e. Foundational hash-based probing model the FKK bounds rely on; the per-level `salt` re-randomization in `Level`/`BucketLevel` (see `level_salt` in [`src/common/math.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/common/math.rs)) follows the universal-hashing assumption.\n\n[^swisstable]: Abseil. _SwissTable design notes_. \u003chttps://abseil.io/about/design/swisstables\u003e. Source of the 7-bit fingerprint control-byte layout + SIMD group scans used by the shared `ArenaSlots` trait (see [`src/common/arena.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/common/arena.rs), [`src/common/control.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/common/control.rs), [`src/common/simd.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/common/simd.rs)) and the triangular `(idx + delta) \u0026 mask` probe sequence used in `Level::triangular_group_start`.\n\n[^cppcon2017]: Matt Kulukundis. _Designing a Fast, Efficient, Cache-friendly Hash Table, Step by Step_ (CppCon 2017). \u003chttps://www.youtube.com/watch?v=ncHmEUmJZf4\u003e. Talk introducing the SwissTable design referenced above.\n\n[^hashbrown]: `hashbrown` — Rust port of SwissTable. \u003chttps://github.com/rust-lang/hashbrown\u003e. Used as the absolute throughput ceiling in the Criterion benches (see [benches/README.md](benches/README.md)).\n\n[^foldhash]: `foldhash` crate. \u003chttps://crates.io/crates/foldhash\u003e. Default `BuildHasher` (`foldhash::fast::RandomState`) wired up in [`src/common/mod.rs`](https://github.com/aaron-ang/opthash-rs/blob/main/src/common/mod.rs).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaron-ang%2Fopthash-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faaron-ang%2Fopthash-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaron-ang%2Fopthash-rs/lists"}