Skip to content

Commit 236355a

Browse files
rozzillaaduh95
authored andcommitted
querystring: precompute Latin-1 escapes
Reuse precomputed escapes for Latin-1 characters instead of joining two byte escapes on every occurrence. This reduces repeated work when querystring and URLSearchParams serialize these characters. Signed-off-by: Roberto Bianchi <roberto.bianchi@spendesk.com> PR-URL: #66756 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
1 parent 6b8a90d commit 236355a

3 files changed

Lines changed: 71 additions & 2 deletions

File tree

‎benchmark/url/percent-encode.js‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('node:assert');
5+
const querystring = require('node:querystring');
6+
7+
const bench = common.createBenchmark(main, {
8+
n: [1e5],
9+
api: ['querystring', 'URLSearchParams', 'URLSearchParams-new-instance'],
10+
type: ['ascii', 'latin1', 'mixed', 'twobyte', 'bmp', 'astral'],
11+
size: [16, 256],
12+
});
13+
14+
function main({ n, api, type, size }) {
15+
const units = {
16+
ascii: 'abc/? &=',
17+
latin1: '\u00e9',
18+
mixed: 'abcd\u00e9',
19+
twobyte: '\u03b1',
20+
bmp: '\u4e2d',
21+
astral: '\ud83d\ude00',
22+
};
23+
const unit = units[type];
24+
const input = unit.repeat(Math.ceil(size / unit.length)).slice(0, size);
25+
const params = new URLSearchParams([['value', input]]);
26+
const expected = api === 'querystring' ? encodeURIComponent(input) :
27+
`value=${encodeURIComponent(input).replaceAll('%20', '+')}`;
28+
let actual;
29+
let checksum = 0;
30+
bench.start();
31+
if (api === 'querystring') {
32+
for (let index = 0; index < n; index++) {
33+
actual = querystring.escape(input);
34+
checksum += actual.charCodeAt(actual.length - 1);
35+
}
36+
} else if (api === 'URLSearchParams') {
37+
for (let index = 0; index < n; index++) {
38+
actual = params.toString();
39+
checksum += actual.charCodeAt(actual.length - 1);
40+
}
41+
} else {
42+
for (let index = 0; index < n; index++) {
43+
actual = new URLSearchParams({ value: input }).toString();
44+
checksum += actual.charCodeAt(actual.length - 1);
45+
}
46+
}
47+
bench.end(n);
48+
assert.strictEqual(actual, expected);
49+
assert.strictEqual(checksum, n * expected.charCodeAt(expected.length - 1));
50+
}

‎lib/internal/querystring.js‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ for (let i = 0; i < 256; ++i)
1717
StringPrototypeToUpperCase((i < 16 ? '0' : '') +
1818
NumberPrototypeToString(i, 16));
1919

20+
const latin1HexTable = new Array(128);
21+
for (let codePoint = 0x80; codePoint < 0x100; codePoint++) {
22+
latin1HexTable[codePoint - 0x80] = hexTable[0xC0 | (codePoint >> 6)] +
23+
hexTable[0x80 | (codePoint & 0x3F)];
24+
}
25+
2026
const isHexTable = new Int8Array([
2127
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
2228
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
@@ -76,8 +82,8 @@ function encodeStr(str, noEscapeTable, hexTable) {
7682
// Multi-byte characters ...
7783
if (c < 0x800) {
7884
lastPos = i + 1;
79-
out += hexTable[0xC0 | (c >> 6)] +
80-
hexTable[0x80 | (c & 0x3F)];
85+
out += c < 0x100 ? latin1HexTable[c - 0x80] :
86+
hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)];
8187
continue;
8288
}
8389
if (c < 0xD800 || c >= 0xE000) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('node:assert');
5+
const querystring = require('node:querystring');
6+
7+
for (let codePoint = 0x80; codePoint < 0x800; codePoint++) {
8+
const character = String.fromCharCode(codePoint);
9+
const input = `prefix${character}${character}/suffix`;
10+
const expected = encodeURIComponent(input);
11+
assert.strictEqual(querystring.escape(input), expected);
12+
assert.strictEqual(new URLSearchParams([[input, input]]).toString(), `${expected}=${expected}`);
13+
}

0 commit comments

Comments
 (0)