Skip to content

Commit

Permalink
Merge pull request #2187 from jerch/parser_inner_loops
Browse files Browse the repository at this point in the history
relocate parser look ahead loops
  • Loading branch information
jerch authored Jun 5, 2019
2 parents 91ec163 + cc05dcf commit 69ad755
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 66 deletions.
2 changes: 0 additions & 2 deletions src/core/parser/EscapeSequenceParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1340,8 +1340,6 @@ describe('EscapeSequenceParser', function (): void {
position: 6,
code: '€'.charCodeAt(0),
currentState: ParserState.CSI_PARAM,
print: -1,
dcs: -1,
osc: '',
collect: '',
params: [1, 2, 0], // extra zero here
Expand Down
116 changes: 56 additions & 60 deletions src/core/parser/EscapeSequenceParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
const r = (start: number, end: number) => blueprint.slice(start, end);

// Default definitions.
const PRINTABLES = r(0x20, 0x7f);
const PRINTABLES = r(0x20, 0x7f); // 0x20 (SP) included, 0x7F (DEL) excluded
const EXECUTABLES = r(0x00, 0x18);
EXECUTABLES.push(0x19);
EXECUTABLES.push.apply(EXECUTABLES, r(0x1c, 0x20));
Expand Down Expand Up @@ -396,12 +396,22 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._activeDcsHandler = null;
}

/**
* Parse UTF32 codepoints in `data` up to `length`.
* Note: For several actions with high data load the parsing is optimized
* by using local read ahead loops with hardcoded conditions to
* avoid costly table lookups. Make sure that any change of table values
* will be reflected in the loop conditions as well. Affected states/actions:
* - GROUND:PRINT
* - CSI_PARAM:PARAM
* - DCS_PARAM:PARAM
* - OSC_STRING:OSC_PUT
* - DCS_PASSTHROUGH:DCS_PUT
*/
parse(data: Uint32Array, length: number): void {
let code = 0;
let transition = 0;
let currentState = this.currentState;
let print = -1;
let dcs = -1;
let osc = this._osc;
let collect = this._collect;
let params = this._params;
Expand All @@ -413,56 +423,48 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
for (let i = 0; i < length; ++i) {
code = data[i];

// shortcut for most chars (print action)
if (currentState === ParserState.GROUND && code > 0x1f && code < 0x80) {
print = (~print) ? print : i;
do i++;
while (i < length && data[i] > 0x1f && data[i] < 0x80);
i--;
continue;
}

// shortcut for CSI params
if (currentState === ParserState.CSI_PARAM && (code > 0x2f && code < 0x39)) {
params[params.length - 1] = params[params.length - 1] * 10 + code - 48;
continue;
}

// normal transition & action lookup
transition = table[currentState << TableAccess.INDEX_STATE_SHIFT | (code < 0xa0 ? code : NON_ASCII_PRINTABLE)];
switch (transition >> TableAccess.TRANSITION_ACTION_SHIFT) {
case ParserAction.PRINT:
print = (~print) ? print : i;
// 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;
}
}
break;
case ParserAction.EXECUTE:
if (~print) {
this._printHandler(data, print, i);
print = -1;
}
callback = this._executeHandlers[code];
if (callback) callback();
else this._executeHandlerFb(code);
break;
case ParserAction.IGNORE:
// handle leftover print or dcs chars
if (~print) {
this._printHandler(data, print, i);
print = -1;
} else if (~dcs) {
if (dcsHandler) {
dcsHandler.put(data, dcs, i);
}
dcs = -1;
}
break;
case ParserAction.ERROR:
const inject: IParsingState = this._errorHandler(
{
position: i,
code,
currentState,
print,
dcs,
osc,
collect,
params,
Expand All @@ -486,8 +488,12 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
}
break;
case ParserAction.PARAM:
if (code === 0x3b) params.push(0);
else params[params.length - 1] = params[params.length - 1] * 10 + code - 48;
// inner loop: digits (0x30 - 0x39) and ; (0x3b)
do {
if (code === 0x3b) params.push(0);
else params[params.length - 1] = params[params.length - 1] * 10 + code - 48;
} while (++i < length && (code = data[i]) > 0x2f && (code < 0x3a || code === 0x3b));
i--;
break;
case ParserAction.COLLECT:
collect += String.fromCharCode(code);
Expand All @@ -498,47 +504,45 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
else this._escHandlerFb(collect, code);
break;
case ParserAction.CLEAR:
if (~print) {
this._printHandler(data, print, i);
print = -1;
}
osc = '';
params = [0];
collect = '';
dcs = -1;
break;
case ParserAction.DCS_HOOK:
dcsHandler = this._dcsHandlers[collect + String.fromCharCode(code)];
if (!dcsHandler) dcsHandler = this._dcsHandlerFb;
dcsHandler.hook(collect, params, code);
break;
case ParserAction.DCS_PUT:
dcs = (~dcs) ? dcs : i;
// inner loop - exit DCS_PUT: 0x18, 0x1a, 0x1b, 0x7f, 0x80 - 0x9f
// unhook triggered by: 0x1b, 0x9c
for (let j = i + 1; ; ++j) {
if (j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
if (dcsHandler) {
dcsHandler.put(data, i, j);
}
i = j - 1;
break;
}
}
break;
case ParserAction.DCS_UNHOOK:
if (dcsHandler) {
if (~dcs) dcsHandler.put(data, dcs, i);
dcsHandler.unhook();
dcsHandler = null;
}
if (code === 0x1b) transition |= ParserState.ESCAPE;
osc = '';
params = [0];
collect = '';
dcs = -1;
break;
case ParserAction.OSC_START:
if (~print) {
this._printHandler(data, print, i);
print = -1;
}
osc = '';
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 <= 0x9f)) {
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code <= 0x9f)) {
osc += utf32ToString(data, i, j);
i = j - 1;
break;
Expand Down Expand Up @@ -576,19 +580,11 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
osc = '';
params = [0];
collect = '';
dcs = -1;
break;
}
currentState = transition & TableAccess.TRANSITION_STATE_MASK;
}

// push leftover pushable buffers to terminal
if (currentState === ParserState.GROUND && ~print) {
this._printHandler(data, print, length);
} else if (currentState === ParserState.DCS_PASSTHROUGH && ~dcs && dcsHandler) {
dcsHandler.put(data, dcs, length);
}

// save non pushable buffers
this._osc = osc;
this._collect = collect;
Expand Down
4 changes: 0 additions & 4 deletions src/core/parser/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ export interface IParsingState {
code: number;
// current parser state
currentState: ParserState;
// print buffer start index (-1 for not set)
print: number;
// dcs buffer start index (-1 for not set)
dcs: number;
// osc string buffer
osc: string;
// collect buffer with intermediate characters
Expand Down

0 comments on commit 69ad755

Please sign in to comment.