hashcrew
Overview
Hashcrew is a zero-dependency Rust library for fast, deterministic, non-cryptographic hashing. It provides allocation-free one-shot APIs, incremental state where the algorithm supports it, stable cross-platform digests for identical raw byte streams, and hardware-accelerated XXH3 kernels.
Every implementation supports no_std. XXH3 inputs longer than 240 bytes use a dedicated kernel layer with scalar, little-endian AArch64 NEON, x86-64 SSE2, and x86-64 AVX2 backends; the other algorithms use compact portable Rust cores.
[!WARNING]
These algorithms are not cryptographically secure. Deterministic hashers are also unsuitable for hash tables exposed to attacker-controlled keys because they do not protect against deliberate hash flooding.
Getting started
cargo add hashcrew
Disable the default std feature for bare-metal and other no_std targets:
[]
= { = "0.1", = false }
Import the algorithm family when the complete input is already in memory:
use ;
let data = b"hashcrew";
let city = cityhash64;
let xxh3 = xxh3_64;
let murmur = murmur3_x64_128;
let fnv = fnv1a_64;
assert_ne!;
assert_ne!;
assert_ne!;
assert_ne!;
Use a state type when data arrives incrementally:
use Murmur3X64_128;
use murmur3_x64_128;
let mut hash = with_seed;
hash.update;
hash.update;
assert_eq!;
Custom XXH3 secrets can be borrowed or moved into the streaming state. Owning the storage is useful when a factory or component needs to return a self-contained hasher:
use Xxh3_64;
use xxh3_64_with_secret;
let secret = ;
let expected = xxh3_64_with_secret.unwrap;
let mut hash = with_secret.unwrap;
hash.update;
assert_eq!;
All public APIs are grouped under the cityhash, xxhash, murmur, and fnv modules. Each module keeps its one-shot functions, streaming states, builders, and configuration together.
API model
Hashcrew exposes the same algorithm at different integration boundaries. Pick the narrowest interface that matches where the bytes come from:
| Input or caller | Interface | What it does |
|---|---|---|
| One complete byte slice | A module-level function such as xxh3_64(input) |
Computes and returns the digest immediately without constructing a state. |
| Byte slices arriving incrementally | A state such as Xxh3_64: construct, call update, then call digest |
Retains bounded working state; digest does not consume it, and reset reuses its configuration. |
A file, socket, decoder, or another std::io source |
The same state through std::io::Write with the default std feature |
Treats every written byte as input; finish the producer, then call digest separately. |
A Rust hash collection or generic Hash caller |
A state through Hasher, usually constructed by its matching builder |
Accepts Rust's typed Hash encoding and returns a u64 from Hasher::finish. |
Hasher only supports a u64 result, so 128-bit states deliberately expose digest() -> u128 instead of truncating their output. CityHash has neither a state nor standard adapters because it cannot hash incrementally with bounded memory.
Algorithm and capability map
The table names the canonical module-level function for complete input. A trailing * means the family also provides explicitly named seeded, custom-secret, or custom-offset-basis forms.
| Variant | Complete input | Incremental state | Digest | Hasher / BuildHasher |
|---|---|---|---|---|
| CityHash32 | cityhash32 |
— | u32 |
— |
| CityHash64 | cityhash64* |
— | u64 |
— |
| CityHash128 | cityhash128* |
— | u128 |
— |
| XXH32 | xxh32 |
Xxh32 |
u32 |
Xxh32 / Xxh32Builder |
| XXH64 | xxh64 |
Xxh64 |
u64 |
Xxh64 / Xxh64Builder |
| XXH3-64 | xxh3_64* |
Xxh3_64 |
u64 |
Xxh3_64 / Xxh3_64Builder or secret builder |
| XXH3-128 | xxh3_128* |
Xxh3_128 |
u128 |
— |
| MurmurHash3 x86_32 | murmur3_x86_32 |
Murmur3X86_32 |
u32 |
Murmur3X86_32 / Murmur3X86_32Builder |
| MurmurHash3 x86_128 | murmur3_x86_128 |
Murmur3X86_128 |
u128 |
— |
| MurmurHash3 x64_128 | murmur3_x64_128 |
Murmur3X64_128 |
u128 |
— |
| FNV-1a 32 | fnv1a_32* |
Fnv1a32 |
u32 |
Fnv1a32 / Fnv1a32Builder |
| FNV-1a 64 | fnv1a_64* |
Fnv1a64 |
u64 |
Fnv1a64 / Fnv1a64Builder |
Hashcrew implements all three variants from the original MurmurHash3 family under their reference-qualified x86_32, x86_128, and x64_128 names. These architecture labels distinguish algorithms and do not restrict which target can run them. cityhash128_to_64 reduces an existing 128-bit CityHash value; it does not hash a new byte slice.
Choosing an algorithm
Use XXH3 for a new general-purpose checksum, cache key, or trusted-input hash table unless interoperability requires another family. Choose a 128-bit result when the application hashes enough distinct values for 64-bit collision probability to matter. XXH32, XXH64, CityHash, MurmurHash3, and FNV-1a are primarily useful for matching an existing format, protocol, or data set; their different outputs are not interchangeable.
Streaming input
Call update when the application already has byte slices, as in the getting-started example above. With the default std feature, every streaming state can also be used as the destination of std::io::copy or another producer that accepts std::io::Write.
The adapter treats every written byte as hash input; it accepts the complete buffer and has nothing to flush. It does not write the digest anywhere. Finish the producer first, then call digest on the state:
use ;
use Xxh3_64;
use xxh3_64;
let mut source = new;
let mut hash = new;
copy.unwrap;
assert_eq!;
This adapter is only needed for std interoperability. The direct update API is available in both std and no_std builds.
Hash tables
The 32-bit and 64-bit streaming states implement core::hash::Hasher, with matching BuildHasher types for trusted-input hash tables:
use HashMap;
use Xxh3_64Builder;
let mut counts = with_hasher;
counts.insert;
assert_eq!;
CityHash is intentionally one-shot. Its digest depends on the complete input length and tail, so a streaming facade would have to retain the entire message and would not provide bounded-memory incremental hashing.
XXH3 accepts custom secrets of at least 136 bytes and returns an error for shorter inputs. Its seed-and-secret APIs follow the reference contract: inputs up to 240 bytes use the seed, while longer inputs use the custom secret. Custom secrets and non-standard FNV offset bases alter deterministic output; neither makes these algorithms cryptographically secure.
Portability
Raw and streaming digests are stable across platforms for identical byte streams. Rust's Hash and BuildHasher adapters use native typed encodings, including platform endianness and usize width, and are not a portable serialization format.
Target-guaranteed CPU features are selected at compile time. Other std builds cache runtime feature detection; no_std builds use compile-time features only and otherwise fall back to the scalar kernel. hashcrew::xxhash::kernel::selected_backend() reports the selected XXH3 backend.
Examples and benchmarks
Runnable examples live in the examples workspace crate. The benchmarks crate contains one-shot and streaming comparisons with independent implementations; see its benchmark guide for filters, input sizes, and the complete case matrix.
Use the repository workflow commands to run them:
cargo x test
cargo x bench
Correctness
Integration tests compare CityHash, xxHash, and MurmurHash3 with independent implementations, and verify FNV-1a against RFC vectors plus an independent 64-bit implementation. The suite covers boundary lengths, multiple seeds, custom secrets, custom FNV offset bases, randomized inputs, streaming partitions, available hardware backends, and both std and no_std builds.
Minimum Supported Rust Version (MSRV)
Hashcrew's minimum supported rustc version is 1.85.0. The MSRV may be increased in a minor release.
License and acknowledgements
This project is licensed under Apache License, Version 2.0. See THIRD_PARTY_NOTICES.md for the specifications, implementations, and development-only comparison dependencies that informed Hashcrew.