feat(hash): decorrelate the synthesized GetHash128 fallback lanes - #214
Merged
Conversation
The two-seed fallback was only as strong as the algorithm's seed handling: seed-affine hashes collide both lanes together and seed-ignoring ones produced h1 == h2. The second pass now skips the first up-to-8 bytes and injects them (with kSeedFlip) into its seed, so both lanes cover every input byte yet hash different data -- the lanes decorrelate even for algorithms with weak or ignored seed handling, at unchanged cost. Alternatives considered: a plain +8 shift leaves lane 2 blind to the first 8 bytes (structural collision class for any algorithm); a half/half split is 2x faster but each output bit would depend on only half the input.
Fab-Cat
approved these changes
Jul 3, 2026
helly25
enabled auto-merge (squash)
July 3, 2026 21:17
helly25
added a commit
that referenced
this pull request
Jul 3, 2026
# Conflicts: # CHANGELOG.md # README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Improves
Hasher<Algo>'s synthesizedGetHash128for 64-bit-only algorithms, per review discussion.Problem: the two-seed fallback (
h2 = GetHash64(data, seed ^ kSeedFlip)) is only as strong as the algorithm's seed handling — for seed-affine hashes (fnv1a-style) an h1-collision forces an h2-collision, and for seed-ignoring ones (simple) it producedh1 == h2outright.Fix (hybrid of the discussed options): the second pass skips the first
min(8, len)bytes and injects them (withkSeedFlip) into its seed:h1 = GetHash64(data, seed) h2 = GetHash64(data.substr(skip), seed ^ kSeedFlip ^ LoadTail(data, skip))Alternatives rejected: plain
+8shift (lane 2 structurally blind to the first 8 bytes — a guaranteed collision class for any algorithm); half/half split (2× faster but each output bit depends on only half the input). Doc comment now states honestly that a synthesized 128-bit value does not reach true 128-bit collision resistance.Tests: fallback formula pinned (incl. the
< 8bytes edge where lane 2 hashes the empty remainder with all bytes in the seed); new test provingsimple(seed-ignoring) now yieldsh1 != h2and that changes past byte 8 move both lanes.