-
Notifications
You must be signed in to change notification settings - Fork 3
/
xxh64.ts
116 lines (97 loc) · 3.44 KB
/
xxh64.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
if (!(globalThis as any).Buffer) {
(globalThis as any).Buffer = require('buffer/').Buffer;
}
const n = (n: number | string) => BigInt(n)
const PRIME64_1 = n('11400714785074694791');
const PRIME64_2 = n('14029467366897019727');
const PRIME64_3 = n('1609587929392839161');
const PRIME64_4 = n('9650029242287828579');
const PRIME64_5 = n('2870177450012600261');
const mask64 = (n(1) << n(64)) - n(1);
// Basically (byte*)buf + offset
function getView(buf: Buffer, offset: number = 0): Buffer {
return Buffer.from(buf.buffer, buf.byteOffset + offset, buf.length - offset)
}
function Rotl64(a: bigint, b: bigint) {
return (a << b) | (a >> (n(64) - b)) & mask64
}
function round(acc: bigint, lane: bigint) {
acc = (acc + (lane * PRIME64_2)) & mask64;
acc = Rotl64(acc, n(31)) & mask64;
return (acc * PRIME64_1) & mask64;
}
function XH64_mergeAccumulator(acc: bigint, accN: bigint) {
acc = acc ^ round(n(0), accN);
acc = (acc * PRIME64_1) & mask64
return (acc + PRIME64_4) & mask64;
}
function XH64_convergeAccumulator(accs: BigUint64Array): bigint {
let acc = Rotl64(accs[0], n(1)) + Rotl64(accs[1], n(7)) + Rotl64(accs[2], n(12)) + Rotl64(accs[3], n(18));
acc = XH64_mergeAccumulator(acc, accs[0]);
acc = XH64_mergeAccumulator(acc, accs[1]);
acc = XH64_mergeAccumulator(acc, accs[2]);
acc = XH64_mergeAccumulator(acc, accs[3]);
return acc
}
function XH64_accumulateRemainder(data: Buffer, acc: bigint): bigint {
let offset = 0
while (data.byteLength - offset >= 8) {
let lane = data.readBigUInt64LE(offset);
acc = acc ^ round(n(0), lane);
acc = Rotl64(acc, n(27)) * PRIME64_1;
acc = (acc + PRIME64_4) & mask64;
offset += 8;
}
if (data.byteLength - offset >= 4) {
let lane = BigInt(data.readUInt32LE(offset));
acc = (acc ^ (lane * PRIME64_1)) & mask64;
acc = (Rotl64(acc, n(23)) * PRIME64_2) & mask64;
acc = (acc + PRIME64_3) & mask64;
offset += 4;
}
while (data.byteLength - offset >= 1) {
let lane = BigInt(data.readUInt8(offset));
acc = (acc ^ (lane * PRIME64_5)) & mask64;
acc = (Rotl64(acc, n(11)) * PRIME64_1) & mask64;
offset += 1;
}
return acc
}
function XH64_accumulate(data: Buffer, accs: BigUint64Array) {
const fullStripes = Math.floor(data.byteLength / 32)
for (let i = 0; i < fullStripes; i++) {
for (let j = 0; j < 4; j++) {
let lane = data.readBigUInt64LE(i * 32 + j * 8)
accs[j] = round(accs[j], lane)
}
}
let acc = XH64_convergeAccumulator(accs)
acc += BigInt(data.byteLength)
if (fullStripes != data.byteLength / 32) {
acc = XH64_accumulateRemainder(getView(data, fullStripes * 32), acc)
}
return XH64_mix(acc)
}
function XH64_mix(acc: bigint) {
acc = acc ^ (acc >> n(33));
acc = (acc * PRIME64_2) & mask64;
acc = acc ^ (acc >> n(29));
acc = (acc * PRIME64_3) & mask64;
acc = acc ^ (acc >> n(32));
return acc
}
function XXH64_small(data: Buffer, seed: bigint) {
let acc = (seed + PRIME64_5) & mask64;
acc = XH64_accumulateRemainder(data, acc)
return XH64_mix(acc)
}
export function XXH64(data: Buffer, seed: bigint = n(0)) {
if (data.byteLength < 32) return XXH64_small(data, seed)
const acc = new BigUint64Array([
seed + PRIME64_1 + PRIME64_2,
seed + PRIME64_2,
seed,
seed - PRIME64_1
])
return XH64_accumulate(data, acc)
}