source: frontend/node_modules/webpack/lib/util/hash/wasm-hash.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.7 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const Hash = require("../Hash");
9
10// 65536 is the size of a wasm memory page
11// 64 is the maximum chunk size for every possible wasm hash implementation
12// 4 is the maximum number of bytes per char for string encoding (max is utf-8)
13// ~3 makes sure that it's always a block of 4 chars, so avoid partially encoded bytes for base64
14const MAX_SHORT_STRING = Math.floor((65536 - 64) / 4) & ~3;
15
16/**
17 * Represents the wasm hash runtime component.
18 * @typedef {object} WasmExports
19 * @property {WebAssembly.Memory} memory
20 * @property {() => void} init
21 * @property {(length: number) => void} update
22 * @property {(length: number) => void} final
23 */
24
25class WasmHash extends Hash {
26 /**
27 * Creates an instance of WasmHash.
28 * @param {WebAssembly.Instance} instance wasm instance
29 * @param {WebAssembly.Instance[]} instancesPool pool of instances
30 * @param {number} chunkSize size of data chunks passed to wasm
31 * @param {number} digestSize size of digest returned by wasm
32 */
33 constructor(instance, instancesPool, chunkSize, digestSize) {
34 super();
35
36 const exports = /** @type {WasmExports} */ (instance.exports);
37 exports.init();
38 /** @type {WasmExports} */
39 this.exports = exports;
40 /** @type {Buffer} */
41 this.mem = Buffer.from(exports.memory.buffer, 0, 65536);
42 /** @type {number} */
43 this.buffered = 0;
44 /** @type {WebAssembly.Instance[]} */
45 this.instancesPool = instancesPool;
46 /** @type {number} */
47 this.chunkSize = chunkSize;
48 /** @type {number} */
49 this.digestSize = digestSize;
50 }
51
52 reset() {
53 this.buffered = 0;
54 this.exports.init();
55 }
56
57 /**
58 * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
59 * @overload
60 * @param {string | Buffer} data data
61 * @returns {Hash} updated hash
62 */
63 /**
64 * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
65 * @overload
66 * @param {string} data data
67 * @param {string=} inputEncoding data encoding
68 * @returns {this} updated hash
69 */
70 /**
71 * Update hash {@link https://nodejs.org/api/crypto.html#crypto_hash_update_data_inputencoding}
72 * @param {string | Buffer} data data
73 * @param {string=} inputEncoding data encoding
74 * @returns {this} updated hash
75 */
76 update(data, inputEncoding) {
77 if (typeof data === "string") {
78 while (data.length > MAX_SHORT_STRING) {
79 this._updateWithShortString(
80 data.slice(0, MAX_SHORT_STRING),
81 /** @type {NodeJS.BufferEncoding} */
82 (inputEncoding)
83 );
84 data = data.slice(MAX_SHORT_STRING);
85 }
86 this._updateWithShortString(
87 data,
88 /** @type {NodeJS.BufferEncoding} */
89 (inputEncoding)
90 );
91 return this;
92 }
93 this._updateWithBuffer(data);
94 return this;
95 }
96
97 /**
98 * Update with short string.
99 * @param {string} data data
100 * @param {BufferEncoding=} encoding encoding
101 * @returns {void}
102 */
103 _updateWithShortString(data, encoding) {
104 const { exports, buffered, mem, chunkSize } = this;
105 /** @type {number} */
106 let endPos;
107 if (data.length < 70) {
108 // eslint-disable-next-line unicorn/text-encoding-identifier-case
109 if (!encoding || encoding === "utf-8" || encoding === "utf8") {
110 endPos = buffered;
111 for (let i = 0; i < data.length; i++) {
112 const cc = data.charCodeAt(i);
113 if (cc < 0x80) {
114 mem[endPos++] = cc;
115 } else if (cc < 0x800) {
116 mem[endPos] = (cc >> 6) | 0xc0;
117 mem[endPos + 1] = (cc & 0x3f) | 0x80;
118 endPos += 2;
119 } else {
120 // bail-out for weird chars
121 endPos += mem.write(data.slice(i), endPos, encoding);
122 break;
123 }
124 }
125 } else if (encoding === "latin1") {
126 endPos = buffered;
127 for (let i = 0; i < data.length; i++) {
128 const cc = data.charCodeAt(i);
129 mem[endPos++] = cc;
130 }
131 } else {
132 endPos = buffered + mem.write(data, buffered, encoding);
133 }
134 } else {
135 endPos = buffered + mem.write(data, buffered, encoding);
136 }
137 if (endPos < chunkSize) {
138 this.buffered = endPos;
139 } else {
140 const l = endPos & ~(this.chunkSize - 1);
141 exports.update(l);
142 const newBuffered = endPos - l;
143 this.buffered = newBuffered;
144 if (newBuffered > 0) mem.copyWithin(0, l, endPos);
145 }
146 }
147
148 /**
149 * Update with buffer.
150 * @param {Buffer} data data
151 * @returns {void}
152 */
153 _updateWithBuffer(data) {
154 const { exports, buffered, mem } = this;
155 const length = data.length;
156 if (buffered + length < this.chunkSize) {
157 data.copy(mem, buffered, 0, length);
158 this.buffered += length;
159 } else {
160 const l = (buffered + length) & ~(this.chunkSize - 1);
161 if (l > 65536) {
162 let i = 65536 - buffered;
163 data.copy(mem, buffered, 0, i);
164 exports.update(65536);
165 const stop = l - buffered - 65536;
166 while (i < stop) {
167 data.copy(mem, 0, i, i + 65536);
168 exports.update(65536);
169 i += 65536;
170 }
171 data.copy(mem, 0, i, l - buffered);
172 exports.update(l - buffered - i);
173 } else {
174 data.copy(mem, buffered, 0, l - buffered);
175 exports.update(l);
176 }
177 const newBuffered = length + buffered - l;
178 this.buffered = newBuffered;
179 if (newBuffered > 0) data.copy(mem, 0, length - newBuffered, length);
180 }
181 }
182
183 /**
184 * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
185 * @overload
186 * @returns {Buffer} digest
187 */
188 /**
189 * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
190 * @overload
191 * @param {string=} encoding encoding of the return value
192 * @returns {string} digest
193 */
194 /**
195 * Calculates the digest {@link https://nodejs.org/api/crypto.html#crypto_hash_digest_encoding}
196 * @param {string=} encoding encoding of the return value
197 * @returns {string | Buffer} digest
198 */
199 digest(encoding) {
200 const { exports, buffered, mem, digestSize } = this;
201 exports.final(buffered);
202 this.instancesPool.push(this);
203 const hex = mem.toString("latin1", 0, digestSize);
204 if (encoding === "hex") return hex;
205 if (encoding === "binary" || !encoding) return Buffer.from(hex, "hex");
206 return Buffer.from(hex, "hex").toString(
207 /** @type {NodeJS.BufferEncoding} */ (encoding)
208 );
209 }
210}
211
212/**
213 * Returns wasm hash.
214 * @param {WebAssembly.Module} wasmModule wasm module
215 * @param {WasmHash[]} instancesPool pool of instances
216 * @param {number} chunkSize size of data chunks passed to wasm
217 * @param {number} digestSize size of digest returned by wasm
218 * @returns {WasmHash} wasm hash
219 */
220const create = (wasmModule, instancesPool, chunkSize, digestSize) => {
221 if (instancesPool.length > 0) {
222 const old = /** @type {WasmHash} */ (instancesPool.pop());
223 old.reset();
224 return old;
225 }
226
227 return new WasmHash(
228 new WebAssembly.Instance(wasmModule),
229 instancesPool,
230 chunkSize,
231 digestSize
232 );
233};
234
235create.MAX_SHORT_STRING = MAX_SHORT_STRING;
236
237module.exports = create;
Note: See TracBrowser for help on using the repository browser.