Skip to content

Instantly share code, notes, and snippets.

@kripod
Created February 18, 2020 00:00
Show Gist options
  • Save kripod/aee3807dbd359410e8d80a7449341ef4 to your computer and use it in GitHub Desktop.
Save kripod/aee3807dbd359410e8d80a7449341ef4 to your computer and use it in GitHub Desktop.
xxHash fast digest algorithm
// ~~: Math.floor
// >>> 0: Clamp to Uint32
const PRIME32_1 = 2654435761; // 0b10011110001101110111100110110001
const PRIME32_2 = 2246822519; // 0b10000101111010111100101001110111
const PRIME32_3 = 3266489917; // 0b11000010101100101010111000111101
const PRIME32_4 = 668265263; // 0b00100111110101001110101100101111
const PRIME32_5 = 374761393; // 0b00010110010101100110011110110001
function xxh32(message: Uint8Array, seed: number = 0): number {
let acc: number;
let offset = 0;
if (message.length >= 16) {
// Step 1: Initialize internal accumulators
let accN = [
(seed + PRIME32_1 + PRIME32_2) >>> 0,
(seed + PRIME32_2) >>> 0,
seed >>> 0,
(seed - PRIME32_1) >>> 0
];
// Step 2: Process stripes
const stripeCount = ~~(message.length / 16);
for (let stripe = 0; stripe < stripeCount; ++stripe) {
for (let i = 0; i < 4; ++i) {
const lane = readInt32LE(message, offset);
accN[i] = (accN[i] + Math.imul(lane, PRIME32_2)) >>> 0;
accN[i] = rotl32(accN[i], 13);
accN[i] = Math.imul(accN[i], PRIME32_1) >>> 0;
offset += 4;
}
}
// Step 3: Accumulator convergence
acc =
rotl32(accN[0], 1) +
rotl32(accN[1], 7) +
rotl32(accN[2], 12) +
rotl32(accN[3], 18);
} else {
// Special case: Input is less than 16 bytes
acc = seed + PRIME32_5;
}
// Step 4: Add input length
acc = (acc + message.length) >>> 0;
// Step 5: Consume remaining input
let remainingLength = message.length % 16;
while (remainingLength >= 4) {
const lane = readInt32LE(message, offset);
acc = (acc + Math.imul(lane, PRIME32_3)) >>> 0;
acc = Math.imul(rotl32(acc, 17), PRIME32_4) >>> 0;
offset += 4;
remainingLength -= 4;
}
while (remainingLength >= 1) {
const lane = message[offset];
acc = (acc + Math.imul(lane, PRIME32_5)) >>> 0;
acc = Math.imul(rotl32(acc, 11), PRIME32_1) >>> 0;
offset += 1;
remainingLength -= 1;
}
// Step 6: Final mix (avalanche)
acc = (acc ^ (acc >>> 15)) >>> 0;
acc = Math.imul(acc, PRIME32_2) >>> 0;
acc = (acc ^ (acc >>> 13)) >>> 0;
acc = Math.imul(acc, PRIME32_3) >>> 0;
acc = (acc ^ (acc >>> 16)) >>> 0;
return acc;
}
function rotl32(x: number, n: number): number {
return (x << n) | (x >>> (-n & 31));
}
function readInt32LE(source: Uint8Array, offset: number): number {
return (
source[offset] +
(source[offset + 1] << 8) +
(source[offset + 2] << 16) +
(source[offset + 3] << 24)
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment