|
| 1 | +/* eslint-disable no-console */ |
| 2 | + |
| 3 | +// TODO: Use TypeScript built-in type |
| 4 | +declare const WebAssembly: any; |
| 5 | + |
| 6 | +export const WASM_DEBUG = !!(process && process.env.WASM_DEBUG === "true"); |
| 7 | + |
1 | 8 | let wasmModule: any; |
2 | 9 | try { |
3 | | - wasmModule = require("../build/wasm/optimized.wasm.js").wasmModule; |
4 | | -} catch { |
| 10 | + if (WASM_DEBUG) { |
| 11 | + wasmModule = require("../build/wasm/untouched.wasm.js").wasmModule; |
| 12 | + } else { |
| 13 | + wasmModule = require("../build/wasm/optimized.wasm.js").wasmModule; |
| 14 | + } |
| 15 | +} catch (e) { |
| 16 | + if (WASM_DEBUG) { |
| 17 | + console.error(e); |
| 18 | + } |
5 | 19 | // WebAssembly is not supported. |
6 | 20 | } |
7 | 21 |
|
8 | | -declare var WebAssembly: any; |
9 | | -const WASM_MEMORY_PAGE_SIZE = 0x10000; // 64KiB |
| 22 | +function abort(filename: number, line: number, column: number): void { |
| 23 | + throw new Error(`abort called at ${filename}:${line}:${column}`); |
| 24 | +} |
10 | 25 |
|
11 | | -const defaultWasmInstance = wasmModule && new WebAssembly.Instance(wasmModule); |
| 26 | +const defaultWasmInstance = |
| 27 | + wasmModule && |
| 28 | + new WebAssembly.Instance(wasmModule, { |
| 29 | + env: { |
| 30 | + abort, |
| 31 | + }, |
| 32 | + }); |
12 | 33 |
|
13 | 34 | export const WASM_AVAILABLE = !!wasmModule && process.env.NO_WASM !== "true"; |
14 | 35 |
|
15 | | -function copyArrayBuffer(dest: ArrayBuffer, src: Uint8Array) { |
16 | | - const destView = new Uint8Array(dest); |
| 36 | +type pointer = number; |
| 37 | + |
| 38 | +function setMemory(wasm: any, destPtr: pointer, src: Uint8Array, size: number) { |
| 39 | + const destView = new Uint8Array(wasm.exports.memory.buffer, destPtr, size); |
17 | 40 | destView.set(src); |
18 | 41 | } |
19 | 42 |
|
20 | 43 | export function utf8DecodeWasm( |
21 | 44 | bytes: Uint8Array, |
22 | 45 | offset: number, |
23 | 46 | byteLength: number, |
24 | | - wasmInstance = defaultWasmInstance, |
| 47 | + wasm = defaultWasmInstance, |
25 | 48 | ): string { |
26 | | - if (!wasmInstance) { |
27 | | - throw new Error("No WebAssembly available"); |
28 | | - } |
| 49 | + const inputPtr: pointer = wasm.exports.malloc(byteLength); |
| 50 | + // in worst case, the UTF-16 array uses the same as byteLength * 2 |
| 51 | + const outputPtr: pointer = wasm.exports.malloc(byteLength * 2); |
| 52 | + try { |
| 53 | + setMemory(wasm, inputPtr, bytes.subarray(offset, offset + byteLength), byteLength); |
29 | 54 |
|
30 | | - const currentMemorySize: number = wasmInstance.exports.memory.buffer.byteLength; |
31 | | - const requiredMemorySize = bytes.length * 3; // input(utf8) + output(utf16) |
32 | | - if (currentMemorySize < requiredMemorySize) { |
33 | | - const page = Math.ceil((requiredMemorySize - currentMemorySize) / WASM_MEMORY_PAGE_SIZE); |
34 | | - wasmInstance.exports.memory.grow(page); |
35 | | - } |
| 55 | + const outputArraySize = wasm.exports.utf8DecodeToUint16Array(outputPtr, inputPtr, byteLength); |
| 56 | + const codepoints = new Uint16Array(wasm.exports.memory.buffer, outputPtr, outputArraySize); |
36 | 57 |
|
37 | | - copyArrayBuffer(wasmInstance.exports.memory.buffer, bytes.subarray(offset, offset + byteLength)); |
38 | | - // console.log(instanceMemory.subarray(0, 10)); |
39 | | - |
40 | | - const outputStart = Math.ceil(byteLength / Uint16Array.BYTES_PER_ELEMENT) * Uint16Array.BYTES_PER_ELEMENT; |
41 | | - const outputEnd = wasmInstance.exports.utf8ToUtf16(byteLength, outputStart); |
42 | | - const codepoints = new Uint16Array( |
43 | | - wasmInstance.exports.memory.buffer, |
44 | | - outputStart, |
45 | | - (outputEnd - outputStart) / Uint16Array.BYTES_PER_ELEMENT, |
46 | | - ); |
47 | | - // console.log([byteLength, outputStart, outputEnd]); |
48 | | - // console.log(instanceMemory.subarray(0, 10)); |
49 | | - // console.log(utf16array); |
50 | | - return String.fromCharCode.apply(String, codepoints as any); |
| 58 | + // FIXME: split codepoints if it is too long (the maximum size depends on the JS engine, though). |
| 59 | + return String.fromCharCode.apply(String, codepoints as any); |
| 60 | + } finally { |
| 61 | + wasm.exports.free(inputPtr); |
| 62 | + wasm.exports.free(outputPtr); |
| 63 | + } |
51 | 64 | } |
0 commit comments