hashcrew 0.1.2

Fast, portable non-cryptographic hashes for Rust
Documentation
# 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:

| 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:

```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.