Merge pull request #5825 from homanp/perf/csi-fast-path
perf: CSI fast-path (+42% throughput)
This commit is contained in:
@@ -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]();
|
||||
|
||||
Reference in New Issue
Block a user