feat(parser): WASM VT500 scanner with ScanResult SOA dispatch

- Add C WASM scanner emitting structure-of-arrays ops (kinds, starts, lengths, aux, params)
- Integrate scan+dispatch in EscapeSequenceParser with async handler resume
- Fix CSI fast-path final byte index, ERROR state reporting, and precedingJoinState for REP
- Add build pipeline (build_parser_wasm.sh, transition table generator, wasm embed)

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-05-31 21:35:02 +00:00
co-authored by Daniel Imms
parent 936442c813
commit 3eca55a80d
15 changed files with 1571 additions and 381 deletions
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Copyright (c) 2026 The xterm.js authors. All rights reserved.
# @license MIT
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
if [[ ! -f src/common/parser/wasm/transition_table.h ]]; then
npm run build
node bin/gen_parser_transition_table.mjs
fi
WASM_DIR=src/common/parser/wasm
OUT="$WASM_DIR/parser.wasm"
clang --target=wasm32-unknown-unknown -O2 -nostdlib -fuse-ld=lld \
-Wl,--no-entry \
-Wl,--export-memory \
-Wl,--export=reset \
-Wl,--export=scan \
-Wl,--export=probe_action \
-Wl,--export=set_state \
-Wl,--export=sync_params_from \
-Wl,--export=export_params_to \
-Wl,--export=get_params_len \
-Wl,--export=get_subparams_len \
-Wl,--export=get_subparams_ptr \
-Wl,--export=get_subparams_idx_ptr \
-Wl,--export=get_input_ptr \
-Wl,--export=get_kinds_ptr \
-Wl,--export=get_starts_ptr \
-Wl,--export=get_lengths_ptr \
-Wl,--export=get_aux_ptr \
-Wl,--export=get_param_starts_ptr \
-Wl,--export=get_param_counts_ptr \
-Wl,--export=get_params_ptr \
-Wl,--export=get_state_ptr \
-Wl,--export=get_header_ptr \
-Wl,--initial-memory=67108864 \
-Wl,--max-memory=67108864 \
"$WASM_DIR/parser.c" -o "$OUT"
node bin/embed_parser_wasm.mjs
echo "Built $OUT"
+23
View File
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const wasm = readFileSync(join(root, 'src/common/parser/wasm/parser.wasm'));
const b64 = wasm.toString('base64');
const out = `/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
/** Base64-encoded parser.wasm (generated by bin/build_parser_wasm.sh) */
export const PARSER_WASM_BASE64 = '${b64}';\n`;
writeFileSync(join(root, 'src/common/parser/wasm/parser.wasm.embed.ts'), out);
console.info(`Embedded parser.wasm (${wasm.length} bytes)`);
+34
View File
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
// @ts-check
import { writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
// Import compiled transition table
const { VT500_TRANSITION_TABLE } = await import(join(root, 'out/common/parser/EscapeSequenceParser.js'));
const table = VT500_TRANSITION_TABLE.table;
let out = '/* Auto-generated by bin/gen_parser_transition_table.mjs - do not edit */\n';
out += `#ifndef PARSER_TRANSITION_TABLE_H\n#define PARSER_TRANSITION_TABLE_H\n\n`;
out += `#include <stdint.h>\n\n`;
out += `#define PARSER_TABLE_SIZE ${table.length}\n\n`;
out += `static const uint16_t PARSER_TRANSITION_TABLE[PARSER_TABLE_SIZE] = {\n`;
for (let i = 0; i < table.length; i++) {
if (i % 16 === 0) out += ' ';
out += `0x${table[i].toString(16)}`;
out += i < table.length - 1 ? ', ' : '';
if (i % 16 === 15) out += '\n';
}
if (table.length % 16 !== 0) out += '\n';
out += `};\n\n#endif\n`;
const headerPath = join(root, 'src/common/parser/wasm/transition_table.h');
writeFileSync(headerPath, out);
console.info(`Wrote ${headerPath} (${table.length} entries)`);
+1
View File
@@ -34,6 +34,7 @@
"start": "node demo/start",
"dev": "concurrently -k -p [{name}] -n tsc,esbuild,esbuild-demo-client,esbuild-demo-server,server -c blue,yellow,cyan,green,magenta \"npm:tsc-watch\" \"npm:esbuild-watch\" \"npm:esbuild-demo-client-watch\" \"npm:esbuild-demo-server-watch\" \"npm:start\"",
"build": "npm run tsc",
"build:parser-wasm": "bash bin/build_parser_wasm.sh",
"watch": "npm run tsc-watch",
"tsc": "tsgo -b ./tsconfig.all.json",
"tsc-watch": "tsgo -b -w ./tsconfig.all.json --preserveWatchOutput",
+23 -23
View File
@@ -77,10 +77,10 @@ class TestEscapeSequenceParser extends EscapeSequenceParser {
public get realParams(): IParams {
return this._params;
}
public get collect(): string {
public get collectStr(): string {
return this.identToString(this._collect);
}
public set collect(value: string) {
public set collectStr(value: string) {
this._collect = 0;
for (let i = 0; i < value.length; ++i) {
this._collect <<= 8;
@@ -248,19 +248,19 @@ describe('EscapeSequenceParser', () => {
assert.equal(parser.currentState, ParserState.GROUND);
assert.equal(parser.osc, '');
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
});
it('reset states', () => {
parser.currentState = 124;
parser.osc = '#';
parser.params = [123];
parser.collect = '#';
parser.collectStr = '#';
parser.reset();
assert.equal(parser.currentState, ParserState.GROUND);
assert.equal(parser.osc, '');
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
});
});
describe('state transitions and actions', () => {
@@ -327,11 +327,11 @@ describe('EscapeSequenceParser', () => {
for (state in states) {
parser.currentState = state;
parser.params = [23];
parser.collect = '#';
parser.collectStr = '#';
parse(parser, '\x1b');
assert.equal(parser.currentState, ParserState.ESCAPE);
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
parser.reset();
}
});
@@ -383,7 +383,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.ESCAPE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -419,7 +419,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -442,21 +442,21 @@ describe('EscapeSequenceParser', () => {
// C0
parser.currentState = ParserState.ESCAPE;
parser.params = [123];
parser.collect = '#';
parser.collectStr = '#';
parse(parser, '[');
assert.equal(parser.currentState, ParserState.CSI_ENTRY);
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
parser.reset();
// C1
for (state in states) {
parser.currentState = state;
parser.params = [123];
parser.collect = '#';
parser.collectStr = '#';
parse(parser, '\x9b');
assert.equal(parser.currentState, ParserState.CSI_ENTRY);
assert.deepEqual(parser.params, [0]);
assert.equal(parser.collect, '');
assert.equal(parser.collectStr, '');
parser.reset();
}
});
@@ -517,7 +517,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_PARAM);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -582,7 +582,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -593,7 +593,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_PARAM;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -619,7 +619,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.CSI_INTERMEDIATE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -848,7 +848,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_PARAM);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -930,7 +930,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -941,7 +941,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_PARAM;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -965,7 +965,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_INTERMEDIATE;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
@@ -976,7 +976,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.DCS_INTERMEDIATE;
parse(parser, '\x20' + chars[i]);
assert.equal(parser.currentState, ParserState.DCS_IGNORE);
assert.equal(parser.collect, '\x20');
assert.equal(parser.collectStr, '\x20');
parser.reset();
}
});
@@ -1079,7 +1079,7 @@ describe('EscapeSequenceParser', () => {
parser.currentState = ParserState.APC_ENTRY;
parse(parser, collect[i]);
assert.equal(parser.currentState, ParserState.APC_INTERMEDIATE);
assert.equal(parser.collect, collect[i]);
assert.equal(parser.collectStr, collect[i]);
parser.reset();
}
});
+11 -358
View File
@@ -11,6 +11,8 @@ import { Params } from './Params';
import { OscParser } from './OscParser';
import { DcsParser } from './DcsParser';
import { ApcParser } from './ApcParser';
import { parseWithWasmScanner } from './EscapeSequenceParserWasm';
import { WasmEscapeScanner } from './WasmEscapeScanner';
/**
* VT commands done by the parser
@@ -269,6 +271,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
protected _params: Params;
protected _collect: number;
public get collect(): number { return this._collect; }
public set collect(value: number) { this._collect = value; }
// handler lookup containers
protected _printHandler: PrintHandlerType;
protected _executeHandlers: { [flag: number]: ExecuteHandlerType };
@@ -297,6 +302,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
chunkPos: 0
};
protected _scanCache: { opIndex: number, stateBeforeScan?: number } = { opIndex: 0 };
constructor(
protected readonly _transitions: TransitionTable = VT500_TRANSITION_TABLE
) {
@@ -494,12 +501,15 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
*/
public reset(): void {
this.currentState = this.initialState;
WasmEscapeScanner.initSync();
WasmEscapeScanner.reset();
this._oscParser.reset();
this._dcsParser.reset();
this._apcParser.reset();
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
this._scanCache.opIndex = 0;
// abort pending continuation from async handler
// Here the RESET type indicates, that the next parse call will
// ignore any saved stack, instead continues sync with next codepoint from GROUND
@@ -572,363 +582,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
* ```
*/
public parse(data: Uint32Array, length: number, promiseResult?: boolean): void | Promise<boolean> {
let code: number;
let transition: number;
let start = 0;
let handlerResult: void | boolean | Promise<boolean>;
// resume from async handler
if (this._parseStack.state) {
// allow sync parser reset even in continuation mode
// Note: can be used to recover parser from improper continuation error below
if (this._parseStack.state === ParserStackType.RESET) {
this._parseStack.state = ParserStackType.NONE;
start = this._parseStack.chunkPos + 1; // continue with next codepoint in GROUND
} else {
if (promiseResult === undefined || this._parseStack.state === ParserStackType.FAIL) {
/**
* Reject further parsing on improper continuation after pausing. This is a really bad
* condition with screwed up execution order and prolly messed up terminal state,
* therefore we exit hard with an exception and reject any further parsing.
*
* Note: With `Terminal.write` usage this exception should never occur, as the top level
* calls are guaranteed to handle async conditions properly. If you ever encounter this
* exception in your terminal integration it indicates, that you injected data chunks to
* `InputHandler.parse` or `EscapeSequenceParser.parse` synchronously without waiting for
* continuation of a running async handler.
*
* It is possible to get rid of this error by calling `reset`. But dont rely on that, as
* the pending async handler still might mess up the terminal later. Instead fix the
* faulty async handling, so this error will not be thrown anymore.
*/
this._parseStack.state = ParserStackType.FAIL;
throw new Error('improper continuation due to previous async handler, giving up parsing');
}
// we have to resume the old handler loop if:
// - return value of the promise was `false`
// - handlers are not exhausted yet
const handlers = this._parseStack.handlers;
let handlerPos = this._parseStack.handlerPos - 1;
switch (this._parseStack.state) {
case ParserStackType.CSI:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as CsiHandlerType[])[handlerPos](this._params);
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
this._parseStack.handlers = [];
break;
case ParserStackType.ESC:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as EscHandlerType[])[handlerPos]();
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
this._parseStack.handlers = [];
break;
case ParserStackType.DCS:
code = data[this._parseStack.chunkPos];
handlerResult = this._dcsParser.unhook(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) {
return handlerResult;
}
if (code === 0x1b) this._parseStack.transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
break;
case ParserStackType.OSC:
code = data[this._parseStack.chunkPos];
handlerResult = this._oscParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) {
return handlerResult;
}
if (code === 0x1b) this._parseStack.transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
break;
case ParserStackType.APC:
code = data[this._parseStack.chunkPos];
handlerResult = this._apcParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) {
return handlerResult;
}
if (code === 0x1b) this._parseStack.transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
break;
}
// cleanup before continuing with the main sync loop
this._parseStack.state = ParserStackType.NONE;
start = this._parseStack.chunkPos + 1;
this.precedingJoinState = 0;
this.currentState = this._parseStack.transition & TableAccess.TRANSITION_STATE_MASK;
}
}
// continue with main sync loop
// process input string
for (let i = start; i < length; ++i) {
code = data[i];
// EXE fast-path: common control bytes (0x00-0x17) in non-payload states
if (code < 0x18 && this.currentState <= ParserState.CSI_IGNORE) {
(this._executeHandlersArr[code] ?? this._executeHandlerFb)(code);
this.precedingJoinState = 0;
continue;
}
// CSI fast-path: collapse ESC [ into a single entry, parse params+final in a tight loop
if (code === 0x1b
&& this.currentState < ParserState.OSC_STRING
&& i + 2 < length && data[i + 1] === 0x5b
) {
this._params.resetZdm();
this._collect = 0;
let k = i + 2;
let ch = data[k];
if (ch >= 0x3c && ch <= 0x3f) {
this._collect = ch;
k++;
}
let csiDone = false;
for (; k < length; k++) {
ch = data[k];
if (ch >= 0x30 && ch <= 0x39) {
this._params.addDigit(ch - 48);
} else if (ch === 0x3b) {
this._params.addParam(0);
} else if (ch === 0x3a) {
this._params.addSubParam(-1);
} else if (ch >= 0x40 && ch <= 0x7e) {
const handlers = this._csiHandlers[this._collect << 8 | ch];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
handlerResult = handlers[j](this._params);
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
transition = ParserAction.CSI_DISPATCH << TableAccess.TRANSITION_ACTION_SHIFT | ParserState.GROUND;
this._preserveStack(ParserStackType.CSI, handlers, j, transition, k);
return handlerResult;
}
}
if (j < 0) {
this._csiHandlerFb(this._collect << 8 | ch, this._params);
}
this.precedingJoinState = 0;
i = k;
this.currentState = ParserState.GROUND;
csiDone = true;
break;
} else {
break;
}
}
if (!csiDone) {
i = k - 1;
this.currentState = ParserState.CSI_PARAM;
}
continue;
}
// normal transition & action lookup
transition = this._transitions.table[
this.currentState << TableAccess.INDEX_STATE_SHIFT |
(code < NON_ASCII_PRINTABLE ? code : NON_ASCII_PRINTABLE)
];
switch (transition >> TableAccess.TRANSITION_ACTION_SHIFT) {
case ParserAction.PRINT:
// Note: 0x20 (SP) is included, 0x7F (DEL) is excluded
let c = i;
const l4 = length - 4;
while (c < l4
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
&& data[++c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)
) {}
if (c >= l4) {
while (c < length && data[c] >= 0x20 && (data[c] <= 0x7e || data[c] >= NON_ASCII_PRINTABLE)) {
c++;
}
}
this._printHandler(data, i, c);
i = c - 1;
break;
case ParserAction.EXECUTE:
if (this._executeHandlers[code]) this._executeHandlers[code]();
else this._executeHandlerFb(code);
this.precedingJoinState = 0;
break;
case ParserAction.IGNORE:
break;
case ParserAction.ERROR:
const inject: IParsingState = this._errorHandler(
{
position: i,
code,
currentState: this.currentState,
collect: this._collect,
params: this._params,
abort: false
});
if (inject.abort) return;
// inject values: currently not implemented
break;
case ParserAction.CSI_DISPATCH:
// Trigger CSI Handler
const handlers = this._csiHandlers[this._collect << 8 | code];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
// true means success and to stop bubbling
// a promise indicates an async handler that needs to finish before progressing
handlerResult = handlers[j](this._params);
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._preserveStack(ParserStackType.CSI, handlers, j, transition, i);
return handlerResult;
}
}
if (j < 0) {
this._csiHandlerFb(this._collect << 8 | code, this._params);
}
this.precedingJoinState = 0;
break;
case ParserAction.PARAM:
// inner loop: digits (0x30 - 0x39) and ; (0x3b) and : (0x3a)
do {
switch (code) {
case 0x3b:
this._params.addParam(0); // ZDM
break;
case 0x3a:
this._params.addSubParam(-1);
break;
default: // 0x30 - 0x39
this._params.addDigit(code - 48);
}
} while (++i < length && (code = data[i]) > 0x2f && code < 0x3c);
i--;
break;
case ParserAction.COLLECT:
this._collect <<= 8;
this._collect |= code;
break;
case ParserAction.ESC_DISPATCH:
const handlersEsc = this._escHandlers[this._collect << 8 | code];
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
for (; jj >= 0; jj--) {
// true means success and to stop bubbling
// a promise indicates an async handler that needs to finish before progressing
handlerResult = handlersEsc[jj]();
if (handlerResult === true) {
break;
} else if (handlerResult instanceof Promise) {
this._preserveStack(ParserStackType.ESC, handlersEsc, jj, transition, i);
return handlerResult;
}
}
if (jj < 0) {
this._escHandlerFb(this._collect << 8 | code);
}
this.precedingJoinState = 0;
break;
case ParserAction.CLEAR:
this._params.resetZdm();
this._collect = 0;
break;
case ParserAction.DCS_HOOK:
this._dcsParser.hook(this._collect << 8 | code, this._params);
break;
case ParserAction.DCS_PUT:
// inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
// unhook triggered by: 0x1b, 0x9c (success) and 0x18, 0x1a (abort)
for (let j = i + 1; ; ++j) {
if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
this._dcsParser.put(data, i, j);
i = j - 1;
break;
}
}
break;
case ParserAction.DCS_UNHOOK:
handlerResult = this._dcsParser.unhook(code !== 0x18 && code !== 0x1a);
if (handlerResult) {
this._preserveStack(ParserStackType.DCS, [], 0, transition, i);
return handlerResult;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
break;
case ParserAction.OSC_START:
this._oscParser.start();
break;
case ParserAction.OSC_PUT:
// inner loop: 0x20 (SP) included, 0x7F (DEL) included
for (let j = i + 1; ; j++) {
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
this._oscParser.put(data, i, j);
i = j - 1;
break;
}
}
break;
case ParserAction.OSC_END:
handlerResult = this._oscParser.end(code !== 0x18 && code !== 0x1a);
if (handlerResult) {
this._preserveStack(ParserStackType.OSC, [], 0, transition, i);
return handlerResult;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
break;
case ParserAction.APC_START:
this._apcParser.start(this._collect << 8 | code);
break;
case ParserAction.APC_PUT:
// inner loop - exit APC_PUT: 0x18, 0x1a, 0x1b, 0x9c
// allowed: 00/08 .. 00/13, 02/00 .. 07/14 + NON_ASCII_PRINTABLE
for (let j = i + 1; ; ++j) {
if (j < length && (
(data[j] >= 0x20 && data[j] < 0x7f) || (data[j] >= 0x08 && data[j] < 0x0e) || data[j] >= NON_ASCII_PRINTABLE
)) continue;
this._apcParser.put(data, i, j);
i = j - 1;
break;
}
break;
case ParserAction.APC_END:
handlerResult = this._apcParser.end(code !== 0x18 && code !== 0x1a);
if (handlerResult) {
this._preserveStack(ParserStackType.APC, [], 0, transition, i);
return handlerResult;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
this._params.resetZdm();
this._collect = 0;
this.precedingJoinState = 0;
break;
}
this.currentState = transition & TableAccess.TRANSITION_STATE_MASK;
}
return parseWithWasmScanner(this as unknown as import('./EscapeSequenceParserWasm').IWasmParseHost, data, length, promiseResult, this._scanCache);
}
}
@@ -0,0 +1,349 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ParserState } from './Constants';
import { Params } from './Params';
import { Op, ScanResult } from './ScanTypes';
import { WasmEscapeScanner } from './WasmEscapeScanner';
import {
CsiHandlerType,
EscHandlerType,
IParams,
IParsingState,
IParserStackState,
ParserStackType,
ResumableHandlersType
} from './Types';
export interface IWasmParseHost {
currentState: number;
collect: number;
precedingJoinState: number;
readonly _params: Params;
readonly _parseStack: IParserStackState;
readonly _csiHandlers: { [ident: number]: CsiHandlerType[] | undefined };
readonly _escHandlers: { [ident: number]: EscHandlerType[] | undefined };
readonly _executeHandlers: { [flag: number]: (() => void) | undefined };
readonly _executeHandlersArr: Array<((code: number) => void) | undefined>;
_csiHandlerFb(ident: number, params: IParams): void;
_escHandlerFb(ident: number): void;
_executeHandlerFb(code: number): void;
_printHandler(data: Uint32Array, start: number, end: number): void;
_oscParser: { start(): void; put(data: Uint32Array, start: number, end: number): void; end(success: boolean, promiseResult?: boolean): void | Promise<boolean> };
_dcsParser: { hook(ident: number, params: IParams): void; put(data: Uint32Array, start: number, end: number): void; unhook(success: boolean, promiseResult?: boolean): void | Promise<boolean> };
_apcParser: { start(ident: number): void; put(data: Uint32Array, start: number, end: number): void; end(success: boolean, promiseResult?: boolean): void | Promise<boolean> };
_preserveStack(state: ParserStackType, handlers: ResumableHandlersType, handlerPos: number, transition: number, chunkPos: number): void;
_errorHandler(state: IParsingState): IParsingState;
}
let wasmReady = false;
function ensureWasm(): void {
if (!wasmReady) {
WasmEscapeScanner.initSync();
wasmReady = true;
}
}
function loadParams(host: IWasmParseHost, scan: ScanResult, opIndex: number): void {
const start = scan.paramStarts[opIndex] ?? 0;
const count = scan.paramCounts[opIndex] ?? 0;
if (count === 0) {
host._params.resetZdm();
return;
}
let idx = start;
host._params.resetZdm();
for (let p = 0; p < count; p++) {
const main = scan.params[idx++] | 0;
if (p === 0) {
host._params.params[0] = main;
host._params.length = 1;
} else {
host._params.addParam(main);
}
while (idx < scan.params.length && (scan.params[idx] & 0x80000000) !== 0) {
const sv = scan.params[idx++] & 0x7FFFFFFF;
host._params.addSubParam(sv === 0x7FFFFFFF ? -1 : sv);
}
}
}
export function parseWithWasmScanner(
host: IWasmParseHost,
data: Uint32Array,
length: number,
promiseResult: boolean | undefined,
scanCache: { scan?: ScanResult; data?: Uint32Array; opIndex: number; stateBeforeScan?: number }
): void | Promise<boolean> {
ensureWasm();
// Resume async handler dispatch without rescanning
if (host._parseStack.state) {
return resumeWasmParse(host, data, length, promiseResult, scanCache);
}
const stateBeforeScan = host.currentState;
scanCache.stateBeforeScan = stateBeforeScan;
WasmEscapeScanner.syncParserState(host.currentState, host.collect, host._params);
const scan = WasmEscapeScanner.scan(data, length);
scanCache.scan = scan;
scanCache.data = data;
scanCache.opIndex = 0;
host.currentState = WasmEscapeScanner.currentState;
host.collect = WasmEscapeScanner.collect;
return dispatchScanOps(host, data, scan, 0, scanCache, promiseResult, stateBeforeScan);
}
function resumeWasmParse(
host: IWasmParseHost,
data: Uint32Array,
length: number,
promiseResult: boolean | undefined,
scanCache: { scan?: ScanResult; data?: Uint32Array; opIndex: number; stateBeforeScan?: number }
): void | Promise<boolean> {
if (host._parseStack.state === ParserStackType.RESET) {
host._parseStack.state = ParserStackType.NONE;
scanCache.opIndex = host._parseStack.chunkPos + 1;
host._parseStack.chunkPos = 0;
if (!scanCache.scan || scanCache.data !== data) {
return parseWithWasmScanner(host, data, length, promiseResult, scanCache);
}
return dispatchScanOps(host, data, scanCache.scan, scanCache.opIndex, scanCache, promiseResult, scanCache.stateBeforeScan ?? host.currentState);
}
if (promiseResult === undefined || host._parseStack.state === ParserStackType.FAIL) {
host._parseStack.state = ParserStackType.FAIL;
throw new Error('improper continuation due to previous async handler, giving up parsing');
}
const handlers = host._parseStack.handlers;
let handlerPos = host._parseStack.handlerPos - 1;
let handlerResult: void | boolean | Promise<boolean>;
const scan = scanCache.scan;
const opIndex = scanCache.opIndex;
if (!scan) {
throw new Error('missing scan cache on wasm parser resume');
}
switch (host._parseStack.state) {
case ParserStackType.CSI:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as CsiHandlerType[])[handlerPos](host._params);
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
host._parseStack.handlers = [];
scanCache.opIndex = opIndex + 1;
break;
case ParserStackType.ESC:
if (promiseResult === false && handlerPos > -1) {
for (; handlerPos >= 0; handlerPos--) {
handlerResult = (handlers as EscHandlerType[])[handlerPos]();
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._parseStack.handlerPos = handlerPos;
return handlerResult;
}
}
}
host._parseStack.handlers = [];
scanCache.opIndex = opIndex + 1;
break;
case ParserStackType.DCS: {
const code = data[host._parseStack.chunkPos];
handlerResult = host._dcsParser.unhook(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) return handlerResult;
if (code === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
scanCache.opIndex = opIndex + 1;
break;
}
case ParserStackType.OSC: {
const code = data[host._parseStack.chunkPos];
handlerResult = host._oscParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) return handlerResult;
if (code === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
scanCache.opIndex = opIndex + 1;
break;
}
case ParserStackType.APC: {
const code = data[host._parseStack.chunkPos];
handlerResult = host._apcParser.end(code !== 0x18 && code !== 0x1a, promiseResult);
if (handlerResult) return handlerResult;
if (code === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
scanCache.opIndex = opIndex + 1;
break;
}
}
host._parseStack.state = ParserStackType.NONE;
host.precedingJoinState = 0;
return dispatchScanOps(host, data, scan, scanCache.opIndex, scanCache, promiseResult, scanCache.stateBeforeScan ?? host.currentState);
}
function dispatchScanOps(
host: IWasmParseHost,
data: Uint32Array,
scan: ScanResult,
fromOp: number,
scanCache: { scan?: ScanResult; data?: Uint32Array; opIndex: number },
promiseResult: boolean | undefined,
stateBeforeScan: number
): void | Promise<boolean> {
let handlerResult: void | boolean | Promise<boolean>;
for (let o = fromOp; o < scan.opCount; o++) {
scanCache.opIndex = o;
const kind = scan.kinds[o];
const start = scan.starts[o];
const len = scan.lengths[o];
const aux = scan.aux[o];
const chunkPos = (kind === Op.Csi || kind === Op.Esc) ? len : start;
switch (kind) {
case Op.Print:
host._printHandler(data, start, start + len);
break;
case Op.Execute: {
const code = aux;
if (code < 0x18) {
const exe = host._executeHandlersArr[code];
if (exe) exe(code);
else host._executeHandlerFb(code);
} else if (host._executeHandlers[code]) {
host._executeHandlers[code]!();
} else {
host._executeHandlerFb(code);
}
host.precedingJoinState = 0;
break;
}
case Op.Csi: {
host.collect = aux >> 8;
loadParams(host, scan, o);
const handlers = host._csiHandlers[aux];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
handlerResult = handlers![j](host._params);
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._preserveStack(ParserStackType.CSI, handlers!, j, 0, chunkPos);
return handlerResult;
}
}
if (j < 0) host._csiHandlerFb(aux, host._params);
host.precedingJoinState = 0;
break;
}
case Op.Esc: {
host.collect = aux >> 8;
loadParams(host, scan, o);
const handlersEsc = host._escHandlers[aux];
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
for (; jj >= 0; jj--) {
handlerResult = handlersEsc![jj]();
if (handlerResult === true) break;
if (handlerResult instanceof Promise) {
host._preserveStack(ParserStackType.ESC, handlersEsc!, jj, 0, chunkPos);
return handlerResult;
}
}
if (jj < 0) host._escHandlerFb(aux);
host.precedingJoinState = 0;
break;
}
case Op.OscStart:
host._oscParser.start();
break;
case Op.OscPut:
if (len > 0) host._oscParser.put(data, start, start + len);
break;
case Op.OscEnd: {
const term = aux;
handlerResult = host._oscParser.end(term !== 0x18 && term !== 0x1a);
if (handlerResult) {
host._preserveStack(ParserStackType.OSC, [], 0, 0, start);
return handlerResult;
}
if (term === 0x1b) host.currentState = ParserState.ESCAPE;
host._params.resetZdm();
host.collect = 0;
host.precedingJoinState = 0;
break;
}
case Op.DcsHook: {
host.collect = aux >> 8;
loadParams(host, scan, o);
host._dcsParser.hook(aux, host._params);
break;
}
case Op.DcsPut:
if (len > 0) host._dcsParser.put(data, start, start + len);
break;
case Op.DcsUnhook: {
const term = aux;
handlerResult = host._dcsParser.unhook(term !== 0x18 && term !== 0x1a);
if (handlerResult) {
host._preserveStack(ParserStackType.DCS, [], 0, 0, start);
return handlerResult;
}
host._params.resetZdm();
host.collect = 0;
host.precedingJoinState = 0;
break;
}
case Op.ApcStart:
host._apcParser.start(aux);
break;
case Op.ApcPut:
if (len > 0) host._apcParser.put(data, start, start + len);
break;
case Op.ApcEnd: {
const term = aux;
handlerResult = host._apcParser.end(term !== 0x18 && term !== 0x1a);
if (handlerResult) {
host._preserveStack(ParserStackType.APC, [], 0, 0, start);
return handlerResult;
}
host._params.resetZdm();
host.collect = 0;
host.precedingJoinState = 0;
break;
}
case Op.Error: {
WasmEscapeScanner.exportParamsTo(host._params);
const inject = host._errorHandler({
position: start,
code: aux & 0xFFFF,
currentState: aux >> 16,
collect: WasmEscapeScanner.collect,
params: host._params,
abort: false
});
if (inject.abort) return;
break;
}
}
}
scanCache.opIndex = scan.opCount;
host.currentState = WasmEscapeScanner.currentState;
host.collect = WasmEscapeScanner.collect;
WasmEscapeScanner.exportParamsTo(host._params);
}
+37
View File
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
export const Op = {
Print: 0,
Execute: 1,
Esc: 2,
Csi: 3,
Osc: 4,
Dcs: 5,
Apc: 6,
DcsHook: 7,
DcsPut: 8,
DcsUnhook: 9,
OscStart: 10,
OscPut: 11,
OscEnd: 12,
ApcStart: 13,
ApcPut: 14,
ApcEnd: 15,
Error: 16
} as const;
export type OpKind = typeof Op[keyof typeof Op];
export type ScanResult = {
kinds: Uint8Array;
starts: Uint32Array;
lengths: Uint32Array;
aux: Uint32Array;
params: Uint32Array;
paramStarts: Uint32Array;
paramCounts: Uint16Array;
opCount: number;
};
@@ -0,0 +1,36 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { StringToUtf32 } from '../input/TextDecoder';
import { Op } from './ScanTypes';
import { WasmEscapeScanner } from './WasmEscapeScanner';
describe('WasmEscapeScanner', () => {
before(() => {
WasmEscapeScanner.initSync();
});
beforeEach(() => {
WasmEscapeScanner.reset();
});
it('scans hello with SGR sequences', () => {
const input = 'hello\x1b[31mred\x1b[0m';
const data = new Uint32Array(input.length);
const len = new StringToUtf32().decode(input, data);
const scan = WasmEscapeScanner.scan(data, len);
assert.equal(scan.opCount, 4);
assert.deepEqual(Array.from(scan.kinds), [Op.Print, Op.Csi, Op.Print, Op.Csi]);
assert.deepEqual(Array.from(scan.starts), [0, 0, 10, 1]);
// CSI/ESC: lengths hold final-byte index; print ops use run length
assert.deepEqual(Array.from(scan.lengths), [5, 9, 3, 16]);
assert.deepEqual(Array.from(scan.aux), [0, 'm'.charCodeAt(0), 0, 'm'.charCodeAt(0)]);
assert.deepEqual(Array.from(scan.paramStarts), [0, 0, 0, 1]);
assert.deepEqual(Array.from(scan.paramCounts), [0, 1, 0, 1]);
assert.deepEqual(Array.from(scan.params), [31, 0]);
});
});
+207
View File
@@ -0,0 +1,207 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Params } from './Params';
import { Op, ScanResult } from './ScanTypes';
import { PARSER_WASM_BASE64 } from './wasm/parser.wasm.embed';
export { Op };
const WASM_IMPORTS = {
env: {
memory: new WebAssembly.Memory({ initial: 1024, maximum: 1024 })
}
};
let wasmModule: WebAssembly.Instance | undefined;
let wasmExports: {
memory: WebAssembly.Memory;
reset: () => void;
scan: (length: number) => number;
set_state: (state: number, collect: number) => void;
get_input_ptr: () => number;
get_kinds_ptr: () => number;
get_starts_ptr: () => number;
get_lengths_ptr: () => number;
get_aux_ptr: () => number;
get_param_starts_ptr: () => number;
get_param_counts_ptr: () => number;
get_params_ptr: () => number;
get_state_ptr: () => number;
get_header_ptr: () => number;
sync_params_from: (srcPtr: number, len: number) => void;
export_params_to: (dstPtr: number, maxLen: number) => void;
get_params_len: () => number;
get_subparams_len: () => number;
get_subparams_ptr: () => number;
get_subparams_idx_ptr: () => number;
};
async function initWasm(): Promise<void> {
if (wasmModule) return;
const bytes = Uint8Array.from(atob(PARSER_WASM_BASE64), c => c.charCodeAt(0));
const mod = await WebAssembly.instantiate(bytes, WASM_IMPORTS);
wasmModule = mod.instance;
wasmExports = wasmModule.exports as typeof wasmExports;
}
function ensureWasm(): typeof wasmExports {
if (!wasmExports) {
throw new Error('WasmEscapeScanner not initialized. Call WasmEscapeScanner.init() first.');
}
return wasmExports;
}
export class WasmEscapeScanner {
private static _initPromise: Promise<void> | undefined;
private static _syncReady = false;
public static init(): Promise<void> {
if (!this._initPromise) {
this._initPromise = initWasm().then(() => {
this._syncReady = true;
wasmExports.reset();
});
}
return this._initPromise;
}
/** Synchronous init for unit tests (Node supports sync instantiate). */
public static initSync(): void {
if (this._syncReady) return;
const binary = globalThis.atob(PARSER_WASM_BASE64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
const mod = new WebAssembly.Module(bytes);
wasmModule = new WebAssembly.Instance(mod, WASM_IMPORTS);
wasmExports = wasmModule.exports as typeof wasmExports;
wasmExports.reset();
this._syncReady = true;
}
public static reset(): void {
ensureWasm().reset();
}
public static get currentState(): number {
const mem = new Uint32Array(ensureWasm().memory.buffer);
const statePtr = ensureWasm().get_state_ptr() >> 2;
return mem[statePtr];
}
public static set currentState(value: number) {
const ex = ensureWasm();
const mem = new Uint32Array(ex.memory.buffer);
const statePtr = ex.get_state_ptr() >> 2;
ex.set_state(value, mem[statePtr + 1]);
}
public static get collect(): number {
const mem = new Uint32Array(ensureWasm().memory.buffer);
return mem[(ensureWasm().get_state_ptr() >> 2) + 1];
}
public static set collect(value: number) {
const ex = ensureWasm();
ex.set_state(memState(ex), value);
}
public static syncParserState(currentState: number, collect: number, params: Params): void {
const ex = ensureWasm();
ex.set_state(currentState, collect);
if (params.length === 0) {
ex.sync_params_from(ex.get_params_ptr(), 0);
return;
}
const mem = ex.memory.buffer;
const paramsPtr = ex.get_params_ptr();
const view = new Int32Array(mem, paramsPtr, params.length);
for (let i = 0; i < params.length; i++) {
view[i] = params.params[i];
}
ex.sync_params_from(paramsPtr, params.length);
}
public static exportParamsTo(params: Params): void {
const ex = ensureWasm();
const len = ex.get_params_len();
params.reset();
if (len === 0) {
params.resetZdm();
return;
}
const mem = ex.memory.buffer;
const dstPtr = ex.get_params_ptr();
ex.export_params_to(dstPtr, len);
const pView = new Int32Array(mem, dstPtr, len);
const subLen = ex.get_subparams_len();
const subView = subLen > 0 ? new Int32Array(mem, ex.get_subparams_ptr(), subLen) : undefined;
const idxView = new Uint16Array(mem, ex.get_subparams_idx_ptr(), len);
params.resetZdm();
params.params[0] = pView[0];
params.length = 1;
if (subView && len > 0) {
const start0 = idxView[0] >> 8;
const end0 = idxView[0] & 0xFF;
for (let k = start0; k < end0; k++) {
params.addSubParam(subView[k]);
}
}
for (let i = 1; i < len; i++) {
params.addParam(pView[i]);
if (subView) {
const start = idxView[i] >> 8;
const end = idxView[i] & 0xFF;
for (let k = start; k < end; k++) {
params.addSubParam(subView[k]);
}
}
}
}
public static scan(data: Uint32Array, length: number): ScanResult {
const ex = ensureWasm();
const inputPtr = ex.get_input_ptr();
const mem = ex.memory.buffer;
const inputView = new Uint32Array(mem, inputPtr, length);
inputView.set(data.subarray(0, length));
const opCount = ex.scan(length);
if (opCount < 0) {
throw new Error('Wasm parser op buffer overflow');
}
const count = opCount;
const kinds = new Uint8Array(mem, ex.get_kinds_ptr(), count);
const starts = new Uint32Array(mem, ex.get_starts_ptr(), count);
const lengths = new Uint32Array(mem, ex.get_lengths_ptr(), count);
const aux = new Uint32Array(mem, ex.get_aux_ptr(), count);
const paramStarts = new Uint32Array(mem, ex.get_param_starts_ptr(), count);
const paramCounts = new Uint16Array(mem, ex.get_param_counts_ptr(), count);
const headerPtr = ex.get_header_ptr() >> 2;
const headerView = new Uint32Array(mem, ex.get_header_ptr(), 3);
const paramsLen = headerView[2];
const params = new Uint32Array(mem, ex.get_params_ptr(), paramsLen);
return {
kinds: kinds.slice(),
starts: starts.slice(),
lengths: lengths.slice(),
aux: aux.slice(),
params: params.slice(),
paramStarts: paramStarts.slice(),
paramCounts: paramCounts.slice(),
opCount: count
};
}
}
function memState(ex: typeof wasmExports): number {
const mem = new Uint32Array(ex.memory.buffer);
return mem[ex.get_state_ptr() >> 2];
}
+441
View File
@@ -0,0 +1,441 @@
/* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT */
#include "parser.h"
#include "transition_table.h"
#include <stddef.h>
/* Place scratch memory above static data (transition table ~8.5KB in linear memory 0) */
#define MEM_BASE 65536
#define MEM_STATE MEM_BASE
/* ParserWasmState is ~2.3KB; keep header and op buffers past it */
#define MEM_HEADER (MEM_STATE + 2048)
#define MEM_KINDS (MEM_HEADER + 64)
#define MEM_STARTS (MEM_KINDS + PARSER_MAX_OPS)
#define MEM_LENGTHS (MEM_STARTS + PARSER_MAX_OPS * 4)
#define MEM_AUX (MEM_LENGTHS + PARSER_MAX_OPS * 4)
#define MEM_PARAM_STARTS (MEM_AUX + PARSER_MAX_OPS * 4)
#define MEM_PARAM_COUNTS (MEM_PARAM_STARTS + PARSER_MAX_OPS * 4)
#define MEM_PARAMS_ARENA (MEM_PARAM_COUNTS + PARSER_MAX_OPS * 2) /* uint16 param counts */
#define MEM_INPUT_BASE (MEM_PARAMS_ARENA + PARSER_MAX_PARAMS * 4)
static ParserWasmState *state(void) { return (ParserWasmState *)(uintptr_t)MEM_STATE; }
static ParserScanHeader *header(void) { return (ParserScanHeader *)(uintptr_t)MEM_HEADER; }
static uint8_t *kinds(void) { return (uint8_t *)(uintptr_t)MEM_KINDS; }
static uint32_t *starts(void) { return (uint32_t *)(uintptr_t)MEM_STARTS; }
static uint32_t *lengths(void) { return (uint32_t *)(uintptr_t)MEM_LENGTHS; }
static uint32_t *aux(void) { return (uint32_t *)(uintptr_t)MEM_AUX; }
static uint32_t *param_starts(void) { return (uint32_t *)(uintptr_t)MEM_PARAM_STARTS; }
static uint16_t *param_counts(void) { return (uint16_t *)(uintptr_t)MEM_PARAM_COUNTS; }
static int32_t *params_arena(void) { return (int32_t *)(uintptr_t)MEM_PARAMS_ARENA; }
static uint32_t *input(void) { return (uint32_t *)(uintptr_t)MEM_INPUT_BASE; }
static uint32_t transition_lookup(uint32_t st, uint32_t code) {
uint32_t idx = code < PARSER_NON_ASCII_PRINTABLE ? code : PARSER_NON_ASCII_PRINTABLE;
return PARSER_TRANSITION_TABLE[st << PARSER_TABLE_INDEX_STATE_SHIFT | idx];
}
static void params_reset_zdm(ParserWasmState *s) {
s->params_len = 1;
s->subparams_len = 0;
s->reject_digits = 0;
s->reject_sub_digits = 0;
s->digit_is_sub = 0;
s->subparams_idx[0] = 0;
s->params[0] = 0;
}
static void params_reset(ParserWasmState *s) {
s->params_len = 0;
s->subparams_len = 0;
s->reject_digits = 0;
s->reject_sub_digits = 0;
s->digit_is_sub = 0;
}
static void params_add_param(ParserWasmState *s, int32_t value) {
s->digit_is_sub = 0;
if (s->params_len >= PARSER_PARAMS_MAX_LEN) {
s->reject_digits = 1;
return;
}
s->subparams_idx[s->params_len] = (uint16_t)(s->subparams_len << 8 | s->subparams_len);
if (value < 0) value = 0;
if (value > 0x7FFFFFFF) value = 0x7FFFFFFF;
s->params[s->params_len++] = value;
}
static void params_add_subparam(ParserWasmState *s, int32_t value) {
s->digit_is_sub = 1;
if (!s->params_len) return;
if (s->reject_digits || s->subparams_len >= PARSER_SUBPARAMS_MAX_LEN) {
s->reject_sub_digits = 1;
return;
}
if (value < 0) value = -1;
if (value > 0x7FFFFFFF) value = 0x7FFFFFFF;
s->subparams[s->subparams_len++] = value;
s->subparams_idx[s->params_len - 1]++;
}
static void params_add_digit(ParserWasmState *s, int32_t digit) {
uint32_t len;
if (s->reject_digits) return;
len = s->digit_is_sub ? s->subparams_len : s->params_len;
if (!len) return;
if (s->digit_is_sub && s->reject_sub_digits) return;
int32_t *store = s->digit_is_sub ? s->subparams : s->params;
int32_t cur = store[len - 1];
store[len - 1] = cur >= 0 ? (cur * 10 + digit > 0x7FFFFFFF ? 0x7FFFFFFF : cur * 10 + digit) : digit;
}
static void copy_params_to_arena(uint32_t op_idx, ParserWasmState *s) {
uint32_t start = header()->params_arena_len;
param_starts()[op_idx] = start;
param_counts()[op_idx] = (uint16_t)s->params_len;
for (uint32_t i = 0; i < s->params_len; i++) {
if (header()->params_arena_len >= PARSER_MAX_PARAMS) return;
params_arena()[header()->params_arena_len++] = s->params[i];
uint16_t idx = s->subparams_idx[i];
uint32_t sub_start = idx >> 8;
uint32_t sub_end = idx & 0xFF;
for (uint32_t k = sub_start; k < sub_end; k++) {
if (header()->params_arena_len >= PARSER_MAX_PARAMS) return;
int32_t sv = s->subparams[k];
uint32_t encoded = 0x80000000u | (uint32_t)(sv < 0 ? 0x7FFFFFFF : sv);
params_arena()[header()->params_arena_len++] = encoded;
}
}
}
static int emit_op(uint8_t kind, uint32_t start, uint32_t length, uint32_t aux_val, ParserWasmState *s) {
uint32_t idx = header()->op_count;
if (idx >= PARSER_MAX_OPS) return -1;
kinds()[idx] = kind;
starts()[idx] = start;
lengths()[idx] = length;
aux()[idx] = aux_val;
if (kind == OP_CSI || kind == OP_ESC || kind == OP_DCS_HOOK) {
uint32_t input_pos = start;
copy_params_to_arena(idx, s);
lengths()[idx] = input_pos;
starts()[idx] = param_starts()[idx];
} else {
param_starts()[idx] = 0;
param_counts()[idx] = 0;
}
header()->op_count++;
return (int)idx;
}
static int is_printable(uint32_t code) {
return (code >= 0x20 && code <= 0x7e) || code >= PARSER_NON_ASCII_PRINTABLE;
}
__attribute__((export_name("reset")))
void reset(void) {
ParserWasmState *s = state();
s->current_state = PARSER_STATE_GROUND;
s->collect = 0;
s->preceding_join_state = 0;
s->osc_start = 0;
s->dcs_start = 0;
s->apc_start = 0;
params_reset_zdm(s);
header()->op_count = 0;
header()->input_len = 0;
header()->params_arena_len = 0;
}
__attribute__((export_name("get_input_ptr")))
uint32_t get_input_ptr(void) { return MEM_INPUT_BASE; }
__attribute__((export_name("get_kinds_ptr")))
uint32_t get_kinds_ptr(void) { return MEM_KINDS; }
__attribute__((export_name("get_starts_ptr")))
uint32_t get_starts_ptr(void) { return MEM_STARTS; }
__attribute__((export_name("get_lengths_ptr")))
uint32_t get_lengths_ptr(void) { return MEM_LENGTHS; }
__attribute__((export_name("get_aux_ptr")))
uint32_t get_aux_ptr(void) { return MEM_AUX; }
__attribute__((export_name("get_param_starts_ptr")))
uint32_t get_param_starts_ptr(void) { return MEM_PARAM_STARTS; }
__attribute__((export_name("get_param_counts_ptr")))
uint32_t get_param_counts_ptr(void) { return MEM_PARAM_COUNTS; }
__attribute__((export_name("get_params_ptr")))
uint32_t get_params_ptr(void) { return MEM_PARAMS_ARENA; }
__attribute__((export_name("get_state_ptr")))
uint32_t get_state_ptr(void) { return MEM_STATE; }
__attribute__((export_name("get_header_ptr")))
uint32_t get_header_ptr(void) { return MEM_HEADER; }
__attribute__((export_name("set_state")))
void set_state(uint32_t current_state, uint32_t collect) {
state()->current_state = current_state;
state()->collect = collect;
}
__attribute__((export_name("export_params_to")))
void export_params_to(uint32_t dst_ptr, uint32_t max_len) {
ParserWasmState *s = state();
int32_t *dst = (int32_t *)(uintptr_t)dst_ptr;
uint32_t n = s->params_len < max_len ? s->params_len : max_len;
for (uint32_t i = 0; i < n; i++) {
dst[i] = s->params[i];
}
}
__attribute__((export_name("get_params_len")))
uint32_t get_params_len(void) {
return state()->params_len;
}
__attribute__((export_name("get_subparams_len")))
uint32_t get_subparams_len(void) {
return state()->subparams_len;
}
__attribute__((export_name("get_subparams_ptr")))
uint32_t get_subparams_ptr(void) {
return (uint32_t)(uintptr_t)state()->subparams;
}
__attribute__((export_name("get_subparams_idx_ptr")))
uint32_t get_subparams_idx_ptr(void) {
return (uint32_t)(uintptr_t)state()->subparams_idx;
}
__attribute__((export_name("sync_params_from")))
void sync_params_from(uint32_t src_ptr, uint32_t len) {
ParserWasmState *s = state();
const int32_t *src = (const int32_t *)(uintptr_t)src_ptr;
s->params_len = len > PARSER_PARAMS_MAX_LEN ? PARSER_PARAMS_MAX_LEN : len;
for (uint32_t i = 0; i < s->params_len; i++) {
s->params[i] = src[i];
}
s->subparams_len = 0;
s->reject_digits = 0;
s->reject_sub_digits = 0;
s->digit_is_sub = 0;
}
__attribute__((export_name("probe_action")))
uint32_t probe_action(uint32_t state_id, uint32_t code) {
uint32_t tr = transition_lookup(state_id, code);
return tr >> PARSER_TABLE_ACTION_SHIFT;
}
__attribute__((export_name("scan")))
int32_t scan(uint32_t length) {
ParserWasmState *s = state();
ParserScanHeader *h = header();
h->op_count = 0;
h->params_arena_len = 0;
h->input_len = length;
uint32_t i = 0;
uint32_t code;
uint32_t transition;
uint32_t action;
uint32_t print_start;
while (i < length) {
code = input()[i];
if (code < 0x18 && s->current_state <= PARSER_STATE_CSI_PARAM + 2) {
if (emit_op(OP_EXECUTE, i, 0, code, s) < 0) return -1;
s->preceding_join_state = 0;
i++;
continue;
}
if (code == 0x1b && s->current_state < PARSER_STATE_OSC_STRING && i + 2 < length && input()[i + 1] == 0x5b) {
params_reset_zdm(s);
s->collect = 0;
uint32_t k = i + 2;
uint32_t ch = input()[k];
if (ch >= 0x3c && ch <= 0x3f) {
s->collect = ch;
k++;
}
int csi_done = 0;
for (; k < length; k++) {
ch = input()[k];
if (ch >= 0x30 && ch <= 0x39) {
params_add_digit(s, ch - 48);
} else if (ch == 0x3b) {
params_add_param(s, 0);
} else if (ch == 0x3a) {
params_add_subparam(s, -1);
} else if (ch >= 0x40 && ch <= 0x7e) {
uint32_t ident = (s->collect << 8) | ch;
if (emit_op(OP_CSI, k, 0, ident, s) < 0) return -1;
s->preceding_join_state = 0;
i = k;
s->current_state = PARSER_STATE_GROUND;
csi_done = 1;
break;
} else {
break;
}
}
if (!csi_done) {
i = k - 1;
s->current_state = PARSER_STATE_CSI_PARAM;
}
i++;
continue;
}
transition = transition_lookup(s->current_state, code);
action = transition >> PARSER_TABLE_ACTION_SHIFT;
{
uint32_t next_state = transition & PARSER_TABLE_STATE_MASK;
switch (action) {
case 1: /* ERROR - report state before transition (matches TS parser) */
if (emit_op(OP_ERROR, i, 0, (s->current_state << 16) | code, s) < 0) return -1;
s->current_state = next_state;
break;
default:
s->current_state = next_state;
break;
}
}
switch (action) {
case PARSER_ACTION_PRINT: {
print_start = i;
uint32_t c = i;
uint32_t l4 = length > 4 ? length - 4 : 0;
while (c < l4 && is_printable(input()[c]) && is_printable(input()[c + 1]) &&
is_printable(input()[c + 2]) && is_printable(input()[c + 3])) {
c += 4;
}
while (c < length && is_printable(input()[c])) c++;
if (emit_op(OP_PRINT, print_start, c - print_start, 0, s) < 0) return -1;
i = c - 1;
break;
}
case PARSER_ACTION_EXECUTE:
if (emit_op(OP_EXECUTE, i, 0, code, s) < 0) return -1;
s->preceding_join_state = 0;
break;
case PARSER_ACTION_IGNORE:
break;
case 1: /* ERROR - handled above */
break;
case PARSER_ACTION_CSI_DISPATCH: {
uint32_t ident = (s->collect << 8) | code;
if (emit_op(OP_CSI, i, 0, ident, s) < 0) return -1;
s->preceding_join_state = 0;
break;
}
case 8: /* PARAM */
do {
switch (code) {
case 0x3b: params_add_param(s, 0); break;
case 0x3a: params_add_subparam(s, -1); break;
default: params_add_digit(s, code - 48); break;
}
} while (++i < length && (code = input()[i]) > 0x2f && code < 0x3c);
i--;
break;
case 9: /* COLLECT */
s->collect <<= 8;
s->collect |= code;
break;
case PARSER_ACTION_ESC_DISPATCH: {
uint32_t ident = (s->collect << 8) | code;
if (emit_op(OP_ESC, i, 0, ident, s) < 0) return -1;
s->preceding_join_state = 0;
break;
}
case 11: /* CLEAR */
params_reset_zdm(s);
s->collect = 0;
break;
case 4: /* OSC_START */
if (emit_op(OP_OSC_START, i, 0, 0, s) < 0) return -1;
s->osc_start = i + 1;
break;
case 5: /* OSC_PUT */
for (uint32_t j = i + 1; ; j++) {
if (j >= length || (code = input()[j]) < 0x20 ||
(code > 0x7f && code < PARSER_NON_ASCII_PRINTABLE)) {
if (j > i && emit_op(OP_OSC_PUT, i, j - i, 0, s) < 0) return -1;
i = j - 1;
break;
}
}
break;
case PARSER_ACTION_OSC_END: {
if (emit_op(OP_OSC_END, i, 0, code, s) < 0) return -1;
s->osc_term = code;
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
params_reset_zdm(s);
s->collect = 0;
s->preceding_join_state = 0;
break;
}
case 12: /* DCS_HOOK */
s->dcs_hook_ident = (s->collect << 8) | code;
if (emit_op(OP_DCS_HOOK, i, 0, s->dcs_hook_ident, s) < 0) return -1;
s->dcs_start = i + 1;
break;
case 13: /* DCS_PUT */
for (uint32_t j = i + 1; ; j++) {
if (j >= length || (code = input()[j]) == 0x18 || code == 0x1a || code == 0x1b ||
(code > 0x7f && code < PARSER_NON_ASCII_PRINTABLE)) {
if (j > i && emit_op(OP_DCS_PUT, i, j - i, 0, s) < 0) return -1;
i = j - 1;
break;
}
}
break;
case PARSER_ACTION_DCS_UNHOOK: {
if (emit_op(OP_DCS_UNHOOK, i, 0, code, s) < 0) return -1;
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
params_reset_zdm(s);
s->collect = 0;
s->preceding_join_state = 0;
break;
}
case 15: /* APC_START */
s->apc_hook_ident = (s->collect << 8) | code;
if (emit_op(OP_APC_START, i, 0, s->apc_hook_ident, s) < 0) return -1;
s->apc_start = i + 1;
break;
case 16: /* APC_PUT */
for (uint32_t j = i + 1; ; j++) {
if (j < length && (
(input()[j] >= 0x20 && input()[j] < 0x7f) ||
(input()[j] >= 0x08 && input()[j] < 0x0e) ||
input()[j] >= PARSER_NON_ASCII_PRINTABLE
)) continue;
if (j > i && emit_op(OP_APC_PUT, i, j - i, 0, s) < 0) return -1;
i = j - 1;
break;
}
break;
case PARSER_ACTION_APC_END: {
if (emit_op(OP_APC_END, i, 0, code, s) < 0) return -1;
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
params_reset_zdm(s);
s->collect = 0;
s->preceding_join_state = 0;
break;
}
default:
break;
}
i++;
}
return (int32_t)h->op_count;
}
+79
View File
@@ -0,0 +1,79 @@
/* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
#ifndef PARSER_WASM_H
#define PARSER_WASM_H
#include <stdint.h>
#define PARSER_NON_ASCII_PRINTABLE 0xA0
#define PARSER_TABLE_ACTION_SHIFT 8
#define PARSER_TABLE_STATE_MASK 255
#define PARSER_TABLE_INDEX_STATE_SHIFT 8
#define OP_PRINT 0
#define OP_EXECUTE 1
#define OP_ESC 2
#define OP_CSI 3
#define OP_OSC 4
#define OP_DCS 5
#define OP_APC 6
#define OP_DCS_HOOK 7
#define OP_DCS_PUT 8
#define OP_DCS_UNHOOK 9
#define OP_OSC_START 10
#define OP_OSC_PUT 11
#define OP_OSC_END 12
#define OP_APC_START 13
#define OP_APC_PUT 14
#define OP_APC_END 15
#define OP_ERROR 16
#define PARSER_STATE_GROUND 0
#define PARSER_STATE_ESCAPE 1
#define PARSER_STATE_OSC_STRING 8
#define PARSER_STATE_DCS_PASSTHROUGH 13
#define PARSER_STATE_APC_PASSTHROUGH 16
#define PARSER_STATE_CSI_PARAM 4
#define PARSER_ACTION_IGNORE 0
#define PARSER_ACTION_PRINT 2
#define PARSER_ACTION_EXECUTE 3
#define PARSER_ACTION_CSI_DISPATCH 7
#define PARSER_ACTION_ESC_DISPATCH 10
#define PARSER_ACTION_OSC_END 6
#define PARSER_ACTION_DCS_UNHOOK 14
#define PARSER_ACTION_APC_END 17
#define PARSER_MAX_OPS 4096
#define PARSER_MAX_PARAMS 4096
#define PARSER_PARAMS_MAX_LEN 32
#define PARSER_SUBPARAMS_MAX_LEN 256
typedef struct {
uint32_t current_state;
uint32_t collect;
uint32_t preceding_join_state;
int32_t params[PARSER_PARAMS_MAX_LEN];
uint32_t params_len;
int32_t subparams[PARSER_SUBPARAMS_MAX_LEN];
uint32_t subparams_len;
uint16_t subparams_idx[PARSER_PARAMS_MAX_LEN];
uint32_t reject_digits;
uint32_t reject_sub_digits;
uint32_t digit_is_sub;
uint32_t osc_start;
uint32_t dcs_start;
uint32_t apc_start;
uint32_t dcs_hook_ident;
uint32_t apc_hook_ident;
uint32_t osc_term;
} ParserWasmState;
typedef struct {
uint32_t op_count;
uint32_t input_len;
uint32_t params_arena_len;
} ParserScanHeader;
#endif
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
+279
View File
@@ -0,0 +1,279 @@
/* Auto-generated by bin/gen_parser_transition_table.mjs - do not edit */
#ifndef PARSER_TRANSITION_TABLE_H
#define PARSER_TRANSITION_TABLE_H
#include <stdint.h>
#define PARSER_TABLE_SIZE 4257
static const uint16_t PARSER_TRANSITION_TABLE[PARSER_TABLE_SIZE] = {
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0xb01, 0x300, 0x300, 0x300, 0x300,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200,
0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x100,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x200, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301,
0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x301, 0x300, 0x301, 0x300, 0xb01, 0x301, 0x301, 0x301, 0x301,
0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xb09, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0x7, 0xa00, 0xa00, 0xb03, 0xa00, 0x408, 0x7, 0xb0e,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0x1,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302,
0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x302, 0x300, 0x302, 0x300, 0xb01, 0x302, 0x302, 0x302, 0x302,
0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902, 0x902,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00,
0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0xa00, 0x2,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303,
0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x300, 0x303, 0x300, 0xb01, 0x303, 0x303, 0x303, 0x303,
0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905,
0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x904, 0x904, 0x904, 0x904,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x3,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304,
0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x304, 0x300, 0x304, 0x300, 0xb01, 0x304, 0x304, 0x304, 0x304,
0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905,
0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x804, 0x6, 0x6, 0x6, 0x6,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x4,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305,
0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x305, 0x300, 0x305, 0x300, 0xb01, 0x305, 0x305, 0x305, 0x305,
0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905, 0x905,
0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700,
0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x5,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306,
0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x306, 0x300, 0x306, 0x300, 0xb01, 0x306, 0x306, 0x306, 0x306,
0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x6, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x300, 0x7, 0x300, 0xb01, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x600, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x600, 0x8, 0x600, 0x600, 0x8, 0x8, 0x8, 0x8,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508, 0x508,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x600, 0x408, 0x7, 0xb0e,
0x508, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9,
0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x300, 0x9, 0x300, 0xb01, 0x9, 0x9, 0x9, 0x9,
0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c,
0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x90a, 0x90a, 0x90a, 0x90a,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0x9,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa,
0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0x300, 0xa, 0x300, 0xb01, 0xa, 0xa, 0xa, 0xa,
0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c,
0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0x80a, 0xb, 0xb, 0xb, 0xb,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xa,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0x300, 0xb, 0x300, 0xb01, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0xb, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc,
0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0x300, 0xc, 0x300, 0xb01, 0xc, 0xc, 0xc, 0xc,
0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c, 0x90c,
0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d,
0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc0d, 0xc,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xe00, 0xd0d, 0xe00, 0xe00, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d,
0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd0d, 0xd,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0xe00, 0x408, 0x7, 0xb0e,
0xd0d, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe,
0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0x300, 0xe, 0x300, 0xb01, 0xe, 0xe, 0xe, 0xe,
0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xe,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x300, 0xf, 0x300, 0xb01, 0xf, 0xf, 0xf, 0xf,
0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f, 0x90f,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10,
0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf10, 0xf,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x0, 0x408, 0x7, 0xb0e,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1100, 0x10, 0x1100, 0x1100, 0x10, 0x10, 0x10, 0x10,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010,
0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x10,
0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300,
0xb09, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x300, 0x7, 0x300, 0x300, 0xb03, 0x1100, 0x408, 0x7, 0xb0e,
0x1010
};
#endif