123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- 'use strict';
- // 65536 is the size of a wasm memory page
- // 64 is the maximum chunk size for every possible wasm hash implementation
- // 4 is the maximum number of bytes per char for string encoding (max is utf-8)
- // ~3 makes sure that it's always a block of 4 chars, so avoid partially encoded bytes for base64
- const MAX_SHORT_STRING = Math.floor((65536 - 64) / 4) & ~3;
- class WasmHash {
- /**
- * @param {WebAssembly.Instance} instance wasm instance
- * @param {WebAssembly.Instance[]} instancesPool pool of instances
- * @param {number} chunkSize size of data chunks passed to wasm
- * @param {number} digestSize size of digest returned by wasm
- */
- constructor(instance, instancesPool, chunkSize, digestSize) {
- const exports = /** @type {any} */ (instance.exports);
- exports.init();
- this.exports = exports;
- this.mem = Buffer.from(exports.memory.buffer, 0, 65536);
- this.buffered = 0;
- this.instancesPool = instancesPool;
- this.chunkSize = chunkSize;
- this.digestSize = digestSize;
- }
- reset() {
- this.buffered = 0;
- this.exports.init();
- }
- /**
- * @param {Buffer | string} data data
- * @param {BufferEncoding=} encoding encoding
- * @returns {this} itself
- */
- update(data, encoding) {
- if (typeof data === 'string') {
- while (data.length > MAX_SHORT_STRING) {
- this._updateWithShortString(data.slice(0, MAX_SHORT_STRING), encoding);
- data = data.slice(MAX_SHORT_STRING);
- }
- this._updateWithShortString(data, encoding);
- return this;
- }
- this._updateWithBuffer(data);
- return this;
- }
- /**
- * @param {string} data data
- * @param {BufferEncoding=} encoding encoding
- * @returns {void}
- */
- _updateWithShortString(data, encoding) {
- const { exports, buffered, mem, chunkSize } = this;
- let endPos;
- if (data.length < 70) {
- if (!encoding || encoding === 'utf-8' || encoding === 'utf8') {
- endPos = buffered;
- for (let i = 0; i < data.length; i++) {
- const cc = data.charCodeAt(i);
- if (cc < 0x80) {
- mem[endPos++] = cc;
- } else if (cc < 0x800) {
- mem[endPos] = (cc >> 6) | 0xc0;
- mem[endPos + 1] = (cc & 0x3f) | 0x80;
- endPos += 2;
- } else {
- // bail-out for weird chars
- endPos += mem.write(data.slice(i), endPos, encoding);
- break;
- }
- }
- } else if (encoding === 'latin1') {
- endPos = buffered;
- for (let i = 0; i < data.length; i++) {
- const cc = data.charCodeAt(i);
- mem[endPos++] = cc;
- }
- } else {
- endPos = buffered + mem.write(data, buffered, encoding);
- }
- } else {
- endPos = buffered + mem.write(data, buffered, encoding);
- }
- if (endPos < chunkSize) {
- this.buffered = endPos;
- } else {
- const l = endPos & ~(this.chunkSize - 1);
- exports.update(l);
- const newBuffered = endPos - l;
- this.buffered = newBuffered;
- if (newBuffered > 0) {
- mem.copyWithin(0, l, endPos);
- }
- }
- }
- /**
- * @param {Buffer} data data
- * @returns {void}
- */
- _updateWithBuffer(data) {
- const { exports, buffered, mem } = this;
- const length = data.length;
- if (buffered + length < this.chunkSize) {
- data.copy(mem, buffered, 0, length);
- this.buffered += length;
- } else {
- const l = (buffered + length) & ~(this.chunkSize - 1);
- if (l > 65536) {
- let i = 65536 - buffered;
- data.copy(mem, buffered, 0, i);
- exports.update(65536);
- const stop = l - buffered - 65536;
- while (i < stop) {
- data.copy(mem, 0, i, i + 65536);
- exports.update(65536);
- i += 65536;
- }
- data.copy(mem, 0, i, l - buffered);
- exports.update(l - buffered - i);
- } else {
- data.copy(mem, buffered, 0, l - buffered);
- exports.update(l);
- }
- const newBuffered = length + buffered - l;
- this.buffered = newBuffered;
- if (newBuffered > 0) {
- data.copy(mem, 0, length - newBuffered, length);
- }
- }
- }
- digest(type) {
- const { exports, buffered, mem, digestSize } = this;
- exports.final(buffered);
- this.instancesPool.push(this);
- const hex = mem.toString('latin1', 0, digestSize);
- if (type === 'hex') {
- return hex;
- }
- if (type === 'binary' || !type) {
- return Buffer.from(hex, 'hex');
- }
- return Buffer.from(hex, 'hex').toString(type);
- }
- }
- const create = (wasmModule, instancesPool, chunkSize, digestSize) => {
- if (instancesPool.length > 0) {
- const old = instancesPool.pop();
- old.reset();
- return old;
- } else {
- return new WasmHash(
- new WebAssembly.Instance(wasmModule),
- instancesPool,
- chunkSize,
- digestSize
- );
- }
- };
- module.exports = create;
- module.exports.MAX_SHORT_STRING = MAX_SHORT_STRING;
|