hashify
A Rust proc-macro crate that generates perfect hash maps, sets, and function dispatch tables at compile time, with zero runtime dependencies.
Repository Health
Technical Analysis
Hashify is a Rust procedural macro crate that builds perfect hashing maps, sets, and match-style function tables entirely at compile time, so the resulting binary carries no runtime hashing dependency or lookup overhead. It picks between two internal strategies depending on dataset size: a gperf-style byte-position/XOR discriminator search for small maps under 500 entries, and a PTHash minimal-perfect-hashing implementation for larger datasets that need a compact, collision-free static table.
The crate exists specifically to outperform the widely used phf crate, which relies on the CHD algorithm: hashify’s benchmarks show it running over 4x faster for tiny maps and about 40% faster for large maps. It ships ten macro variants — map, set, tiny_map, tiny_set, fnc_map, and case-insensitive counterparts of each — covering static maps, sets, and switch-like dispatch on string or byte-slice keys.
Because FNV-1a underlies its hashing, hashify is tuned for short keys (roughly 1-50 bytes), making it a natural fit for parsing fixed vocabularies like protocol keywords, header names, or command tokens rather than arbitrary long strings.
What You Get
- Ten macro entry points (
map,map_ignore_case,set,set_ignore_case,tiny_map,tiny_map_ignore_case,fnc_map,fnc_map_ignore_case,tiny_set,tiny_set_ignore_case) covering maps, sets, and function dispatch - Automatic algorithm selection: a gperf-style position/XOR discriminator search for small maps (<500 entries), PTHash minimal perfect hashing for larger ones
- Zero runtime dependencies — all hashing work happens during compilation and is baked into the emitted code
- Case-insensitive variants of every macro for ASCII-insensitive key matching
- Support for string, byte-slice, char, integer, and boolean literal keys
Common Use Cases
- Parsing fixed protocol keywords or command vocabularies (e.g. Sieve script tokens, HTTP header names) into enum values without a runtime HashMap
- Replacing
phf-based static lookups where the extra 4x-40% throughput matters on a hot parsing path - Building compile-time character-set or charset-alias tables (the crate’s own README example maps encoding names like
koi8_rto numeric IDs) - Implementing case-insensitive command or keyword matching without allocating a lowercase copy of the input
Under The Hood
Architecture The crate is a proc-macro library exposing ten macro entry points in src/lib.rs, which parse macro input into typed AST structures (MapInput, BigMapInput, FncMapInput, SetInput, KeyValue, Key) via syn’s Parse trait, convert literal key expressions into raw byte sequences through the ParsedKey enum (modeled on phf’s own key-parsing logic), and dispatch to one of two code-generation backends chosen purely by key count. src/tiny.rs’s build_tiny_map recursively partitions keys by length and searches for a single-byte discriminator position or an XOR of two byte positions that uniquely disambiguates every key, falling back to nested match tables when no simple position works. src/large.rs’s build_map implements PTHash minimal perfect hashing — bucket assignment, per-bucket pilot search, and free-slot compaction — to produce a static pilot table and lookup array embedded directly in the generated code, with a 32-bit variant in src/large_32.rs for cross-target-width builds. Because everything happens inside macro expansion, the crate has no runtime surface beyond the macros themselves, so any change to key-parsing or either codegen backend changes what gets compiled into every downstream crate directly, not a runtime dependency graph.
Tech Stack
Built on syn 2.0 (full feature set) for macro-input parsing, quote 1.0 and proc-macro2 1.0 for code generation, and indexmap 2.13 for order-preserving key grouping — a deliberately small dependency set for a proc-macro crate. Targets Rust edition 2024, is declared proc-macro = true with doctest = false, and exposes a force-32bit feature to select the 32-bit PTHash backend regardless of target pointer width. Dev-dependencies bring in criterion 0.5 for benchmarking and phf 0.11.3 purely as a comparison baseline in the bundled benchmark suite — neither ships to consumers.
Code Quality
The test suite (tests/test.rs) is extensive — thousands of lines exercising every macro variant against a large realistic vocabulary (Sieve mail-filtering script keywords), which stresses the discriminator search and PTHash fallback paths against real collision-prone data rather than toy examples. No unsafe code appears anywhere in the crate. No CI workflow files, clippy configuration, or rustfmt configuration were found in the repository, so lint/format enforcement (if any) happens outside the repo. Naming is consistent and the small module boundary (lib.rs / tiny.rs / large.rs / large_32.rs) keeps each algorithm’s logic isolated and easy to trace.
What Makes It Unique
Unlike general-purpose hashing crates, hashify’s entire value proposition is a measured performance delta against the incumbent phf crate: the README documents over 4x faster lookups for small maps and roughly 40% faster for large maps, attributed to combining a cheaper discriminator-search strategy for small key sets with a from-scratch PTHash implementation for large ones, rather than using CHD (phf’s algorithm) universally. The dual-strategy split by dataset size, rather than a one-size-fits-all algorithm, is the crate’s distinguishing engineering choice.