fix(parser): chunked WASM scan, op coalescing, and benchmark runtime paths
The WASM scanner could fill its fixed 4096-op buffer on large inputs (e.g. 5MB of newlines or OSC traffic) and previously failed or appeared hung. ESC op coalescing also corrupted the lengths field used for dispatch, causing millions of redundant handler invocations on OSC benchmark data. WASM scanner (parser.c / parser.h): - Add scan_offset to ParserScanHeader; scan(offset, length) resumes after a partial fill instead of returning -1 for the whole buffer. - Return early from scan when the op buffer is full; all emit sites use emit_or_stop() so scan_offset records where to continue. - Coalesce consecutive OP_EXECUTE and OP_ESC ops with the same code/ident; lengths holds repeat count, starts holds the input index for ESC. - Keep CSI/DCS hook layout unchanged (lengths = final byte index, starts = param arena offset). TypeScript dispatch (EscapeSequenceParserWasm.ts): - Drive parsing via runWasmScanChunks(): scan a slice, dispatch ops, advance inputOffset to nextOffset until the chunk is consumed. - Fix ESC dispatch: chunkPos uses starts (input index); escCount uses lengths (repeat count). Avoid treating the CSI-style final-byte index as a loop bound. - Throw if nextOffset does not advance to catch stuck scans. EscapeSequenceParser.ts: - Extend _scanCache for chunked scan state; reset() clears inputOffset and cached scan data so later parses are not skipped. Benchmarks: - Import compiled modules from ../../out/common and ../../out/browser so npm run benchmark works without NODE_PATH after path-mapping removal. - Update benchmark skill notes accordingly. Build: - build_parser_wasm.sh selects a clang with wasm32 support (Homebrew LLVM on macOS when Xcode clang lacks the target). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -25,4 +25,4 @@ When writing benchmark instructions:
|
||||
## Notes
|
||||
|
||||
- Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`.
|
||||
- Keep `NODE_PATH=./out` (handled by the npm script).
|
||||
- Benchmark sources import compiled modules from `../../out/common/` and `../../out/browser/` (run `npm run build` first).
|
||||
|
||||
@@ -13,7 +13,48 @@ fi
|
||||
WASM_DIR=src/common/parser/wasm
|
||||
OUT="$WASM_DIR/parser.wasm"
|
||||
|
||||
clang --target=wasm32-unknown-unknown -O2 -nostdlib -fuse-ld=lld \
|
||||
# Apple Clang (Xcode CLT) has no wasm32 target; prefer Homebrew LLVM when needed.
|
||||
wasm_clang_has_target() {
|
||||
"$1" --print-targets 2>/dev/null | grep -q 'wasm32'
|
||||
}
|
||||
|
||||
resolve_wasm_toolchain() {
|
||||
local clang_bin=clang
|
||||
if wasm_clang_has_target clang; then
|
||||
:
|
||||
else
|
||||
local llvm_prefix=""
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
llvm_prefix="$(brew --prefix llvm 2>/dev/null || true)"
|
||||
fi
|
||||
for prefix in "$llvm_prefix" /opt/homebrew/opt/llvm /usr/local/opt/llvm; do
|
||||
if [[ -n "$prefix" && -x "$prefix/bin/clang" ]] && wasm_clang_has_target "$prefix/bin/clang"; then
|
||||
clang_bin="$prefix/bin/clang"
|
||||
local lld_prefix=""
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
lld_prefix="$(brew --prefix lld 2>/dev/null || true)"
|
||||
fi
|
||||
for lld_dir in "$lld_prefix/bin" /opt/homebrew/opt/lld/bin /usr/local/opt/lld/bin; do
|
||||
if [[ -n "$lld_dir" && -x "$lld_dir/lld" ]]; then
|
||||
PATH="$lld_dir:$(dirname "$clang_bin"):$PATH"
|
||||
break
|
||||
fi
|
||||
done
|
||||
PATH="$(dirname "$clang_bin"):$PATH"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if ! wasm_clang_has_target "$clang_bin"; then
|
||||
echo "error: no clang with wasm32 support found (macOS: brew install llvm lld)" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
WASM_CLANG="$clang_bin"
|
||||
}
|
||||
|
||||
resolve_wasm_toolchain
|
||||
|
||||
"$WASM_CLANG" --target=wasm32-unknown-unknown -O2 -nostdlib -fuse-ld=lld \
|
||||
-Wl,--no-entry \
|
||||
-Wl,--export-memory \
|
||||
-Wl,--export=reset \
|
||||
|
||||
@@ -302,7 +302,14 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
chunkPos: 0
|
||||
};
|
||||
|
||||
protected _scanCache: { opIndex: number, stateBeforeScan?: number } = { opIndex: 0 };
|
||||
protected _scanCache: {
|
||||
opIndex: number;
|
||||
stateBeforeScan?: number;
|
||||
inputOffset?: number;
|
||||
chunkStart?: number;
|
||||
scan?: import('./ScanTypes').ScanResult;
|
||||
data?: Uint32Array;
|
||||
} = { opIndex: 0 };
|
||||
|
||||
constructor(
|
||||
protected readonly _transitions: TransitionTable = VT500_TRANSITION_TABLE
|
||||
@@ -510,6 +517,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._collect = 0;
|
||||
this.precedingJoinState = 0;
|
||||
this._scanCache.opIndex = 0;
|
||||
this._scanCache.inputOffset = 0;
|
||||
this._scanCache.chunkStart = undefined;
|
||||
this._scanCache.scan = undefined;
|
||||
this._scanCache.data = undefined;
|
||||
this._scanCache.stateBeforeScan = undefined;
|
||||
// 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
|
||||
|
||||
@@ -76,7 +76,7 @@ export function parseWithWasmScanner(
|
||||
data: Uint32Array,
|
||||
length: number,
|
||||
promiseResult: boolean | undefined,
|
||||
scanCache: { scan?: ScanResult; data?: Uint32Array; opIndex: number; stateBeforeScan?: number }
|
||||
scanCache: IWasmScanCache
|
||||
): void | Promise<boolean> {
|
||||
ensureWasm();
|
||||
|
||||
@@ -85,18 +85,62 @@ export function parseWithWasmScanner(
|
||||
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.stateBeforeScan = host.currentState;
|
||||
scanCache.inputOffset = 0;
|
||||
scanCache.scan = undefined;
|
||||
scanCache.opIndex = 0;
|
||||
return runWasmScanChunks(host, data, length, promiseResult, scanCache);
|
||||
}
|
||||
|
||||
host.currentState = WasmEscapeScanner.currentState;
|
||||
host.collect = WasmEscapeScanner.collect;
|
||||
interface IWasmScanCache {
|
||||
scan?: ScanResult;
|
||||
data?: Uint32Array;
|
||||
opIndex: number;
|
||||
stateBeforeScan?: number;
|
||||
inputOffset?: number;
|
||||
chunkStart?: number;
|
||||
}
|
||||
|
||||
return dispatchScanOps(host, data, scan, 0, scanCache, promiseResult, stateBeforeScan);
|
||||
function runWasmScanChunks(
|
||||
host: IWasmParseHost,
|
||||
data: Uint32Array,
|
||||
length: number,
|
||||
promiseResult: boolean | undefined,
|
||||
scanCache: IWasmScanCache
|
||||
): void | Promise<boolean> {
|
||||
let pos = scanCache.inputOffset ?? 0;
|
||||
const stateBeforeScan = scanCache.stateBeforeScan ?? host.currentState;
|
||||
|
||||
while (pos < length) {
|
||||
if (!scanCache.scan || scanCache.data !== data || scanCache.chunkStart !== pos) {
|
||||
WasmEscapeScanner.syncParserState(host.currentState, host.collect, host._params);
|
||||
const scan = WasmEscapeScanner.scan(data, length, pos);
|
||||
scanCache.scan = scan;
|
||||
scanCache.data = data;
|
||||
scanCache.chunkStart = pos;
|
||||
scanCache.opIndex = 0;
|
||||
host.currentState = WasmEscapeScanner.currentState;
|
||||
host.collect = WasmEscapeScanner.collect;
|
||||
}
|
||||
|
||||
const result = dispatchScanOps(host, data, scanCache.scan, scanCache.opIndex, scanCache, promiseResult, stateBeforeScan);
|
||||
if (result instanceof Promise) {
|
||||
scanCache.inputOffset = pos;
|
||||
return result;
|
||||
}
|
||||
|
||||
const nextOffset = scanCache.scan.nextOffset;
|
||||
if (nextOffset <= pos) {
|
||||
throw new Error(`Wasm parser scan did not advance (stuck at input offset ${pos})`);
|
||||
}
|
||||
pos = nextOffset;
|
||||
scanCache.inputOffset = pos;
|
||||
scanCache.scan = undefined;
|
||||
scanCache.opIndex = 0;
|
||||
}
|
||||
|
||||
scanCache.inputOffset = length;
|
||||
scanCache.scan = undefined;
|
||||
}
|
||||
|
||||
function resumeWasmParse(
|
||||
@@ -104,7 +148,7 @@ function resumeWasmParse(
|
||||
data: Uint32Array,
|
||||
length: number,
|
||||
promiseResult: boolean | undefined,
|
||||
scanCache: { scan?: ScanResult; data?: Uint32Array; opIndex: number; stateBeforeScan?: number }
|
||||
scanCache: IWasmScanCache
|
||||
): void | Promise<boolean> {
|
||||
if (host._parseStack.state === ParserStackType.RESET) {
|
||||
host._parseStack.state = ParserStackType.NONE;
|
||||
@@ -113,7 +157,7 @@ function resumeWasmParse(
|
||||
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);
|
||||
return runWasmScanChunks(host, data, length, promiseResult, scanCache);
|
||||
}
|
||||
|
||||
if (promiseResult === undefined || host._parseStack.state === ParserStackType.FAIL) {
|
||||
@@ -194,7 +238,7 @@ function resumeWasmParse(
|
||||
|
||||
host._parseStack.state = ParserStackType.NONE;
|
||||
host.precedingJoinState = 0;
|
||||
return dispatchScanOps(host, data, scan, scanCache.opIndex, scanCache, promiseResult, scanCache.stateBeforeScan ?? host.currentState);
|
||||
return runWasmScanChunks(host, data, length, promiseResult, scanCache);
|
||||
}
|
||||
|
||||
function dispatchScanOps(
|
||||
@@ -202,7 +246,7 @@ function dispatchScanOps(
|
||||
data: Uint32Array,
|
||||
scan: ScanResult,
|
||||
fromOp: number,
|
||||
scanCache: { scan?: ScanResult; data?: Uint32Array; opIndex: number },
|
||||
scanCache: IWasmScanCache,
|
||||
promiseResult: boolean | undefined,
|
||||
stateBeforeScan: number
|
||||
): void | Promise<boolean> {
|
||||
@@ -214,7 +258,7 @@ function dispatchScanOps(
|
||||
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;
|
||||
const chunkPos = kind === Op.Csi ? len : start;
|
||||
|
||||
switch (kind) {
|
||||
case Op.Print:
|
||||
@@ -222,14 +266,17 @@ function dispatchScanOps(
|
||||
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);
|
||||
const count = len > 0 ? len : 1;
|
||||
for (let n = 0; n < count; n++) {
|
||||
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;
|
||||
@@ -255,16 +302,19 @@ function dispatchScanOps(
|
||||
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;
|
||||
const escCount = len > 0 ? len : 1;
|
||||
escLoop: for (let rep = 0; rep < escCount; rep++) {
|
||||
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
|
||||
for (; jj >= 0; jj--) {
|
||||
handlerResult = handlersEsc![jj]();
|
||||
if (handlerResult === true) continue escLoop;
|
||||
if (handlerResult instanceof Promise) {
|
||||
host._preserveStack(ParserStackType.ESC, handlersEsc!, jj, 0, chunkPos);
|
||||
return handlerResult;
|
||||
}
|
||||
}
|
||||
if (jj < 0) host._escHandlerFb(aux);
|
||||
}
|
||||
if (jj < 0) host._escHandlerFb(aux);
|
||||
host.precedingJoinState = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -34,4 +34,5 @@ export type ScanResult = {
|
||||
paramStarts: Uint32Array;
|
||||
paramCounts: Uint16Array;
|
||||
opCount: number;
|
||||
nextOffset: number;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ let wasmModule: WebAssembly.Instance | undefined;
|
||||
let wasmExports: {
|
||||
memory: WebAssembly.Memory;
|
||||
reset: () => void;
|
||||
scan: (length: number) => number;
|
||||
scan: (offset: number, length: number) => number;
|
||||
set_state: (state: number, collect: number) => void;
|
||||
get_input_ptr: () => number;
|
||||
get_kinds_ptr: () => number;
|
||||
@@ -162,14 +162,14 @@ export class WasmEscapeScanner {
|
||||
}
|
||||
}
|
||||
|
||||
public static scan(data: Uint32Array, length: number): ScanResult {
|
||||
public static scan(data: Uint32Array, length: number, offset = 0): 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);
|
||||
const opCount = ex.scan(offset, length);
|
||||
if (opCount < 0) {
|
||||
throw new Error('Wasm parser op buffer overflow');
|
||||
}
|
||||
@@ -182,9 +182,9 @@ export class WasmEscapeScanner {
|
||||
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 headerView = new Uint32Array(mem, ex.get_header_ptr(), 4);
|
||||
const paramsLen = headerView[2];
|
||||
const nextOffset = headerView[3];
|
||||
|
||||
const params = new Uint32Array(mem, ex.get_params_ptr(), paramsLen);
|
||||
|
||||
@@ -196,7 +196,8 @@ export class WasmEscapeScanner {
|
||||
params: params.slice(),
|
||||
paramStarts: paramStarts.slice(),
|
||||
paramCounts: paramCounts.slice(),
|
||||
opCount: count
|
||||
opCount: count,
|
||||
nextOffset
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,16 +110,37 @@ static void copy_params_to_arena(uint32_t op_idx, ParserWasmState *s) {
|
||||
|
||||
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 > 0) {
|
||||
uint32_t prev = idx - 1;
|
||||
if (kind == OP_EXECUTE && kinds()[prev] == OP_EXECUTE && aux()[prev] == aux_val) {
|
||||
lengths()[prev]++;
|
||||
return (int)prev;
|
||||
}
|
||||
if (kind == OP_ESC && kinds()[prev] == OP_ESC && aux()[prev] == aux_val) {
|
||||
lengths()[prev]++;
|
||||
return (int)prev;
|
||||
}
|
||||
}
|
||||
if (idx >= PARSER_MAX_OPS) return -1;
|
||||
kinds()[idx] = kind;
|
||||
starts()[idx] = start;
|
||||
lengths()[idx] = length;
|
||||
if ((kind == OP_EXECUTE || kind == OP_ESC) && length == 0) {
|
||||
lengths()[idx] = 1;
|
||||
} else {
|
||||
lengths()[idx] = length;
|
||||
}
|
||||
aux()[idx] = aux_val;
|
||||
if (kind == OP_CSI || kind == OP_ESC || kind == OP_DCS_HOOK) {
|
||||
if (kind == OP_CSI || 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 if (kind == OP_ESC) {
|
||||
uint32_t input_pos = start;
|
||||
uint32_t repeat = lengths()[idx];
|
||||
copy_params_to_arena(idx, s);
|
||||
starts()[idx] = input_pos;
|
||||
lengths()[idx] = repeat;
|
||||
} else {
|
||||
param_starts()[idx] = 0;
|
||||
param_counts()[idx] = 0;
|
||||
@@ -145,6 +166,7 @@ void reset(void) {
|
||||
header()->op_count = 0;
|
||||
header()->input_len = 0;
|
||||
header()->params_arena_len = 0;
|
||||
header()->scan_offset = 0;
|
||||
}
|
||||
|
||||
__attribute__((export_name("get_input_ptr")))
|
||||
@@ -233,14 +255,22 @@ uint32_t probe_action(uint32_t state_id, uint32_t code) {
|
||||
return tr >> PARSER_TABLE_ACTION_SHIFT;
|
||||
}
|
||||
|
||||
static int emit_or_stop(uint8_t kind, uint32_t start, uint32_t length, uint32_t aux_val, ParserWasmState *s, uint32_t stop_at) {
|
||||
if (emit_op(kind, start, length, aux_val, s) < 0) {
|
||||
header()->scan_offset = stop_at;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((export_name("scan")))
|
||||
int32_t scan(uint32_t length) {
|
||||
int32_t scan(uint32_t offset, 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 i = offset;
|
||||
uint32_t code;
|
||||
uint32_t transition;
|
||||
uint32_t action;
|
||||
@@ -250,7 +280,9 @@ int32_t scan(uint32_t 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;
|
||||
if (!emit_or_stop(OP_EXECUTE, i, 0, code, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->preceding_join_state = 0;
|
||||
i++;
|
||||
continue;
|
||||
@@ -276,7 +308,9 @@ int32_t scan(uint32_t length) {
|
||||
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;
|
||||
if (!emit_or_stop(OP_CSI, k, 0, ident, s, k)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->preceding_join_state = 0;
|
||||
i = k;
|
||||
s->current_state = PARSER_STATE_GROUND;
|
||||
@@ -300,7 +334,9 @@ int32_t scan(uint32_t length) {
|
||||
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;
|
||||
if (!emit_or_stop(OP_ERROR, i, 0, (s->current_state << 16) | code, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->current_state = next_state;
|
||||
break;
|
||||
default:
|
||||
@@ -319,12 +355,16 @@ int32_t scan(uint32_t length) {
|
||||
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;
|
||||
if (!emit_or_stop(OP_PRINT, print_start, c - print_start, 0, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
i = c - 1;
|
||||
break;
|
||||
}
|
||||
case PARSER_ACTION_EXECUTE:
|
||||
if (emit_op(OP_EXECUTE, i, 0, code, s) < 0) return -1;
|
||||
if (!emit_or_stop(OP_EXECUTE, i, 0, code, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->preceding_join_state = 0;
|
||||
break;
|
||||
case PARSER_ACTION_IGNORE:
|
||||
@@ -333,7 +373,9 @@ int32_t scan(uint32_t length) {
|
||||
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;
|
||||
if (!emit_or_stop(OP_CSI, i, 0, ident, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->preceding_join_state = 0;
|
||||
break;
|
||||
}
|
||||
@@ -353,7 +395,9 @@ int32_t scan(uint32_t length) {
|
||||
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;
|
||||
if (!emit_or_stop(OP_ESC, i, 0, ident, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->preceding_join_state = 0;
|
||||
break;
|
||||
}
|
||||
@@ -362,21 +406,27 @@ int32_t scan(uint32_t length) {
|
||||
s->collect = 0;
|
||||
break;
|
||||
case 4: /* OSC_START */
|
||||
if (emit_op(OP_OSC_START, i, 0, 0, s) < 0) return -1;
|
||||
if (!emit_or_stop(OP_OSC_START, i, 0, 0, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -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;
|
||||
if (j > i && !emit_or_stop(OP_OSC_PUT, i, j - i, 0, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
i = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PARSER_ACTION_OSC_END: {
|
||||
if (emit_op(OP_OSC_END, i, 0, code, s) < 0) return -1;
|
||||
if (!emit_or_stop(OP_OSC_END, i, 0, code, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->osc_term = code;
|
||||
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
|
||||
params_reset_zdm(s);
|
||||
@@ -386,21 +436,27 @@ int32_t scan(uint32_t length) {
|
||||
}
|
||||
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;
|
||||
if (!emit_or_stop(OP_DCS_HOOK, i, 0, s->dcs_hook_ident, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -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;
|
||||
if (j > i && !emit_or_stop(OP_DCS_PUT, i, j - i, 0, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -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 (!emit_or_stop(OP_DCS_UNHOOK, i, 0, code, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
|
||||
params_reset_zdm(s);
|
||||
s->collect = 0;
|
||||
@@ -409,7 +465,9 @@ int32_t scan(uint32_t length) {
|
||||
}
|
||||
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;
|
||||
if (!emit_or_stop(OP_APC_START, i, 0, s->apc_hook_ident, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
s->apc_start = i + 1;
|
||||
break;
|
||||
case 16: /* APC_PUT */
|
||||
@@ -419,13 +477,17 @@ int32_t scan(uint32_t length) {
|
||||
(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;
|
||||
if (j > i && !emit_or_stop(OP_APC_PUT, i, j - i, 0, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -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 (!emit_or_stop(OP_APC_END, i, 0, code, s, i)) {
|
||||
return h->op_count > 0 ? (int32_t)h->op_count : -1;
|
||||
}
|
||||
if (code == 0x1b) s->current_state = PARSER_STATE_ESCAPE;
|
||||
params_reset_zdm(s);
|
||||
s->collect = 0;
|
||||
@@ -437,5 +499,6 @@ int32_t scan(uint32_t length) {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
h->scan_offset = length;
|
||||
return (int32_t)h->op_count;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ typedef struct {
|
||||
uint32_t op_count;
|
||||
uint32_t input_len;
|
||||
uint32_t params_arena_len;
|
||||
uint32_t scan_offset;
|
||||
} ParserScanHeader;
|
||||
|
||||
#endif
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
import { perfContext, before, RuntimeCase } from 'xterm-benchmark';
|
||||
import { DecorationService } from '../../src/common/services/DecorationService';
|
||||
import { MockLogService, MockBufferService, MockOptionsService } from '../../src/common/TestUtils.test';
|
||||
import { DecorationService } from '../../out/common/services/DecorationService';
|
||||
import { MockLogService, MockBufferService, MockOptionsService } from '../../out/common/TestUtils.test';
|
||||
const enum Constants {
|
||||
COLS = 80,
|
||||
ROWS = 30,
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
*/
|
||||
import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-benchmark';
|
||||
|
||||
import { EscapeSequenceParser } from '../../src/common/parser/EscapeSequenceParser';
|
||||
import { C0, C1 } from '../../src/common/data/EscapeSequences';
|
||||
import { IDcsHandler, IOscHandler, IApcHandler, IParams } from '../../src/common/parser/Types';
|
||||
import { OscHandler } from '../../src/common/parser/OscParser';
|
||||
import { DcsHandler } from '../../src/common/parser/DcsParser';
|
||||
import { ApcHandler } from '../../src/common/parser/ApcParser';
|
||||
import { EscapeSequenceParser } from '../../out/common/parser/EscapeSequenceParser';
|
||||
import { C0, C1 } from '../../out/common/data/EscapeSequences';
|
||||
import { IDcsHandler, IOscHandler, IApcHandler, IParams } from '../../out/common/parser/Types';
|
||||
import { OscHandler } from '../../out/common/parser/OscParser';
|
||||
import { DcsHandler } from '../../out/common/parser/DcsParser';
|
||||
import { ApcHandler } from '../../out/common/parser/ApcParser';
|
||||
|
||||
const SIZE = 5000000;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { perfContext, before, RuntimeCase } from 'xterm-benchmark';
|
||||
import { Emitter } from '../../src/common/Event';
|
||||
import { Emitter } from '../../out/common/Event';
|
||||
|
||||
const ITERATIONS = 1_000_000;
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
|
||||
|
||||
import { spawn } from 'node-pty';
|
||||
import { Utf8ToUtf32, stringFromCodePoint } from '../../src/common/input/TextDecoder';
|
||||
import { CoreBrowserTerminal } from '../../src/browser/CoreBrowserTerminal';
|
||||
import { Utf8ToUtf32, stringFromCodePoint } from '../../out/common/input/TextDecoder';
|
||||
import { CoreBrowserTerminal } from '../../out/browser/CoreBrowserTerminal';
|
||||
|
||||
perfContext('Terminal: ls -lR /usr/lib', () => {
|
||||
let content = '';
|
||||
|
||||
Reference in New Issue
Block a user