Skip to content

Switch default hasher to foldhash::quality#126

Merged
arthurprs merged 3 commits into
evolve-lifecycle-with-statefrom
switch-foldhash
Jun 25, 2026
Merged

Switch default hasher to foldhash::quality#126
arthurprs merged 3 commits into
evolve-lifecycle-with-statefrom
switch-foldhash

Conversation

@arthurprs

@arthurprs arthurprs commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the default hasher an implementation detail and switches it to foldhash::quality.

Stacked on #124 (base evolve-lifecycle-with-state) — review/merge #124 first.

1. Abstract the default hasher behind an opaque newtype

DefaultHashBuilder was a public type alias (= ahash::RandomState), which leaked the concrete hasher — naming it or calling ahash-specific methods made the choice part of the public API. It's now an opaque newtype wrapping a private, cfg-selected inner hasher, so the algorithm can change with no public-API churn. The ahash feature is likewise renamed to custom-hasher and wired via dep:foldhash, so the crate name no longer appears in the public feature list either.

2. Switch the underlying hasher: ahash → foldhash (quality)

With the type/feature opaque, the inner hasher is just type DefaultHasherImpl = …. This moves it from ahash::RandomState to foldhash::quality::RandomState. The quality variant (over fast) is deliberate: the cache reuses one hash for both shard selection and hashbrown's bucket/tag bits, slicing it into independent bit-windows that need good avalanche (see the compute_shard_index comment). RandomState keeps per-instance random seeding.

3. Doc note on compute_shard_index

Documents why the shard-bit rotation is tied to usize::BITS rather than u64::BITS: on 32-bit targets a usize-width hasher (e.g. FxHash) leaves the top 32 bits zero, so the low word is where the entropy lives — u64::BITS / 2 there would send every key to shard 0 (hashbrown guards against the same thing in h1/Tag::full).

Breaking

  • features = ["ahash"]["custom-hasher"].
  • Don't depend on the concrete hasher behind DefaultHashBuilder.
  • foldhash is fast and non-cryptographic and (unlike ahash) makes no HashDoS-resistance guarantee; disable custom-hasher to fall back to the std SipHash hasher for adversarial-key workloads.

Verification

cargo build / cargo test (56 + doctests) / cargo clippy / cargo doc -D warnings all clean; cargo check --no-default-features (std fallback) compiles.

BREAKING (0.7.0). `DefaultHashBuilder` was a public type alias for
`ahash::RandomState`, leaking the concrete hasher; the `ahash` feature
leaked it too. Both are now implementation-agnostic:

- `DefaultHashBuilder` is an opaque newtype wrapping a private, cfg-selected
  inner hasher. The algorithm can change with no public-API churn.
- The `ahash` feature is renamed to `custom-hasher` and pulls the hasher
  crate via `dep:ahash`, so the crate name no longer appears as a feature.

Migration: replace feature `ahash` with `custom-hasher`; don't name the
concrete hasher behind `DefaultHashBuilder`.
Exercises the DefaultHashBuilder abstraction: swapping the inner hasher
touches only the private `DefaultHasherImpl` alias and the optional
dependency — the public `DefaultHashBuilder` type and the `custom-hasher`
feature are unchanged.

foldhash's `quality` variant gives stronger distribution than its `fast`
variant while staying far faster than the std SipHash fallback.
@arthurprs arthurprs force-pushed the evolve-lifecycle-with-state branch from 2c74856 to dbfcea7 Compare June 24, 2026 23:02
@arthurprs arthurprs changed the title Switch default hasher to foldhash (quality) Switch default hasher to foldhash::quality Jun 25, 2026
@arthurprs arthurprs marked this pull request as ready for review June 25, 2026 19:12
@arthurprs arthurprs merged commit eb5e8aa into evolve-lifecycle-with-state Jun 25, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant