Skip to content
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 len: usize = key.length << 1;
var pos = changetype<usize>(key);
var h = <u32>len;

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