diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index febb4878c..8cb6d39f3 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -506,6 +506,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP * - OSC_STRING:OSC_PUT * - DCS_PASSTHROUGH:DCS_PUT * + * Additionally the following fast paths exist before the table lookup: + * - EXE bytes < 0x18 in non-payload states (avoids table lookup entirely) + * - 7-bit CSI sequences without intermediates (ESC [ params final) + * * Note on asynchronous handler support: * Any handler returning a promise will be treated as asynchronous. * To keep the in-band blocking working for async handlers, `parse` pauses execution, @@ -649,34 +653,89 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP 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) { + if (this._executeHandlers[code]) this._executeHandlers[code](); + else 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.reset(); + this._params.addParam(0); // ZDM + 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 < 0xa0 ? code : NON_ASCII_PRINTABLE)]; switch (transition >> TableAccess.TRANSITION_ACTION_SHIFT) { case ParserAction.PRINT: - // read ahead with loop unrolling // Note: 0x20 (SP) is included, 0x7F (DEL) is excluded - for (let j = i + 1; ; ++j) { - if (j >= length || (code = data[j]) < 0x20 || (code > 0x7e && code < NON_ASCII_PRINTABLE)) { - this._printHandler(data, i, j); - i = j - 1; - break; - } - if (++j >= length || (code = data[j]) < 0x20 || (code > 0x7e && code < NON_ASCII_PRINTABLE)) { - this._printHandler(data, i, j); - i = j - 1; - break; - } - if (++j >= length || (code = data[j]) < 0x20 || (code > 0x7e && code < NON_ASCII_PRINTABLE)) { - this._printHandler(data, i, j); - i = j - 1; - break; - } - if (++j >= length || (code = data[j]) < 0x20 || (code > 0x7e && code < NON_ASCII_PRINTABLE)) { - this._printHandler(data, i, j); - i = j - 1; - break; + 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]();