highwayhasher
Version:
HighwayHash implementation for both node and browsers
529 lines (462 loc) • 16.2 kB
JavaScript
;
var os = require('os');
function validKey(key) {
if (key && key.length != 32) {
throw new Error("expected the key buffer to be 32 bytes long");
}
return key || new Uint8Array();
}
let wasm$2;
/**
* @param {number} key_data_ptr
* @param {number} key_len
* @param {number} idx
* @returns {number}
*/
function new_hasher$1(key_data_ptr, key_len, idx) {
const ret = wasm$2.new_hasher(key_data_ptr, key_len, idx);
return ret;
}
/**
* @param {number} data_ptr
* @param {number} data_len
* @param {number} idx
*/
function append$1(data_ptr, data_len, idx) {
wasm$2.append(data_ptr, data_len, idx);
}
/**
* @param {number} data_ptr
* @param {number} idx
*/
function finalize64$1(data_ptr, idx) {
wasm$2.finalize64(data_ptr, idx);
}
/**
* @param {number} data_ptr
* @param {number} idx
*/
function finalize128$1(data_ptr, idx) {
wasm$2.finalize128(data_ptr, idx);
}
/**
* @param {number} data_ptr
* @param {number} idx
*/
function finalize256$1(data_ptr, idx) {
wasm$2.finalize256(data_ptr, idx);
}
async function __wbg_load$1(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function __wbg_get_imports$1() {
const imports = {};
imports.wbg = {};
return imports;
}
function __wbg_finalize_init$1(instance, module) {
wasm$2 = instance.exports;
__wbg_init$1.__wbindgen_wasm_module = module;
return wasm$2;
}
async function __wbg_init$1(input) {
if (wasm$2 !== undefined) return wasm$2;
if (typeof input === 'undefined') {
input = new URL('highwayhasher_wasm_bg.wasm', "");
}
const imports = __wbg_get_imports$1();
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
const { instance, module } = await __wbg_load$1(await input, imports);
return __wbg_finalize_init$1(instance, module);
}
let wasm$1;
/**
* @param {number} key_data_ptr
* @param {number} key_len
* @param {number} idx
* @returns {number}
*/
function new_hasher(key_data_ptr, key_len, idx) {
const ret = wasm$1.new_hasher(key_data_ptr, key_len, idx);
return ret;
}
/**
* @param {number} data_ptr
* @param {number} data_len
* @param {number} idx
*/
function append(data_ptr, data_len, idx) {
wasm$1.append(data_ptr, data_len, idx);
}
/**
* @param {number} data_ptr
* @param {number} idx
*/
function finalize64(data_ptr, idx) {
wasm$1.finalize64(data_ptr, idx);
}
/**
* @param {number} data_ptr
* @param {number} idx
*/
function finalize128(data_ptr, idx) {
wasm$1.finalize128(data_ptr, idx);
}
/**
* @param {number} data_ptr
* @param {number} idx
*/
function finalize256(data_ptr, idx) {
wasm$1.finalize256(data_ptr, idx);
}
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
return imports;
}
function __wbg_finalize_init(instance, module) {
wasm$1 = instance.exports;
__wbg_init.__wbindgen_wasm_module = module;
return wasm$1;
}
async function __wbg_init(input) {
if (wasm$1 !== undefined) return wasm$1;
if (typeof input === 'undefined') {
input = new URL('highwayhasher_wasm_simd_bg.wasm', "");
}
const imports = __wbg_get_imports();
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
input = fetch(input);
}
const { instance, module } = await __wbg_load(await input, imports);
return __wbg_finalize_init(instance, module);
}
let wasmInit = undefined;
const setWasmInit = (arg) => {
wasmInit = arg;
};
let wasmSimdInit = undefined;
const setWasmSimdInit = (arg) => {
wasmSimdInit = arg;
};
const isWasmInput = (x) => x === undefined || (typeof x === "object" && "simd" in x);
/**
* A Highway hasher implemented in Web assembly.
*/
class WasmHighwayHash {
static async loadModule(options) {
var _a, _b, _c;
if (isWasmInput(options === null || options === void 0 ? void 0 : options.wasm)) {
const useSimd = (_a = options === null || options === void 0 ? void 0 : options.simd) !== null && _a !== void 0 ? _a : hasSimd();
if (!useSimd) {
return await loadWasm((_b = options === null || options === void 0 ? void 0 : options.wasm) === null || _b === void 0 ? void 0 : _b.sisd);
}
else {
return await loadWasmSimd((_c = options === null || options === void 0 ? void 0 : options.wasm) === null || _c === void 0 ? void 0 : _c.simd);
}
}
else {
return await loadWasm(options === null || options === void 0 ? void 0 : options.wasm);
}
}
static async load(key, options) {
const module = await WasmHighwayHash.loadModule(options);
return module.create(key);
}
static resetModule() {
sisdMemory = undefined;
simdMemory = undefined;
}
}
const PAGE_SIZE = 65536;
class Allocator {
constructor(memory, offset) {
this.memory = memory;
this.offset = offset;
this.slots = [];
this.resultBufferOffset = this.appendBufferOffset;
this.keyBufferOffset = this.resultBufferOffset;
}
newAllocation() {
for (let i = 0; i < this.slots.length; i++) {
if (this.slots[i] === false) {
this.slots[i] = true;
return i;
}
}
const result = this.slots.length;
this.slots.push(true);
return result;
}
returnSlot(slot) {
this.slots[slot] = false;
}
appendBufferOffset() {
return this.offset;
}
}
function wasmHighway(alloc, hasher) {
const WasmHash = class {
constructor(key) {
var _a;
key = validKey(key);
this.idx = alloc.newAllocation();
const offset = alloc.keyBufferOffset();
const keyDest = new Uint8Array(alloc.memory.buffer, offset);
keyDest.set(key !== null && key !== void 0 ? key : new Uint8Array());
const hasherSlot = hasher.new_hasher(offset, (_a = key === null || key === void 0 ? void 0 : key.byteLength) !== null && _a !== void 0 ? _a : 0, this.idx);
if (hasherSlot !== this.idx) {
throw new Error("unable to allocate hasher in slot");
}
}
append(data) {
while (data.length != 0) {
const toWrite = Math.min(data.length, PAGE_SIZE);
const source = data.subarray(0, toWrite);
const dataOffset = alloc.appendBufferOffset();
new Uint8Array(alloc.memory.buffer, dataOffset, toWrite).set(source);
hasher.append(dataOffset, toWrite, this.idx);
data = data.subarray(toWrite);
}
}
finalize64() {
const resultOffset = alloc.resultBufferOffset();
hasher.finalize64(resultOffset, this.idx);
const out = new Uint8Array(alloc.memory.buffer, resultOffset, 8);
const result = new Uint8Array(8);
result.set(out);
alloc.returnSlot(this.idx);
return result;
}
finalize128() {
const resultOffset = alloc.resultBufferOffset();
hasher.finalize128(resultOffset, this.idx);
const out = new Uint8Array(alloc.memory.buffer, resultOffset, 16);
const result = new Uint8Array(16);
result.set(out);
alloc.returnSlot(this.idx);
return result;
}
finalize256() {
const resultOffset = alloc.resultBufferOffset();
hasher.finalize256(resultOffset, this.idx);
const out = new Uint8Array(alloc.memory.buffer, resultOffset, 32);
const result = new Uint8Array(32);
result.set(out);
alloc.returnSlot(this.idx);
return result;
}
};
return {
create: (key) => new WasmHash(key),
hash64(key, data) {
const hasher = new WasmHash(key);
hasher.append(data);
return hasher.finalize64();
},
hash128(key, data) {
const hasher = new WasmHash(key);
hasher.append(data);
return hasher.finalize128();
},
hash256(key, data) {
const hasher = new WasmHash(key);
hasher.append(data);
return hasher.finalize256();
},
};
}
let sisdMemory;
let simdMemory;
const loadWasmSimd = async (module) => {
if (simdMemory === undefined) {
simdMemory = __wbg_init(module !== null && module !== void 0 ? module : wasmSimdInit === null || wasmSimdInit === void 0 ? void 0 : wasmSimdInit()).then((x) => {
const prevLength = x.memory.grow(1);
const alloc = new Allocator(x.memory, prevLength);
return wasmHighway(alloc, {
new_hasher: new_hasher,
append: append,
finalize64: finalize64,
finalize128: finalize128,
finalize256: finalize256,
});
});
}
return await simdMemory;
};
const loadWasm = async (module) => {
if (sisdMemory === undefined) {
sisdMemory = __wbg_init$1(module !== null && module !== void 0 ? module : wasmInit === null || wasmInit === void 0 ? void 0 : wasmInit()).then((x) => {
// grow by 1 page to hold key, results, and data hashing
const prevLength = x.memory.grow(1);
const alloc = new Allocator(x.memory, prevLength);
return wasmHighway(alloc, {
new_hasher: new_hasher$1,
append: append$1,
finalize64: finalize64$1,
finalize128: finalize128$1,
finalize256: finalize256$1,
});
});
}
return await sisdMemory;
};
// Extracted from the compiled file of:
// https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/40269813c83f7e9ff370afc92cde3cc0456c557e/src/detectors/simd/module.wat
//
// Changes:
// - Validation is cached so it needs to only run once
// - There's no need to mark as async
let simdEnabled;
const hasSimd = () => simdEnabled !== null && simdEnabled !== void 0 ? simdEnabled : (simdEnabled = WebAssembly.validate(new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10,
1, 8, 0, 65, 0, 253, 15, 253, 98, 11,
])));
const getTriple = () => {
const platform = os.platform();
const arch = os.arch();
if (platform === "linux" && arch === "x64") {
return "x86_64-unknown-linux-gnu";
}
else if (platform === "linux" && arch === "arm64") {
return "aarch64-unknown-linux-gnu";
}
else if (platform === "linux" && arch === "arm") {
return "armv7-unknown-linux-gnueabihf";
}
else if (platform === "darwin" && arch === "x64") {
return "x86_64-apple-darwin";
}
else if (platform === "darwin" && arch === "arm64") {
return "aarch64-apple-darwin";
}
else if (platform === "win32" && arch == "x64") {
return "x86_64-pc-windows-msvc";
}
else {
throw new Error(`unknown platform-arch: ${platform}-${arch}`);
}
};
const MODULE_NAME = "highwayhasher";
let nativeHasher;
function initializeNative(native) {
const NativeHash = class {
constructor(key) {
this.append = (data) => this.inner.append(data);
this.finalize64 = () => new Uint8Array(this.inner.finalize64().buffer);
this.finalize128 = () => new Uint8Array(this.inner.finalize128().buffer);
this.finalize256 = () => new Uint8Array(this.inner.finalize256().buffer);
this.inner = new native.HighwayHasher(validKey(key));
}
};
return {
create: (key) => new NativeHash(key),
hash64: (key, data) => new Uint8Array(native.hash64(validKey(key), data).buffer),
hash128: (key, data) => new Uint8Array(native.hash128(validKey(key), data).buffer),
hash256: (key, data) => new Uint8Array(native.hash256(validKey(key), data).buffer),
};
}
class NativeHighwayHash {
static async loadModule(_options) {
if (nativeHasher === undefined) {
const native = require(`./${MODULE_NAME}-${getTriple()}.node`);
nativeHasher = initializeNative(native);
}
return nativeHasher;
}
static async load(key, options) {
const mod = await NativeHighwayHash.loadModule(options);
return mod.create(key);
}
static resetModule() {
nativeHasher = undefined;
}
}
function _loadWasmModule (sync, filepath, src, imports) {
function _instantiateOrCompile(source, imports, stream) {
var instantiateFunc = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate;
var compileFunc = stream ? WebAssembly.compileStreaming : WebAssembly.compile;
if (imports) {
return instantiateFunc(source, imports)
} else {
return compileFunc(source)
}
}
var buf = null;
if (filepath) {
var fs = require("fs");
var path = require("path");
return new Promise((resolve, reject) => {
fs.readFile(path.resolve(__dirname, filepath), (error, buffer) => {
if (error != null) {
reject(error);
} else {
resolve(_instantiateOrCompile(buffer, imports, false));
}
});
});
}
buf = Buffer.from(src, 'base64');
if(sync) {
var mod = new WebAssembly.Module(buf);
return imports ? new WebAssembly.Instance(mod, imports) : mod
} else {
return _instantiateOrCompile(buf, imports, false)
}
}
function wasm(imports){return _loadWasmModule(0, '../highwayhasher_wasm_bg.wasm', null, imports)}
function wasmSimd(imports){return _loadWasmModule(0, '../highwayhasher_wasm_simd_bg.wasm', null, imports)}
// @ts-ignore
setWasmInit(() => wasm());
// @ts-ignore
setWasmSimdInit(() => wasmSimd());
exports.HighwayHash = NativeHighwayHash;
exports.WasmHighwayHash = WasmHighwayHash;
exports.hasWasmSimd = hasSimd;