UNPKG

highwayhasher

Version:

HighwayHash implementation for both node and browsers

529 lines (462 loc) 16.2 kB
'use strict'; 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;