# hashcrew
[![Crates.io][crates-badge]][crates-url]
[![Documentation][docs-badge]][docs-url]
[![MSRV 1.85][msrv-badge]](https://www.whatrustisit.com)
[![Apache 2.0 licensed][license-badge]][license-url]
[![Build Status][actions-badge]][actions-url]
[crates-badge]: https://img.shields.io/crates/v/hashcrew.svg
[crates-url]: https://crates.io/crates/hashcrew
[docs-badge]: https://docs.rs/hashcrew/badge.svg
[docs-url]: https://docs.rs/hashcrew
[msrv-badge]: https://img.shields.io/badge/MSRV-1.85-green?logo=rust
[license-badge]: https://img.shields.io/crates/l/hashcrew
[license-url]: https://www.apache.org/licenses/LICENSE-2.0
[actions-badge]: https://github.com/fast/hashcrew/actions/workflows/ci.yml/badge.svg
[actions-url]: https://github.com/fast/hashcrew/actions/workflows/ci.yml
## 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
```shell
cargo add hashcrew
```
Disable the default `std` feature for bare-metal and other `no_std` targets:
```toml
[dependencies]
hashcrew = { version = "0.1", default-features = false }
```
Import the algorithm family when the complete input is already in memory:
```rust
use hashcrew::{cityhash, fnv, murmur, xxhash};
let data = b"hashcrew";
let city = cityhash::cityhash64(data);
let xxh3 = xxhash::xxh3_64(data);
let murmur = murmur::murmur3_x64_128(data, 42);
let fnv = fnv::fnv1a_64(data);
assert_ne!(city, 0);
assert_ne!(xxh3, 0);
assert_ne!(murmur, 0);
assert_ne!(fnv, 0);
```
Use a state type when data arrives incrementally:
```rust
use hashcrew::murmur::Murmur3X64_128;
use hashcrew::murmur::murmur3_x64_128;
let mut hash = Murmur3X64_128::with_seed(42);
hash.update(b"hash");
hash.update(b"crew");
assert_eq!(hash.digest(), murmur3_x64_128(b"hashcrew", 42));
```
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:
```rust
use hashcrew::xxhash::Xxh3_64;
use hashcrew::xxhash::xxh3_64_with_secret;
let secret = [0xa5; 192];
let expected = xxh3_64_with_secret(b"hashcrew", &secret).unwrap();
let mut hash = Xxh3_64::with_secret(secret).unwrap();
hash.update(b"hashcrew");
assert_eq!(hash.digest(), expected);
```
All public APIs are grouped under the [`cityhash`](https://docs.rs/hashcrew/*/hashcrew/cityhash/), [`xxhash`](https://docs.rs/hashcrew/*/hashcrew/xxhash/), [`murmur`](https://docs.rs/hashcrew/*/hashcrew/murmur/), and [`fnv`](https://docs.rs/hashcrew/*/hashcrew/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:
| 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.
| 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:
```rust
use std::io::{self, Cursor};
use hashcrew::xxhash::Xxh3_64;
use hashcrew::xxhash::xxh3_64;
let mut source = Cursor::new(b"hashcrew");
let mut hash = Xxh3_64::new();
io::copy(&mut source, &mut hash).unwrap();
assert_eq!(hash.digest(), xxh3_64(b"hashcrew"));
```
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:
```rust
use std::collections::HashMap;
use hashcrew::xxhash::Xxh3_64Builder;
let mut counts = HashMap::with_hasher(Xxh3_64Builder::with_seed(7));
counts.insert("hashcrew", 1);
assert_eq!(counts["hashcrew"], 1);
```
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()`](https://docs.rs/hashcrew/*/hashcrew/xxhash/kernel/fn.selected_backend.html) reports the selected XXH3 backend.
## Examples and benchmarks
Runnable examples live in the [`examples`](examples) workspace crate. The [`benchmarks`](benchmarks) crate contains one-shot and streaming comparisons with independent implementations; see its [benchmark guide](benchmarks/README.md) for filters, input sizes, and the complete case matrix.
Repository workflows use the active Rust toolchain. `cargo x lint` selects nightly for Clippy and rustfmt; its rustdoc check uses the active toolchain. Use `cargo x --help` to list the workflows, or run tests and benchmarks with:
```shell
cargo x test
cargo x bench
```
See the [release guide](RELEASE.md) for checks on stable and the MSRV.
## 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 Rust version policy
This crate's minimum supported `rustc` version is `1.85.0`.
The current policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if `crate 1.0` requires Rust 1.85.0, then `crate 1.0.z` for all values of `z` will also require Rust 1.85.0 or newer. However, `crate 1.y` for `y > 0` may require a newer minimum version of Rust.
## License and acknowledgements
This project is licensed under [Apache License, Version 2.0](LICENSE). The license file also records incorporated third-party code and its copyright notices and terms.