Skip to content

Commit 03439be

Browse files
privatenumberjuanarbol
authored andcommitted
util: add fast path to stripVTControlCharacters
PR-URL: #61833 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent b1d880a commit 03439be

3 files changed

Lines changed: 53 additions & 0 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
5+
const { stripVTControlCharacters } = require('node:util');
6+
const assert = require('node:assert');
7+
8+
const bench = common.createBenchmark(main, {
9+
input: ['noAnsi-short', 'noAnsi-long', 'ansi-short', 'ansi-long'],
10+
n: [1e6],
11+
});
12+
13+
function main({ input, n }) {
14+
let str;
15+
switch (input) {
16+
case 'noAnsi-short':
17+
str = 'This is a plain text string without any ANSI codes';
18+
break;
19+
case 'noAnsi-long':
20+
str = 'Long plain text without ANSI. '.repeat(333);
21+
break;
22+
case 'ansi-short':
23+
str = '\u001B[31mHello\u001B[39m';
24+
break;
25+
case 'ansi-long':
26+
str = ('\u001B[31m' + 'colored text '.repeat(10) + '\u001B[39m').repeat(10);
27+
break;
28+
}
29+
30+
bench.start();
31+
for (let i = 0; i < n; i++) {
32+
const result = stripVTControlCharacters(str);
33+
assert.ok(typeof result === 'string');
34+
}
35+
bench.end(n);
36+
}

lib/internal/util/inspect.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,6 +2756,13 @@ if (internalBinding('config').hasIntl) {
27562756
function stripVTControlCharacters(str) {
27572757
validateString(str, 'str');
27582758

2759+
// Short-circuit: all ANSI escape sequences start with either
2760+
// ESC (\u001B, 7-bit) or CSI (\u009B, 8-bit) introducer.
2761+
// If neither is present, the string has no VT control characters.
2762+
if (StringPrototypeIndexOf(str, '\u001B') === -1 &&
2763+
StringPrototypeIndexOf(str, '\u009B') === -1)
2764+
return str;
2765+
27592766
return RegExpPrototypeSymbolReplace(ansi, str, '');
27602767
}
27612768

test/parallel/test-util.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,13 @@ assert.throws(() => {
186186
message: 'The "str" argument must be of type string.' +
187187
common.invalidArgTypeHelper({})
188188
});
189+
190+
// stripVTControlCharacters: fast path returns input when no ANSI codes
191+
assert.strictEqual(util.stripVTControlCharacters('hello'), 'hello');
192+
assert.strictEqual(util.stripVTControlCharacters(''), '');
193+
194+
// stripVTControlCharacters: strips 7-bit ESC sequences
195+
assert.strictEqual(util.stripVTControlCharacters('\u001B[31mfoo\u001B[39m'), 'foo');
196+
197+
// stripVTControlCharacters: strips 8-bit CSI sequences
198+
assert.strictEqual(util.stripVTControlCharacters('\u009B31mfoo\u009B39m'), 'foo');

0 commit comments

Comments
 (0)