Skip to content

[std]: Use XXHash32 instead FNV-1a #1580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 7, 2021
131 changes: 88 additions & 43 deletions std/assembly/util/hash.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-ignore: decorator
@inline
export function HASH<T>(key: T): u32 {
if (isString<T>()) {
return hashStr(changetype<string>(key));
Expand All @@ -10,63 +8,110 @@ export function HASH<T>(key: T): u32 {
if (sizeof<T>() == 4) return hash32(reinterpret<u32>(f32(key)));
if (sizeof<T>() == 8) return hash64(reinterpret<u64>(f64(key)));
} else {
if (sizeof<T>() == 1) return hash8 (u32(key));
if (sizeof<T>() == 2) return hash16(u32(key));
if (sizeof<T>() == 4) return hash32(u32(key));
if (sizeof<T>() <= 4) return hash32(u32(key), sizeof<T>());
if (sizeof<T>() == 8) return hash64(u64(key));
}
return unreachable();
}

// FNV-1a 32-bit as a starting point, see: http://isthe.com/chongo/tech/comp/fnv/
// XXHash 32-bit as a starting point, see: https://cyan4973.github.io/xxHash

// primes
// @ts-ignore: decorator
@inline const FNV_OFFSET: u32 = 2166136261;

@inline const XXH32_P1: u32 = 2654435761;
// @ts-ignore: decorator
@inline const FNV_PRIME: u32 = 16777619;

function hash8(key: u32): u32 {
return (FNV_OFFSET ^ key) * FNV_PRIME;
}
@inline const XXH32_P2: u32 = 2246822519;
// @ts-ignore: decorator
@inline const XXH32_P3: u32 = 3266489917;
// @ts-ignore: decorator
@inline const XXH32_P4: u32 = 668265263;
// @ts-ignore: decorator
@inline const XXH32_P5: u32 = 374761393;
// @ts-ignore: decorator
@inline const XXH32_SEED: u32 = 0;

function hash16(key: u32): u32 {
var v = FNV_OFFSET;
v = (v ^ ( key & 0xff)) * FNV_PRIME;
v = (v ^ ( key >> 8 )) * FNV_PRIME;
return v;
// @ts-ignore: decorator
@inline
function hash32(key: u32, len: u32 = 4): u32 {
var h: u32 = XXH32_SEED + XXH32_P5 + len;
h += key * XXH32_P3;
h = rotl(h, 17) * XXH32_P4;
h ^= h >> 15;
h *= XXH32_P2;
h ^= h >> 13;
h *= XXH32_P3;
h ^= h >> 16;
return h;
}

function hash32(key: u32): u32 {
var v = FNV_OFFSET;
v = (v ^ ( key & 0xff)) * FNV_PRIME;
v = (v ^ ((key >> 8) & 0xff)) * FNV_PRIME;
v = (v ^ ((key >> 16) & 0xff)) * FNV_PRIME;
v = (v ^ ( key >> 24 )) * FNV_PRIME;
return v;
// @ts-ignore: decorator
@inline
function hash64(key: u64): u32 {
var h: u32 = XXH32_SEED + XXH32_P5 + 8;
h += <u32>key * XXH32_P3;
h = rotl(h, 17) * XXH32_P4;
h += <u32>(key >> 32) * XXH32_P3;
h = rotl(h, 17) * XXH32_P4;
h ^= h >> 15;
h *= XXH32_P2;
h ^= h >> 13;
h *= XXH32_P3;
h ^= h >> 16;
return h;
}

function hash64(key: u64): u32 {
var l = <u32> key;
var h = <u32>(key >>> 32);
var v = FNV_OFFSET;
v = (v ^ ( l & 0xff)) * FNV_PRIME;
v = (v ^ ((l >> 8) & 0xff)) * FNV_PRIME;
v = (v ^ ((l >> 16) & 0xff)) * FNV_PRIME;
v = (v ^ ( l >> 24 )) * FNV_PRIME;
v = (v ^ ( h & 0xff)) * FNV_PRIME;
v = (v ^ ((h >> 8) & 0xff)) * FNV_PRIME;
v = (v ^ ((h >> 16) & 0xff)) * FNV_PRIME;
v = (v ^ ( h >> 24 )) * FNV_PRIME;
return v;
// @ts-ignore: decorator
@inline
function mix(h: u32, key: u32): u32 {
return rotl(h + key * XXH32_P2, 13) * XXH32_P1;
}

// @ts-ignore: decorator
@inline
function hashStr(key: string): u32 {
var v = FNV_OFFSET;
if (key !== null) {
for (let i: usize = 0, k: usize = key.length << 1; i < k; ++i) {
v = (v ^ <u32>load<u8>(changetype<usize>(key) + i)) * FNV_PRIME;
if (key === null) return XXH32_SEED;

var h: u32 = key.length << 1;
var len: usize = h;
var pos = changetype<usize>(key);

if (len >= 16) {
let s1 = XXH32_SEED + XXH32_P1 + XXH32_P2;
let s2 = XXH32_SEED + XXH32_P2;
let s3 = XXH32_SEED;
let s4 = XXH32_SEED - XXH32_P1;

let end = len + pos - 16;
while (pos <= end) {
s1 = mix(s1, load<u32>(pos ));
s2 = mix(s2, load<u32>(pos, 4));
s3 = mix(s3, load<u32>(pos, 8));
s4 = mix(s4, load<u32>(pos, 12));
pos += 16;
}
h += rotl(s1, 1) + rotl(s2, 7) + rotl(s3, 12) + rotl(s4, 18);
} else {
h += XXH32_SEED + XXH32_P5;
}

var end = changetype<usize>(key) + len - 4;
while (pos <= end) {
h += load<u32>(pos) * XXH32_P3;
h = rotl(h, 17) * XXH32_P4;
pos += 4;
}
return v;

end = changetype<usize>(key) + len;
while (pos < end) {
h += <u32>load<u8>(pos) * XXH32_P5;
h = rotl(h, 11) * XXH32_P1;
pos++;
}

h ^= h >> 15;
h *= XXH32_P2;
h ^= h >> 13;
h *= XXH32_P3;
h ^= h >> 16;
return h;
}
Loading