Skip to content

Commit febcecd

Browse files
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 ec33dd9 commit febcecd

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed
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
@@ -3036,6 +3036,13 @@ if (internalBinding('config').hasIntl) {
30363036
function stripVTControlCharacters(str) {
30373037
validateString(str, 'str');
30383038

3039+
// Short-circuit: all ANSI escape sequences start with either
3040+
// ESC (\u001B, 7-bit) or CSI (\u009B, 8-bit) introducer.
3041+
// If neither is present, the string has no VT control characters.
3042+
if (StringPrototypeIndexOf(str, '\u001B') === -1 &&
3043+
StringPrototypeIndexOf(str, '\u009B') === -1)
3044+
return str;
3045+
30393046
return RegExpPrototypeSymbolReplace(ansi, str, '');
30403047
}
30413048

test/parallel/test-util.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,13 @@ assert.throws(() => {
8787
message: 'The "str" argument must be of type string.' +
8888
common.invalidArgTypeHelper({})
8989
});
90+
91+
// stripVTControlCharacters: fast path returns input when no ANSI codes
92+
assert.strictEqual(util.stripVTControlCharacters('hello'), 'hello');
93+
assert.strictEqual(util.stripVTControlCharacters(''), '');
94+
95+
// stripVTControlCharacters: strips 7-bit ESC sequences
96+
assert.strictEqual(util.stripVTControlCharacters('\u001B[31mfoo\u001B[39m'), 'foo');
97+
98+
// stripVTControlCharacters: strips 8-bit CSI sequences
99+
assert.strictEqual(util.stripVTControlCharacters('\u009B31mfoo\u009B39m'), 'foo');

0 commit comments

Comments
 (0)