Skip to content

feat(hash): decorrelate the synthesized GetHash128 fallback lanes - #214

Merged
helly25 merged 2 commits into
mainfrom
hash/128-fallback
Jul 3, 2026
Merged

feat(hash): decorrelate the synthesized GetHash128 fallback lanes#214
helly25 merged 2 commits into
mainfrom
hash/128-fallback

Conversation

@helly25

@helly25 helly25 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Improves Hasher<Algo>'s synthesized GetHash128 for 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 produced h1 == h2 outright.

Fix (hybrid of the discussed options): the second pass skips the first min(8, len) bytes and injects them (with kSeedFlip) into its seed:

h1 = GetHash64(data, seed)
h2 = GetHash64(data.substr(skip), seed ^ kSeedFlip ^ LoadTail(data, skip))
  • Both lanes cover every input byte (the skipped head reaches lane 2 via the seed).
  • The lanes hash different data, so they decorrelate even for seed-affine / seed-ignoring algorithms.
  • Same cost as before (two passes).

Alternatives rejected: plain +8 shift (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 < 8 bytes edge where lane 2 hashes the empty remainder with all bytes in the seed); new test proving simple (seed-ignoring) now yields h1 != h2 and that changes past byte 8 move both lanes.

helly25 added 2 commits July 3, 2026 21:27
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.
@helly25
helly25 enabled auto-merge (squash) July 3, 2026 21:17
@helly25
helly25 merged commit 9abfcf4 into main Jul 3, 2026
40 checks passed
@helly25
helly25 deleted the hash/128-fallback branch July 3, 2026 21:45
helly25 added a commit that referenced this pull request Jul 3, 2026
# Conflicts:
#	CHANGELOG.md
#	README.md
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.

2 participants