Created
February 18, 2020 00:00
-
-
Save kripod/aee3807dbd359410e8d80a7449341ef4 to your computer and use it in GitHub Desktop.
xxHash fast digest algorithm
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
// ~~: 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