diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd465c0191..753375421c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,6 @@ jobs: ./addons/xterm-addon-canvas/out-test/* \ ./addons/xterm-addon-fit/out/* \ ./addons/xterm-addon-fit/out-test/* \ - ./addons/xterm-addon-image/inwasm-builds/out/* \ ./addons/xterm-addon-image/out/* \ ./addons/xterm-addon-image/out-test/* \ ./addons/xterm-addon-ligatures/out/* \ diff --git a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json b/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json deleted file mode 100644 index b4a652c8ee..0000000000 --- a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json +++ /dev/null @@ -1 +0,0 @@ -{"def":{"name":"decode","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"\n typedef struct {\n unsigned int wp;\n unsigned int sp;\n unsigned int dp;\n unsigned int e_size;\n unsigned int b_size;\n unsigned int dummy[3];\n unsigned char data[0];\n } State;\n\n unsigned int *D0 = (unsigned int *) 1024;\n unsigned int *D1 = (unsigned int *) 2048;\n unsigned int *D2 = (unsigned int *) 3072;\n unsigned int *D3 = (unsigned int *) 4096;\n State *state = (State *) 5120;\n\n __attribute__((noinline)) int dec() {\n unsigned int nsp = (state->wp - 1) & ~3;\n unsigned char *src = state->data + state->sp;\n unsigned char *end = state->data + nsp;\n unsigned char *dst = state->data + state->dp;\n unsigned int accu;\n\n while (src < end) {\n if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1;\n *((unsigned int *) dst) = accu;\n dst += 3;\n src += 4;\n }\n state->sp = nsp;\n state->dp = dst - state->data;\n return 0;\n }\n\n int end() {\n int rem = state->wp - state->sp;\n if (rem > 4 && dec()) return 1;\n rem = state->wp - state->sp;\n if (rem < 2) return 1;\n\n unsigned char *src = state->data + state->sp;\n unsigned int accu = D0[src[0]] | D1[src[1]];\n int dp = 1;\n if (rem > 2 && src[2] != 61) {\n accu |= D2[src[2]];\n dp++;\n }\n if (rem == 4 && src[3] != 61) {\n accu |= D3[src[3]];\n dp++;\n }\n if (accu >> 24) return 1;\n *((unsigned int *) (state->data + state->dp)) = accu;\n state->dp += dp;\n return state->dp != state->b_size;\n }\n "},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"},"srcDef":"{\n name: 'decode',\n type: 0 /* OutputType.INSTANCE */,\n mode: 1 /* OutputMode.SYNC */,\n srctype: 'Clang-C',\n imports: {\n env: { memory: new WebAssembly.Memory({ initial: 1 }) }\n },\n exports: {\n dec: () => 0,\n end: () => 0\n },\n compile: {\n switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first']\n },\n code: `\n typedef struct {\n unsigned int wp;\n unsigned int sp;\n unsigned int dp;\n unsigned int e_size;\n unsigned int b_size;\n unsigned int dummy[3];\n unsigned char data[0];\n } State;\n\n unsigned int *D0 = (unsigned int *) ${256 /* P32.D0 */ * 4};\n unsigned int *D1 = (unsigned int *) ${512 /* P32.D1 */ * 4};\n unsigned int *D2 = (unsigned int *) ${768 /* P32.D2 */ * 4};\n unsigned int *D3 = (unsigned int *) ${1024 /* P32.D3 */ * 4};\n State *state = (State *) ${1280 /* P32.STATE */ * 4};\n\n __attribute__((noinline)) int dec() {\n unsigned int nsp = (state->wp - 1) & ~3;\n unsigned char *src = state->data + state->sp;\n unsigned char *end = state->data + nsp;\n unsigned char *dst = state->data + state->dp;\n unsigned int accu;\n\n while (src < end) {\n if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1;\n *((unsigned int *) dst) = accu;\n dst += 3;\n src += 4;\n }\n state->sp = nsp;\n state->dp = dst - state->data;\n return 0;\n }\n\n int end() {\n int rem = state->wp - state->sp;\n if (rem > 4 && dec()) return 1;\n rem = state->wp - state->sp;\n if (rem < 2) return 1;\n\n unsigned char *src = state->data + state->sp;\n unsigned int accu = D0[src[0]] | D1[src[1]];\n int dp = 1;\n if (rem > 2 && src[2] != 61) {\n accu |= D2[src[2]];\n dp++;\n }\n if (rem == 4 && src[3] != 61) {\n accu |= D3[src[3]];\n dp++;\n }\n if (accu >> 24) return 1;\n *((unsigned int *) (state->data + state->dp)) = accu;\n state->dp += dp;\n return state->dp != state->b_size;\n }\n `\n}","hash":""} \ No newline at end of file diff --git a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wasm b/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wasm deleted file mode 100644 index c1ac6e8ad7..0000000000 Binary files a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wasm and /dev/null differ diff --git a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat b/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat deleted file mode 100644 index d7832f4510..0000000000 --- a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat +++ /dev/null @@ -1,210 +0,0 @@ -(module - (type (;0;) (func (result i32))) - (import "env" "memory" (memory (;0;) 1)) - (func (;0;) (type 0) (result i32) - (local i32 i32 i32 i32 i32) - i32.const 5128 - i32.load - i32.const 5152 - i32.add - local.set 1 - i32.const 5124 - i32.load - local.tee 0 - i32.const 5120 - i32.load - i32.const 1 - i32.sub - i32.const -4 - i32.and - local.tee 2 - i32.lt_s - if ;; label = @1 - local.get 2 - i32.const 5152 - i32.add - local.set 3 - local.get 0 - i32.const 5152 - i32.add - local.set 0 - loop ;; label = @2 - local.get 0 - i32.load8_u offset=3 - i32.const 2 - i32.shl - i32.load offset=4096 - local.get 0 - i32.load8_u offset=2 - i32.const 2 - i32.shl - i32.load offset=3072 - local.get 0 - i32.load8_u offset=1 - i32.const 2 - i32.shl - i32.load offset=2048 - local.get 0 - i32.load8_u - i32.const 2 - i32.shl - i32.load offset=1024 - i32.or - i32.or - i32.or - local.tee 4 - i32.const 16777215 - i32.gt_u - if ;; label = @3 - i32.const 1 - return - end - local.get 1 - local.get 4 - i32.store - local.get 1 - i32.const 3 - i32.add - local.set 1 - local.get 0 - i32.const 4 - i32.add - local.tee 0 - local.get 3 - i32.lt_u - br_if 0 (;@2;) - end - end - i32.const 5124 - local.get 2 - i32.store - i32.const 5128 - local.get 1 - i32.const 5152 - i32.sub - i32.store - i32.const 0) - (func (;1;) (type 0) (result i32) - (local i32 i32 i32 i32 i32 i32) - block ;; label = @1 - i32.const 5120 - i32.load - local.tee 1 - i32.const 5124 - i32.load - local.tee 0 - i32.sub - i32.const 5 - i32.ge_s - if ;; label = @2 - i32.const 1 - local.set 3 - call 0 - br_if 1 (;@1;) - i32.const 5120 - i32.load - local.set 1 - i32.const 5124 - i32.load - local.set 0 - end - i32.const 1 - local.set 3 - local.get 1 - local.get 0 - i32.sub - local.tee 4 - i32.const 2 - i32.lt_s - br_if 0 (;@1;) - local.get 0 - i32.const 5153 - i32.add - i32.load8_u - i32.const 2 - i32.shl - i32.load offset=2048 - local.get 0 - i32.const 5152 - i32.add - i32.load8_u - i32.const 2 - i32.shl - i32.load offset=1024 - i32.or - local.set 1 - block ;; label = @2 - local.get 4 - i32.const 2 - i32.eq - if ;; label = @3 - i32.const 1 - local.set 2 - br 1 (;@2;) - end - i32.const 1 - local.set 2 - local.get 0 - i32.load8_u offset=5154 - local.tee 5 - i32.const 61 - i32.ne - if ;; label = @3 - i32.const 2 - local.set 2 - local.get 5 - i32.const 2 - i32.shl - i32.load offset=3072 - local.get 1 - i32.or - local.set 1 - end - local.get 4 - i32.const 4 - i32.ne - br_if 0 (;@2;) - local.get 0 - i32.load8_u offset=5155 - local.tee 0 - i32.const 61 - i32.eq - br_if 0 (;@2;) - local.get 2 - i32.const 1 - i32.add - local.set 2 - local.get 0 - i32.const 2 - i32.shl - i32.load offset=4096 - local.get 1 - i32.or - local.set 1 - end - local.get 1 - i32.const 16777215 - i32.gt_u - br_if 0 (;@1;) - i32.const 5128 - i32.load - i32.const 5152 - i32.add - local.get 1 - i32.store - i32.const 5128 - i32.const 5128 - i32.load - local.get 2 - i32.add - local.tee 0 - i32.store - local.get 0 - i32.const 5136 - i32.load - i32.ne - local.set 3 - end - local.get 3) - (export "dec" (func 0)) - (export "end" (func 1))) diff --git a/addons/xterm-addon-image/package.json b/addons/xterm-addon-image/package.json index 69b87b384c..8de858b5c8 100644 --- a/addons/xterm-addon-image/package.json +++ b/addons/xterm-addon-image/package.json @@ -17,8 +17,7 @@ "xterm.js" ], "scripts": { - "inwasm": "inwasm out/*.wasm.js", - "prepackage": "../../node_modules/.bin/tsc -p . && inwasm -f out/*.wasm.js", + "prepackage": "../../node_modules/.bin/tsc -p .", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package" }, @@ -26,7 +25,7 @@ "xterm": "^5.2.0" }, "devDependencies": { - "inwasm": "^0.0.13", - "sixel": "^0.16.0" + "sixel": "^0.16.0", + "xterm-wasm-parts": "^0.1.0" } } diff --git a/addons/xterm-addon-image/src/IIPHandler.ts b/addons/xterm-addon-image/src/IIPHandler.ts index a8a851e6dd..ae62100d16 100644 --- a/addons/xterm-addon-image/src/IIPHandler.ts +++ b/addons/xterm-addon-image/src/IIPHandler.ts @@ -5,7 +5,7 @@ import { IImageAddonOptions, IOscHandler, IResetHandler, ITerminalExt } from './Types'; import { ImageRenderer } from './ImageRenderer'; import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; -import { Base64Decoder } from './base64.wasm'; +import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser'; import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics'; diff --git a/addons/xterm-addon-image/src/base64.benchmark.ts b/addons/xterm-addon-image/src/base64.benchmark.ts deleted file mode 100644 index 6870295de8..0000000000 --- a/addons/xterm-addon-image/src/base64.benchmark.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { ThroughputRuntimeCase, perfContext } from 'xterm-benchmark'; -import { Base64Decoder } from './base64.wasm'; - -// eslint-disable-next-line -declare const Buffer: any; - -function toBytes(s: string): Uint8Array { - const bytes = new Uint8Array(s.length); - for (let i = 0; i < s.length; ++i) { - bytes[i] = s.charCodeAt(i) & 0xFF; - } - return bytes; -} - -const d256 = 'ABCD'.repeat(64); -const d4096 = 'ABCD'.repeat(64 * 16); -const d65536 = 'ABCD'.repeat(64 * 16 * 16); -const d1M = 'ABCD'.repeat(64 * 16 * 16 * 16); -const b256 = toBytes(d256); -const b4096 = toBytes(d4096); -const b65536 = toBytes(d65536); -const b1M = toBytes(d1M); -const dec = new Base64Decoder(4000000); - - -const RUNS = 100; - -perfContext('Base64', () => { - perfContext('Node - Buffer', () => { - new ThroughputRuntimeCase('decode - 256', () => { - Buffer.from(d256, 'base64'); - return { payloadSize: d256.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 4096', () => { - Buffer.from(d4096, 'base64'); - return { payloadSize: d4096.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 65536', () => { - Buffer.from(d65536, 'base64'); - return { payloadSize: d65536.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 1048576', () => { - Buffer.from(d1M, 'base64'); - return { payloadSize: d1M.length }; - }, { repeat: RUNS }).showAverageThroughput(); - }); - - perfContext('Base64Decoder', () => { - new ThroughputRuntimeCase('decode - 256', () => { - dec.init(192); - dec.put(b256, 0, b256.length); - dec.end(); - return { payloadSize: b256.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 4096', () => { - dec.init(3072); - dec.put(b4096, 0, b4096.length); - dec.end(); - return { payloadSize: b4096.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 65536', () => { - dec.init(49152); - dec.put(b65536, 0, b65536.length); - dec.end(); - return { payloadSize: b65536.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 1048576', () => { - dec.init(786432); - dec.put(b1M, 0, b1M.length); - dec.end(); - return { payloadSize: b1M.length }; - }, { repeat: RUNS }).showAverageThroughput(); - }); -}); diff --git a/addons/xterm-addon-image/src/base64.test.ts b/addons/xterm-addon-image/src/base64.test.ts deleted file mode 100644 index 2d4e6d76a4..0000000000 --- a/addons/xterm-addon-image/src/base64.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { assert } from 'chai'; -import { Base64Decoder } from './base64.wasm'; - -// eslint-disable-next-line -declare const Buffer: any; - - -// some helpers -function toBs(bytes: Uint8Array): string { - let bs = ''; - for (let i = 0; i < bytes.length; ++i) bs += String.fromCharCode(bytes[i]); - return bs; -} -function fromBs(bs: string): Uint8Array { - const r = new Uint8Array(bs.length); - for (let i = 0; i < r.length; ++i) r[i] = bs.charCodeAt(i); - return r; -} -function encNative(bytes: Uint8Array): string { - return typeof Buffer !== 'undefined' ? Buffer.from(bytes).toString('base64') : btoa(toBs(bytes)); -} -function rtrim(x: string, c: string): string { - let end = x.length - 1; - while (c.indexOf(x[end]) >= 0) end -= 1; - return x.slice(0, end + 1); -} -const MAP = new Uint8Array( - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .map(el => el.charCodeAt(0)) -); - - -describe('Base64Decoder', () => { - describe('decoding', () => { - it('single bytes', function() { - this.timeout(20000); - const dec = new Base64Decoder(0); - for (let i = 0; i < 256; ++i) { - dec.init(1); - const inp = new Uint8Array([i]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - for (let a = 0; a < 256; ++a) { - it(`1+2 bytes (${a})`, function() { - const dec = new Base64Decoder(0); - for (let b = 0; b < 256; ++b) { - dec.init(2); - const inp = new Uint8Array([a, b]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - } - for (let a = 0; a < 256; ++a) { - it(`2+3 bytes (${a})`, function() { - const dec = new Base64Decoder(0); - for (let b = 0; b < 256; ++b) { - dec.init(3); - const inp = new Uint8Array([0, a, b]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - } - for (let a = 0; a < 256; ++a) { - it(`3+4 bytes (${a})`, function() { - const dec = new Base64Decoder(0); - for (let b = 0; b < 256; ++b) { - dec.init(4); - const inp = new Uint8Array([0, 0, a, b]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - } - it('padding', () => { - const dec = new Base64Decoder(0); - const d = fromBs('Hello, here comes the mouse'); - const encData = []; - const encDataTrimmed = []; - for (let i = 1; i < d.length; ++i) { - encData.push(encNative(d.slice(0, i))); - encDataTrimmed.push(rtrim(encNative(d.slice(0, i)), '=')); - } - for (let i = 0; i < encData.length; ++i) { - // with padding - dec.init(i + 1); - let enc = fromBs(encData[i]); - assert.strictEqual(dec.put(enc, 0, enc.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, d.slice(0, i + 1)); - // w'o padding - dec.init(i + 1); - enc = fromBs(encDataTrimmed[i]); - assert.strictEqual(dec.put(enc, 0, enc.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, d.slice(0, i + 1)); - } - }); - it('exit on false byte', function() { - this.timeout(20000); - const dec = new Base64Decoder(0); - for (let pos = 0; pos < 8; ++pos) { - const inp = new Uint8Array([65, 65, 65, 65, 65, 65, 65, 65]); - for (let i = 0; i < 256; ++i) { - dec.release(); - dec.init(6); - inp[pos] = i; - dec.put(inp, 0, 8); - assert.strictEqual(dec.end(), MAP.includes(i) ? 0 : 1); - } - } - }); - }); - describe('memory', () => { - it('always release (keepSize 0)', () => { - const dec = new Base64Decoder(0); - dec.init(16); - dec.put(fromBs('A'.repeat(16)), 0, 16); - dec.end(); - assert.strictEqual(dec.data8.length, 12); - dec.release(); - assert.strictEqual(dec.data8.length, 0); - assert.isNull((dec as any)._mem); - }); - it('keep 1 page (keepSize 65536)', () => { - const dec = new Base64Decoder(65536); - dec.init(384); - dec.put(fromBs('A'.repeat(512)), 0, 512); - dec.end(); - assert.strictEqual(dec.data8.length, 384); - dec.release(); - assert.strictEqual(dec.data8.length, 0); - assert.isNotNull((dec as any)._mem); - // grow to 2 pages + free afterwards - dec.init(65536); - dec.put(fromBs('A'.repeat(65536)), 0, 65536); - dec.end(); - assert.strictEqual(dec.data8.length, 49152); - dec.release(); - assert.strictEqual(dec.data8.length, 0); - assert.isNull((dec as any)._mem); - }); - }); -}); diff --git a/addons/xterm-addon-image/src/base64.wasm.ts b/addons/xterm-addon-image/src/base64.wasm.ts deleted file mode 100644 index 02965c2ddb..0000000000 --- a/addons/xterm-addon-image/src/base64.wasm.ts +++ /dev/null @@ -1,372 +0,0 @@ -/** - * Copyright (c) 2023 The xterm.js authors. All rights reserved. - * @license MIT - */ -import { InWasm, IWasmInstance, OutputMode, OutputType } from 'inwasm'; - - -// memory addresses in uint32 -const enum P32 { - D0 = 256, - D1 = 512, - D2 = 768, - D3 = 1024, - STATE = 1280, - STATE_WP = 1280, - STATE_SP = 1281, - STATE_DP = 1282, - STATE_ESIZE = 1283, - STATE_BSIZE = 1284, - STATE_DATA = 1288 // 16 aligned -} - -/** - * wasm base64 decoder. - */ -const wasmDecode = InWasm({ - name: 'decode', - type: OutputType.INSTANCE, - mode: OutputMode.SYNC, - srctype: 'Clang-C', - imports: { - env: { memory: new WebAssembly.Memory({ initial: 1 }) } - }, - exports: { - dec: () => 0, - end: () => 0 - }, - compile: { - switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first'] - }, - code: ` - typedef struct { - unsigned int wp; - unsigned int sp; - unsigned int dp; - unsigned int e_size; - unsigned int b_size; - unsigned int dummy[3]; - unsigned char data[0]; - } State; - - unsigned int *D0 = (unsigned int *) ${P32.D0*4}; - unsigned int *D1 = (unsigned int *) ${P32.D1*4}; - unsigned int *D2 = (unsigned int *) ${P32.D2*4}; - unsigned int *D3 = (unsigned int *) ${P32.D3*4}; - State *state = (State *) ${P32.STATE*4}; - - __attribute__((noinline)) int dec() { - unsigned int nsp = (state->wp - 1) & ~3; - unsigned char *src = state->data + state->sp; - unsigned char *end = state->data + nsp; - unsigned char *dst = state->data + state->dp; - unsigned int accu; - - while (src < end) { - if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1; - *((unsigned int *) dst) = accu; - dst += 3; - src += 4; - } - state->sp = nsp; - state->dp = dst - state->data; - return 0; - } - - int end() { - int rem = state->wp - state->sp; - if (rem > 4 && dec()) return 1; - rem = state->wp - state->sp; - if (rem < 2) return 1; - - unsigned char *src = state->data + state->sp; - unsigned int accu = D0[src[0]] | D1[src[1]]; - int dp = 1; - if (rem > 2 && src[2] != 61) { - accu |= D2[src[2]]; - dp++; - } - if (rem == 4 && src[3] != 61) { - accu |= D3[src[3]]; - dp++; - } - if (accu >> 24) return 1; - *((unsigned int *) (state->data + state->dp)) = accu; - state->dp += dp; - return state->dp != state->b_size; - } - ` -}); - -// SIMD version - commented out for now due to missing Safari support -// const wasmDecode = InWasm({ -// name: 'decode', -// type: OutputType.INSTANCE, -// mode: OutputMode.SYNC, -// srctype: 'Clang-C', -// imports: { -// env: { memory: new WebAssembly.Memory({ initial: 1 }) } -// }, -// exports: { -// dec: () => 0, -// end: () => 0 -// }, -// compile: { -// switches: ['-msimd128', '-Wl,-z,stack-size=0', '-Wl,--stack-first'] -// }, -// code: ` -// #include -// typedef struct { -// unsigned int wp; -// unsigned int sp; -// unsigned int dp; -// unsigned int e_size; -// unsigned int b_size; -// unsigned int dummy[3]; -// unsigned char data[0]; -// } State; -// -// unsigned int *D0 = (unsigned int *) ${P32.D0*4}; -// unsigned int *D1 = (unsigned int *) ${P32.D1*4}; -// unsigned int *D2 = (unsigned int *) ${P32.D2*4}; -// unsigned int *D3 = (unsigned int *) ${P32.D3*4}; -// State *state = (State *) ${P32.STATE*4}; -// -// #define packed_byte(x) wasm_i8x16_splat((char) x) -// #define packed_dword(x) wasm_i32x4_splat(x) -// #define masked(x, mask) wasm_v128_and(x, wasm_i32x4_splat(mask)) -// -// int dec4() { -// unsigned int nsp = (state->wp - 1) & ~3; -// unsigned char *src = state->data + state->sp; -// unsigned char *end = state->data + nsp; -// unsigned char *dst = state->data + state->dp; -// unsigned int accu; -// -// while (src < end) { -// if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1; -// *((unsigned int *) dst) = accu; -// dst += 3; -// src += 4; -// } -// state->sp = nsp; -// state->dp = dst - state->data; -// return 0; -// } -// -// int dec() { -// unsigned int nsp = (state->wp - 1) & ~15; -// unsigned char *src = state->data + state->sp; -// unsigned char *end = state->data + nsp; -// unsigned char *dst = state->data + state->dp; -// unsigned int accu; -// -// v128_t err = wasm_i8x16_splat(0); -// -// while (src < end) { -// v128_t data = wasm_v128_load((v128_t *) src); -// -// // wasm-simd rewrite of http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html#vector-lookup-pshufb -// const v128_t higher_nibble = wasm_u32x4_shr(data, 4) & packed_byte(0x0f); -// const char linv = 1; -// const char hinv = 0; -// -// const v128_t lower_bound_LUT = wasm_i8x16_make( -// /* 0 */ linv, /* 1 */ linv, /* 2 */ 0x2b, /* 3 */ 0x30, -// /* 4 */ 0x41, /* 5 */ 0x50, /* 6 */ 0x61, /* 7 */ 0x70, -// /* 8 */ linv, /* 9 */ linv, /* a */ linv, /* b */ linv, -// /* c */ linv, /* d */ linv, /* e */ linv, /* f */ linv -// ); -// const v128_t upper_bound_LUT = wasm_i8x16_make( -// /* 0 */ hinv, /* 1 */ hinv, /* 2 */ 0x2b, /* 3 */ 0x39, -// /* 4 */ 0x4f, /* 5 */ 0x5a, /* 6 */ 0x6f, /* 7 */ 0x7a, -// /* 8 */ hinv, /* 9 */ hinv, /* a */ hinv, /* b */ hinv, -// /* c */ hinv, /* d */ hinv, /* e */ hinv, /* f */ hinv -// ); -// // the difference between the shift and lower bound -// const v128_t shift_LUT = wasm_i8x16_make( -// /* 0 */ 0x00, /* 1 */ 0x00, /* 2 */ 0x3e - 0x2b, /* 3 */ 0x34 - 0x30, -// /* 4 */ 0x00 - 0x41, /* 5 */ 0x0f - 0x50, /* 6 */ 0x1a - 0x61, /* 7 */ 0x29 - 0x70, -// /* 8 */ 0x00, /* 9 */ 0x00, /* a */ 0x00, /* b */ 0x00, -// /* c */ 0x00, /* d */ 0x00, /* e */ 0x00, /* f */ 0x00 -// ); -// -// const v128_t upper_bound = wasm_i8x16_swizzle(upper_bound_LUT, higher_nibble); -// const v128_t lower_bound = wasm_i8x16_swizzle(lower_bound_LUT, higher_nibble); -// -// const v128_t below = wasm_i8x16_lt(data, lower_bound); -// const v128_t above = wasm_i8x16_gt(data, upper_bound); -// const v128_t eq_2f = wasm_i8x16_eq(data, packed_byte(0x2f)); -// -// // in_range = not (below or above) or eq_2f -// // outside = not in_range = below or above and not eq_2f (from deMorgan law) -// const v128_t outside = wasm_v128_andnot(eq_2f, above | below); -// err = wasm_v128_or(err, outside); -// -// const v128_t shift = wasm_i8x16_swizzle(shift_LUT, higher_nibble); -// const v128_t t0 = wasm_i8x16_add(data, shift); -// v128_t v = wasm_i8x16_add(t0, wasm_v128_and(eq_2f, packed_byte(-3))); -// -// // pack bytes -// const v128_t ca = masked(v, 0x003f003f); -// const v128_t db = masked(v, 0x3f003f00); -// const v128_t t00 = wasm_v128_or(wasm_u32x4_shr(db, 8), wasm_i32x4_shl(ca, 6)); -// v128_t res = wasm_v128_or(wasm_u32x4_shr(t00, 16), wasm_i32x4_shl(t00, 12)); -// res = wasm_i8x16_swizzle(res, wasm_i8x16_const(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, 16, 16, 16, 16)); -// -// wasm_v128_store((v128_t *) dst, res); -// dst += 12; -// src += 16; -// } -// -// if (wasm_i8x16_bitmask(err) != 0) return 1; -// -// state->sp = nsp; -// state->dp = dst - state->data; -// return 0; -// } -// -// int end() { -// int rem = state->wp - state->sp; -// if (rem > 4 && dec4()) return 1; -// rem = state->wp - state->sp; -// if (rem < 2) return 1; -// -// unsigned char *src = state->data + state->sp; -// unsigned int accu = D0[src[0]] | D1[src[1]]; -// int dp = 1; -// if (rem > 2 && src[2] != 61) { -// accu |= D2[src[2]]; -// dp++; -// } -// if (rem == 4 && src[3] != 61) { -// accu |= D3[src[3]]; -// dp++; -// } -// if (accu >> 24) return 1; -// *((unsigned int *) (state->data + state->dp)) = accu; -// state->dp += dp; -// return state->dp != state->b_size; -// } -// ` -// }); - -// FIXME: currently broken in inwasm -type ExtractDefinition = Type extends () => IWasmInstance ? X : never; -type DecodeDefinition = ExtractDefinition; - -// base64 map -const MAP = new Uint8Array( - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .map(el => el.charCodeAt(0)) -); - -// init decoder maps in LE order -const D = new Uint32Array(1024); -D.fill(0xFF000000); -for (let i = 0; i < MAP.length; ++i) D[MAP[i]] = i << 2; -for (let i = 0; i < MAP.length; ++i) D[256 + MAP[i]] = i >> 4 | ((i << 4) & 0xFF) << 8; -for (let i = 0; i < MAP.length; ++i) D[512 + MAP[i]] = (i >> 2) << 8 | ((i << 6) & 0xFF) << 16; -for (let i = 0; i < MAP.length; ++i) D[768 + MAP[i]] = i << 16; - -const EMPTY = new Uint8Array(0); - -/** - * base64 streamline inplace decoder. - * - * Features / assumptions: - * - optimized uint32 read/write (only LE support!) - * - lazy chunkwise decoding - * - errors out on any non base64 chars (no support for NL formatted base64) - * - decodes in wasm - * - inplace decoding to save memory - * - supports a keepSize for lazy memory release - */ -export class Base64Decoder { - private _d!: Uint8Array; - private _m32!: Uint32Array; - private _inst!: IWasmInstance; - private _mem!: WebAssembly.Memory; - - constructor(public keepSize: number) {} - - /** - * Currently decoded bytes (borrowed). - * Must be accessed before calling `release` or `init`. - */ - public get data8(): Uint8Array { - return this._inst ? this._d.subarray(0, this._m32[P32.STATE_DP]) : EMPTY; - } - - /** - * Release memory conditionally based on `keepSize`. - * If memory gets released, also the wasm instance will be freed and recreated on next `init`, - * otherwise the instance will be reused. - */ - public release(): void { - if (!this._inst) return; - if (this._mem.buffer.byteLength > this.keepSize) { - this._inst = this._m32 = this._d = this._mem = null!; - } else { - this._m32[P32.STATE_WP] = 0; - this._m32[P32.STATE_SP] = 0; - this._m32[P32.STATE_DP] = 0; - } - } - - /** - * Initializes the decoder for new base64 data. - * Must be called before doing any decoding attempts. - * `size` is the amount of decoded bytes to be expected. - * The method will either spawn a new wasm instance or grow - * the needed memory of an existing instance. - */ - public init(size: number): void { - let m = this._m32; - const bytes = (Math.ceil(size / 3) + P32.STATE_DATA) * 4; - if (!this._inst) { - this._mem = new WebAssembly.Memory({ initial: Math.ceil(bytes / 65536) }); - this._inst = wasmDecode({ env: { memory: this._mem } }); - m = new Uint32Array(this._mem.buffer, 0); - m.set(D, P32.D0); - this._d = new Uint8Array(this._mem.buffer, P32.STATE_DATA * 4); - } else if (this._mem.buffer.byteLength < bytes) { - this._mem.grow(Math.ceil((bytes - this._mem.buffer.byteLength) / 65536)); - m = new Uint32Array(this._mem.buffer, 0); - this._d = new Uint8Array(this._mem.buffer, P32.STATE_DATA * 4); - } - m[P32.STATE_BSIZE] = size; - m[P32.STATE_ESIZE] = Math.ceil(size / 3) * 4; - m[P32.STATE_WP] = 0; - m[P32.STATE_SP] = 0; - m[P32.STATE_DP] = 0; - this._m32 = m; - } - - /** - * Put bytes in `data` from `start` to `end` (exclusive) into the decoder. - * Also decodes base64 data inplace once the payload exceeds 2^17 bytes. - * Returns 1 on error, else 0. - */ - public put(data: Uint8Array | Uint16Array | Uint32Array, start: number, end: number): number { - if (!this._inst) return 1; - const m = this._m32; - if (end - start + m[P32.STATE_WP] > m[P32.STATE_ESIZE]) return 1; - this._d.set(data.subarray(start, end), m[P32.STATE_WP]); - m[P32.STATE_WP] += end - start; - // max chunk in input handler is 2^17, try to run in "tandem mode" - // also assures that we dont run into illegal offsets in the wasm part - return m[P32.STATE_WP] - m[P32.STATE_SP] >= 131072 ? this._inst.exports.dec() : 0; - } - - /** - * End the current decoding. - * Decodes leftover payload and finally checks for the correct amount of - * decoded bytes by comparing to the value given to `init`. - * Returns 1 on error, else 0. - */ - public end(): number { - return this._inst ? this._inst.exports.end() : 1; - } -} diff --git a/package.json b/package.json index de8238324e..702149330f 100644 --- a/package.json +++ b/package.json @@ -44,10 +44,8 @@ "install-addons": "node ./bin/install-addons.js", "presetup": "npm run install-addons", "setup": "npm run build", - "postsetup": "npm run inwasm", "prepublishOnly": "npm run package", "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput", - "inwasm": "cd addons/xterm-addon-image && npm run inwasm -- -S", "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/test/benchmark/*benchmark.js", "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/test/benchmark/*benchmark.js",