diff --git a/src/common/parser/DcsParser.ts b/src/common/parser/DcsParser.ts index 3a692ca0..2fa28992 100644 --- a/src/common/parser/DcsParser.ts +++ b/src/common/parser/DcsParser.ts @@ -100,19 +100,27 @@ export class DcsParser implements IDcsParser { } } +// predefine empty params as [0] (ZDM) +const EMPTY_PARAMS = new Params(); +EMPTY_PARAMS.addParam(0); + /** * Convenient class to create a DCS handler from a single callback function. * Note: The payload is currently limited to 50 MB (hardcoded). */ export class DcsHandler implements IDcsHandler { private _data = ''; - private _params: IParams | undefined; + private _params: IParams = EMPTY_PARAMS; private _hitLimit: boolean = false; constructor(private _handler: (data: string, params: IParams) => boolean) {} public hook(params: IParams): void { - this._params = params.clone(); + // since we need to preserve params until `unhook`, we have to clone it + // (only borrowed from parser and spans multiple parser states) + // perf optimization: + // clone only, if we have non empty params, otherwise stick with default + this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS; this._data = ''; this._hitLimit = false; } @@ -133,9 +141,9 @@ export class DcsHandler implements IDcsHandler { if (this._hitLimit) { ret = false; } else if (success) { - ret = this._handler(this._data, this._params || new Params()); + ret = this._handler(this._data, this._params); } - this._params = undefined; + this._params = EMPTY_PARAMS; this._data = ''; this._hitLimit = false; return ret; diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 065cd1d9..9a403feb 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -582,21 +582,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP i = j - 1; break; } - if (++j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { - dcs.put(data, i, j); - i = j - 1; - break; - } - if (++j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { - dcs.put(data, i, j); - i = j - 1; - break; - } - if (++j >= length || (code = data[j]) === 0x18 || code === 0x1a || code === 0x1b || (code > 0x7f && code < NON_ASCII_PRINTABLE)) { - dcs.put(data, i, j); - i = j - 1; - break; - } } break; case ParserAction.DCS_UNHOOK: @@ -613,7 +598,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP 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 < NON_ASCII_PRINTABLE)) { osc.put(data, i, j); i = j - 1; break; diff --git a/test/benchmark/EscapeSequenceParser.benchmark.ts b/test/benchmark/EscapeSequenceParser.benchmark.ts index 88bfefe5..c2707f19 100644 --- a/test/benchmark/EscapeSequenceParser.benchmark.ts +++ b/test/benchmark/EscapeSequenceParser.benchmark.ts @@ -6,9 +6,11 @@ import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-be import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; import { C0, C1 } from 'common/data/EscapeSequences'; -import { IDcsHandler, IParams } from 'common/parser/Types'; +import { IDcsHandler, IOscHandler, IParams } from 'common/parser/Types'; import { OscHandler } from 'common/parser/OscParser'; +import { DcsHandler } from '../../out/common/parser/DcsParser'; +const SIZE = 5000000; function toUtf32(s: string): Uint32Array { const result = new Uint32Array(s.length); @@ -18,10 +20,16 @@ function toUtf32(s: string): Uint32Array { return result; } -class DcsHandler implements IDcsHandler { +class FastDcsHandler implements IDcsHandler { public hook(params: IParams): void {} public put(data: Uint32Array, start: number, end: number): void {} - public unhook(): boolean { return true; } + public unhook(success: boolean): boolean { return true; } +} + +class FastOscHandler implements IOscHandler { + public start(): void {} + public put(data: Uint32Array, start: number, end: number): void {} + public end(success: boolean): boolean { return true; } } @@ -81,7 +89,7 @@ perfContext('Parser throughput - 50MB data', () => { parser.setExecuteHandler(C1.NEL, () => true); parser.setExecuteHandler(C1.HTS, () => true); parser.registerOscHandler(0, new OscHandler(data => true)); - parser.registerOscHandler(2, new OscHandler(data => true)); + parser.registerOscHandler(1, new FastOscHandler()); parser.registerEscHandler({final: '7'}, () => true); parser.registerEscHandler({final: '8'}, () => true); parser.registerEscHandler({final: 'D'}, () => true); @@ -98,14 +106,15 @@ perfContext('Parser throughput - 50MB data', () => { parser.registerEscHandler({final: '~'}, () => true); parser.registerEscHandler({intermediates: '%', final: '@'}, () => true); parser.registerEscHandler({intermediates: '%', final: 'G'}, () => true); - parser.registerDcsHandler({final: 'q'}, new DcsHandler()); + parser.registerDcsHandler({final: 'p'}, new DcsHandler(data => true)); + parser.registerDcsHandler({final: 'q'}, new FastDcsHandler()); }); perfContext('PRINT - a', () => { before(() => { const data = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -120,7 +129,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\n\n\n\n\n\n\n'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -135,7 +144,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -150,7 +159,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -165,7 +174,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -180,7 +189,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -195,7 +204,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -210,7 +219,7 @@ perfContext('Parser throughput - 50MB data', () => { before(() => { const data = '\x1b[1;2;3;4;5;6;7;8;9;0m\x1b[1;2;3;4;5;6;7;8;9;0m\x1b[1;2;3;4;5;6;7;8;9;0m'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -221,11 +230,11 @@ perfContext('Parser throughput - 50MB data', () => { }, {fork: true}).showAverageThroughput(); }); - perfContext('OSC (short) - OSC 0;hi ST', () => { + perfContext('OSC string interface (short seq) - OSC 0;hi ST', () => { before(() => { const data = '\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\x1b]0;hi\x1b\\'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -236,11 +245,11 @@ perfContext('Parser throughput - 50MB data', () => { }, {fork: true}).showAverageThroughput(); }); - perfContext('OSC (long) - OSC 0; ST', () => { + perfContext('OSC string interface (long seq) - OSC 0; ST', () => { before(() => { const data = '\x1b]0;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -251,11 +260,41 @@ perfContext('Parser throughput - 50MB data', () => { }, {fork: true}).showAverageThroughput(); }); - perfContext('DCS (short)', () => { + perfContext('OSC class interface (short seq) - OSC 0;hi ST', () => { before(() => { - const data = '\x1bPq~~\x1b\\'; + const data = '\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\x1b]1;hi\x1b\\'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { + content += data; + } + parsed = toUtf32(content); + }); + new ThroughputRuntimeCase('', () => { + parser.parse(parsed, parsed.length); + return {payloadSize: parsed.length}; + }, {fork: true}).showAverageThroughput(); + }); + + perfContext('OSC class interface (long seq) - OSC 0; ST', () => { + before(() => { + const data = '\x1b]1;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\'; + let content = ''; + while (content.length < SIZE) { + content += data; + } + parsed = toUtf32(content); + }); + new ThroughputRuntimeCase('', () => { + parser.parse(parsed, parsed.length); + return {payloadSize: parsed.length}; + }, {fork: true}).showAverageThroughput(); + }); + + perfContext('DCS string interface (short seq)', () => { + before(() => { + const data = '\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\'; + let content = ''; + while (content.length < SIZE) { content += data; } parsed = toUtf32(content); @@ -266,11 +305,41 @@ perfContext('Parser throughput - 50MB data', () => { }, {fork: true}).showAverageThroughput(); }); - perfContext('DCS (long)', () => { + perfContext('DCS string interface (long seq)', () => { before(() => { - const data = '\x1bPq#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0#1~~@@vv@@~~@@~~$#2??}}GG}}??}}??-#1!14@\x1b\\'; + const data = '\x1bPpLorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\'; let content = ''; - while (content.length < 50000000) { + while (content.length < SIZE) { + content += data; + } + parsed = toUtf32(content); + }); + new ThroughputRuntimeCase('', async () => { + parser.parse(parsed, parsed.length); + return {payloadSize: parsed.length}; + }, {fork: true}).showAverageThroughput(); + }); + + perfContext('DCS class interface (short seq)', () => { + before(() => { + const data = '\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\'; + let content = ''; + while (content.length < SIZE) { + content += data; + } + parsed = toUtf32(content); + }); + new ThroughputRuntimeCase('', async () => { + parser.parse(parsed, parsed.length); + return {payloadSize: parsed.length}; + }, {fork: true}).showAverageThroughput(); + }); + + perfContext('DCS class interface (long seq)', () => { + before(() => { + const data = '\x1bPqLorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\'; + let content = ''; + while (content.length < SIZE) { content += data; } parsed = toUtf32(content);