blob: b08c07508331fbd9aaba58ffa0f829bc25ffdb7d [file] [log] [blame]
/*!
* +----------------------------------------------------------------------------------+
* | murmurHash3.js v3.0.0 (http://github.com/karanlyons/murmurHash3.js) |
* | A TypeScript/JavaScript implementation of MurmurHash3's hashing algorithms. |
* |----------------------------------------------------------------------------------|
* | Copyright (c) 2012-2020 Karan Lyons. Freely distributable under the MIT license. |
* +----------------------------------------------------------------------------------+
*/
type Brand<Name, Type> = Type & {_type?: Name};
export type u32 = Brand<'u32', number>;
export type u64 = Brand<'u64', [u32, u32]>;
// 16bit chunks in big endian as u32s to allow carrying of overflows.
type u64spill = Brand<'u64spill', [u32, u32, u32, u32]>;
const hexLUT = Array.from({ length: 256 }, (_, i) => `00${i.toString(16)}`.slice(-2));
export function bufToHex(buf: Uint8Array = new Uint8Array(0)): string {
let str = "";
for (let i = 0; i < buf.byteLength; i++) {
str += hexLUT[buf[i]];
}
return str;
}
function add64(m: u64, n: u64): u64 {
const ms: u64spill = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff];
const ns: u64spill = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff];
const os: u64spill = [0x0, 0x0, 0x0, 0x0];
os[3] += ms[3] + ns[3];
os[2] += os[3] >>> 16;
os[3] &= 0xffff;
os[2] += ms[2] + ns[2];
os[1] += os[2] >>> 16;
os[2] &= 0xffff;
os[1] += ms[1] + ns[1];
os[0] += os[1] >>> 16;
os[1] &= 0xffff;
os[0] += ms[0] + ns[0];
os[0] &= 0xffff;
return [(os[0] << 16) | os[1], (os[2] << 16) | os[3]];
}
function mul64(m: u64, n: u64): u64 {
const ms: u64spill = [m[0] >>> 16, m[0] & 0xffff, m[1] >>> 16, m[1] & 0xffff];
const ns: u64spill = [n[0] >>> 16, n[0] & 0xffff, n[1] >>> 16, n[1] & 0xffff];
const os: u64spill = [0x0, 0x0, 0x0, 0x0];
os[3] += ms[3] * ns[3];
os[2] += os[3] >>> 16;
os[3] &= 0xffff;
os[2] += ms[2] * ns[3];
os[1] += os[2] >>> 16;
os[2] &= 0xffff;
os[2] += ms[3] * ns[2];
os[1] += os[2] >>> 16;
os[2] &= 0xffff;
os[1] += ms[1] * ns[3];
os[0] += os[1] >>> 16;
os[1] &= 0xffff;
os[1] += ms[2] * ns[2];
os[0] += os[1] >>> 16;
os[1] &= 0xffff;
os[1] += ms[3] * ns[1];
os[0] += os[1] >>> 16;
os[1] &= 0xffff;
os[0] += (ms[0] * ns[3]) + (ms[1] * ns[2]) + (ms[2] * ns[1]) + (ms[3] * ns[0]);
os[0] &= 0xffff;
return [(os[0] << 16) | os[1], (os[2] << 16) | os[3]];
}
function rol64(n: u64, r: number): u64 {
r %= 64;
// istanbul ignore if: here for completeness but never used.
if (r === 32) {
return [n[1], n[0]];
} else if (r < 32) {
return [
(n[0] << r) | (n[1] >>> (32 - r)),
(n[1] << r) | (n[0] >>> (32 - r)),
];
} else {
r -= 32;
return [
(n[1] << r) | (n[0] >>> (32 - r)),
(n[0] << r) | (n[1] >>> (32 - r)),
];
}
}
function shl64(n: u64, s: number): u64 {
s %= 64;
// istanbul ignore if: here for completeness but never used.
if (s === 0) {
return n;
} else if (s < 32) {
return [(n[0] << s) | (n[1] >>> (32 - s)), n[1] << s];
} else {
return [n[1] << (s - 32), 0x0];
}
}
function xor64(a: u64, b: u64): u64 {
return [a[0] ^ b[0], a[1] ^ b[1]];
}
export type x86hash32State = {
h1: u32,
len: number,
rem: Uint8Array,
};
function x64fmix64(h: u64): u64 {
// [0x0, h[0] >>> 1] is a 33 bit shr.
h = xor64(h, [0x0, h[0] >>> 1]);
h = mul64(h, [0xff51afd7, 0xed558ccd]);
h = xor64(h, [0x0, h[0] >>> 1]);
h = mul64(h, [0xc4ceb9fe, 0x1a85ec53]);
h = xor64(h, [0x0, h[0] >>> 1]);
return h;
}
const x64hash128c1: u64 = [0x87c37b91, 0x114253d5];
const x64hash128c2: u64 = [0x4cf5ad43, 0x2745937f];
function x64mix128(h1: u64, h2: u64, k1: u64, k2: u64): [u64, u64] {
k1 = mul64(k1, x64hash128c1);
k1 = rol64(k1, 31);
k1 = mul64(k1, x64hash128c2);
h1 = xor64(h1, k1);
h1 = rol64(h1, 27);
h1 = add64(h1, h2);
h1 = add64(mul64(h1, [0x0, 5]), [0x0, 0x52dce729]);
k2 = mul64(k2, x64hash128c2);
k2 = rol64(k2, 33);
k2 = mul64(k2, x64hash128c1);
h2 = xor64(h2, k2);
h2 = rol64(h2, 31);
h2 = add64(h2, h1);
h2 = add64(mul64(h2, [0x0, 5]), [0x0, 0x38495ab5]);
return [h1, h2];
}
export type x64hash128State = {
h1: u64,
h2: u64,
len: number,
rem: Uint8Array,
};
export function x64hash128(
buf: Uint8Array = new Uint8Array(0),
state: u32 = 0x0,
): DataView {
let h1: u64;
let h2: u64;
let i: number;
let len: number;
if (typeof state === 'number') {
h1 = [0x0, state];
h2 = [0x0, state];
i = 0;
len = 0;
} else {
({ h1, h2, len } = state as x64hash128State);
const rem = (state as x64hash128State).rem;
if (rem.byteLength === 0) {
i = 0;
} else if (rem.byteLength + buf.byteLength >= 16) {
len += 16;
i = 16 - rem.byteLength;
const blk = new Uint8Array(16);
const dtv = new DataView(blk.buffer);
blk.set(rem);
blk.set(buf.subarray(0, i), rem.byteLength);
[h1, h2] = x64mix128(
h1, h2,
[dtv.getUint32(4, true), dtv.getUint32(0, true)],
[dtv.getUint32(12, true), dtv.getUint32(8, true)],
);
} else {
const newBuf = new Uint8Array(buf.byteLength + rem.byteLength);
newBuf.set(rem);
newBuf.set(buf, rem.byteLength);
buf = newBuf;
i = 0;
}
}
const dtv = new DataView(buf.buffer, buf.byteOffset);
const remainder = (buf.byteLength - i) % 16;
const bytes = buf.byteLength - i - remainder;
len += bytes;
for (; i < bytes; i += 16) {
[h1, h2] = x64mix128(
h1, h2,
[dtv.getUint32(i + 4, true), dtv.getUint32(i, true)],
[dtv.getUint32(i + 12, true), dtv.getUint32(i + 8, true)],
);
}
{
len += remainder;
let k1: u64 = [0x0, 0x0];
let k2: u64 = [0x0, 0x0];
switch (remainder) {
case 15: k2 = xor64(k2, shl64([0x0, buf[i + 14]], 48));
case 14: k2 = xor64(k2, shl64([0x0, buf[i + 13]], 40));
case 13: k2 = xor64(k2, shl64([0x0, buf[i + 12]], 32));
case 12: k2 = xor64(k2, shl64([0x0, buf[i + 11]], 24));
case 11: k2 = xor64(k2, shl64([0x0, buf[i + 10]], 16));
case 10: k2 = xor64(k2, shl64([0x0, buf[i + 9]], 8));
case 9:
k2 = xor64(k2, [0x0, buf[i + 8]]);
k2 = mul64(k2, x64hash128c2);
k2 = rol64(k2, 33);
k2 = mul64(k2, x64hash128c1);
h2 = xor64(h2, k2);
case 8: k1 = xor64(k1, shl64([0x0, buf[i + 7]], 56));
case 7: k1 = xor64(k1, shl64([0x0, buf[i + 6]], 48));
case 6: k1 = xor64(k1, shl64([0x0, buf[i + 5]], 40));
case 5: k1 = xor64(k1, shl64([0x0, buf[i + 4]], 32));
case 4: k1 = xor64(k1, shl64([0x0, buf[i + 3]], 24));
case 3: k1 = xor64(k1, shl64([0x0, buf[i + 2]], 16));
case 2: k1 = xor64(k1, shl64([0x0, buf[i + 1]], 8));
case 1:
k1 = xor64(k1, [0x0, buf[i]]);
k1 = mul64(k1, x64hash128c1);
k1 = rol64(k1, 31);
k1 = mul64(k1, x64hash128c2);
h1 = xor64(h1, k1);
}
h1 = xor64(h1, [0x0, len & 0xffffffff]);
h2 = xor64(h2, [0x0, len & 0xffffffff]);
h1 = add64(h1, h2);
h2 = add64(h2, h1);
h1 = x64fmix64(h1);
h2 = x64fmix64(h2);
h1 = add64(h1, h2);
h2 = add64(h2, h1);
const hash = new DataView(new ArrayBuffer(16));
hash.setUint32(0, h1[0], false);
hash.setUint32(4, h1[1], false);
hash.setUint32(8, h2[0], false);
hash.setUint32(12, h2[1], false);
return hash;
}
}