Skip to content

Commit 6905299

Browse files
authored
feat: Implement xxHash32 and use it in stdlib (#1580)
1 parent 7f4296d commit 6905299

10 files changed

+4976
-5292
lines changed

std/assembly/util/hash.ts

+88-43
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// @ts-ignore: decorator
2-
@inline
31
export function HASH<T>(key: T): u32 {
42
if (isString<T>()) {
53
return hashStr(changetype<string>(key));
@@ -10,63 +8,110 @@ export function HASH<T>(key: T): u32 {
108
if (sizeof<T>() == 4) return hash32(reinterpret<u32>(f32(key)));
119
if (sizeof<T>() == 8) return hash64(reinterpret<u64>(f64(key)));
1210
} else {
13-
if (sizeof<T>() == 1) return hash8 (u32(key));
14-
if (sizeof<T>() == 2) return hash16(u32(key));
15-
if (sizeof<T>() == 4) return hash32(u32(key));
11+
if (sizeof<T>() <= 4) return hash32(u32(key), sizeof<T>());
1612
if (sizeof<T>() == 8) return hash64(u64(key));
1713
}
1814
return unreachable();
1915
}
2016

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

19+
// primes
2320
// @ts-ignore: decorator
24-
@inline const FNV_OFFSET: u32 = 2166136261;
25-
21+
@inline const XXH32_P1: u32 = 2654435761;
2622
// @ts-ignore: decorator
27-
@inline const FNV_PRIME: u32 = 16777619;
28-
29-
function hash8(key: u32): u32 {
30-
return (FNV_OFFSET ^ key) * FNV_PRIME;
31-
}
23+
@inline const XXH32_P2: u32 = 2246822519;
24+
// @ts-ignore: decorator
25+
@inline const XXH32_P3: u32 = 3266489917;
26+
// @ts-ignore: decorator
27+
@inline const XXH32_P4: u32 = 668265263;
28+
// @ts-ignore: decorator
29+
@inline const XXH32_P5: u32 = 374761393;
30+
// @ts-ignore: decorator
31+
@inline const XXH32_SEED: u32 = 0;
3232

33-
function hash16(key: u32): u32 {
34-
var v = FNV_OFFSET;
35-
v = (v ^ ( key & 0xff)) * FNV_PRIME;
36-
v = (v ^ ( key >> 8 )) * FNV_PRIME;
37-
return v;
33+
// @ts-ignore: decorator
34+
@inline
35+
function hash32(key: u32, len: u32 = 4): u32 {
36+
var h: u32 = XXH32_SEED + XXH32_P5 + len;
37+
h += key * XXH32_P3;
38+
h = rotl(h, 17) * XXH32_P4;
39+
h ^= h >> 15;
40+
h *= XXH32_P2;
41+
h ^= h >> 13;
42+
h *= XXH32_P3;
43+
h ^= h >> 16;
44+
return h;
3845
}
3946

40-
function hash32(key: u32): u32 {
41-
var v = FNV_OFFSET;
42-
v = (v ^ ( key & 0xff)) * FNV_PRIME;
43-
v = (v ^ ((key >> 8) & 0xff)) * FNV_PRIME;
44-
v = (v ^ ((key >> 16) & 0xff)) * FNV_PRIME;
45-
v = (v ^ ( key >> 24 )) * FNV_PRIME;
46-
return v;
47+
// @ts-ignore: decorator
48+
@inline
49+
function hash64(key: u64): u32 {
50+
var h: u32 = XXH32_SEED + XXH32_P5 + 8;
51+
h += <u32>key * XXH32_P3;
52+
h = rotl(h, 17) * XXH32_P4;
53+
h += <u32>(key >> 32) * XXH32_P3;
54+
h = rotl(h, 17) * XXH32_P4;
55+
h ^= h >> 15;
56+
h *= XXH32_P2;
57+
h ^= h >> 13;
58+
h *= XXH32_P3;
59+
h ^= h >> 16;
60+
return h;
4761
}
4862

49-
function hash64(key: u64): u32 {
50-
var l = <u32> key;
51-
var h = <u32>(key >>> 32);
52-
var v = FNV_OFFSET;
53-
v = (v ^ ( l & 0xff)) * FNV_PRIME;
54-
v = (v ^ ((l >> 8) & 0xff)) * FNV_PRIME;
55-
v = (v ^ ((l >> 16) & 0xff)) * FNV_PRIME;
56-
v = (v ^ ( l >> 24 )) * FNV_PRIME;
57-
v = (v ^ ( h & 0xff)) * FNV_PRIME;
58-
v = (v ^ ((h >> 8) & 0xff)) * FNV_PRIME;
59-
v = (v ^ ((h >> 16) & 0xff)) * FNV_PRIME;
60-
v = (v ^ ( h >> 24 )) * FNV_PRIME;
61-
return v;
63+
// @ts-ignore: decorator
64+
@inline
65+
function mix(h: u32, key: u32): u32 {
66+
return rotl(h + key * XXH32_P2, 13) * XXH32_P1;
6267
}
6368

69+
// @ts-ignore: decorator
70+
@inline
6471
function hashStr(key: string): u32 {
65-
var v = FNV_OFFSET;
66-
if (key !== null) {
67-
for (let i: usize = 0, k: usize = key.length << 1; i < k; ++i) {
68-
v = (v ^ <u32>load<u8>(changetype<usize>(key) + i)) * FNV_PRIME;
72+
if (key === null) return XXH32_SEED;
73+
74+
var h: u32 = key.length << 1;
75+
var len: usize = h;
76+
var pos = changetype<usize>(key);
77+
78+
if (len >= 16) {
79+
let s1 = XXH32_SEED + XXH32_P1 + XXH32_P2;
80+
let s2 = XXH32_SEED + XXH32_P2;
81+
let s3 = XXH32_SEED;
82+
let s4 = XXH32_SEED - XXH32_P1;
83+
84+
let end = len + pos - 16;
85+
while (pos <= end) {
86+
s1 = mix(s1, load<u32>(pos ));
87+
s2 = mix(s2, load<u32>(pos, 4));
88+
s3 = mix(s3, load<u32>(pos, 8));
89+
s4 = mix(s4, load<u32>(pos, 12));
90+
pos += 16;
6991
}
92+
h += rotl(s1, 1) + rotl(s2, 7) + rotl(s3, 12) + rotl(s4, 18);
93+
} else {
94+
h += XXH32_SEED + XXH32_P5;
95+
}
96+
97+
var end = changetype<usize>(key) + len - 4;
98+
while (pos <= end) {
99+
h += load<u32>(pos) * XXH32_P3;
100+
h = rotl(h, 17) * XXH32_P4;
101+
pos += 4;
70102
}
71-
return v;
103+
104+
end = changetype<usize>(key) + len;
105+
while (pos < end) {
106+
h += <u32>load<u8>(pos) * XXH32_P5;
107+
h = rotl(h, 11) * XXH32_P1;
108+
pos++;
109+
}
110+
111+
h ^= h >> 15;
112+
h *= XXH32_P2;
113+
h ^= h >> 13;
114+
h *= XXH32_P3;
115+
h ^= h >> 16;
116+
return h;
72117
}

0 commit comments

Comments
 (0)