Skip to content

Commit e8572e7

Browse files
mscdexcodebytere
authored andcommitted
querystring: improve stringify() performance
PR-URL: #33669 Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent b8fdde4 commit e8572e7

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

benchmark/querystring/querystring-stringify.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const common = require('../common.js');
33
const querystring = require('querystring');
44

55
const bench = common.createBenchmark(main, {
6-
type: ['noencode', 'encodemany', 'encodelast', 'array'],
6+
type: ['noencode', 'encodemany', 'encodelast', 'array', 'multiprimitives'],
77
n: [1e6],
88
});
99

@@ -28,7 +28,12 @@ function main({ type, n }) {
2828
foo: [],
2929
baz: ['bar'],
3030
xyzzy: ['bar', 'quux', 'thud']
31-
}
31+
},
32+
multiprimitives: {
33+
foo: false,
34+
bar: -13.37,
35+
baz: '',
36+
},
3237
};
3338
const input = inputs[type];
3439

lib/internal/querystring.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,25 @@ function encodeStr(str, noEscapeTable, hexTable) {
3636

3737
let out = '';
3838
let lastPos = 0;
39+
let i = 0;
3940

40-
for (let i = 0; i < len; i++) {
41+
outer:
42+
for (; i < len; i++) {
4143
let c = str.charCodeAt(i);
4244

4345
// ASCII
44-
if (c < 0x80) {
45-
if (noEscapeTable[c] === 1)
46-
continue;
47-
if (lastPos < i)
48-
out += str.slice(lastPos, i);
49-
lastPos = i + 1;
50-
out += hexTable[c];
51-
continue;
46+
while (c < 0x80) {
47+
if (noEscapeTable[c] !== 1) {
48+
if (lastPos < i)
49+
out += str.slice(lastPos, i);
50+
lastPos = i + 1;
51+
out += hexTable[c];
52+
}
53+
54+
if (++i === len)
55+
break outer;
56+
57+
c = str.charCodeAt(i);
5258
}
5359

5460
if (lastPos < i)

lib/querystring.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
const {
2727
Array,
2828
ArrayIsArray,
29+
MathAbs,
2930
ObjectCreate,
3031
ObjectKeys,
3132
} = primordials;
@@ -162,6 +163,25 @@ function stringifyPrimitive(v) {
162163
}
163164

164165

166+
function encodeStringified(v, encode) {
167+
if (typeof v === 'string')
168+
return (v.length ? encode(v) : '');
169+
if (typeof v === 'number' && isFinite(v)) {
170+
// Values >= 1e21 automatically switch to scientific notation which requires
171+
// escaping due to the inclusion of a '+' in the output
172+
return (MathAbs(v) < 1e21 ? '' + v : encode('' + v));
173+
}
174+
if (typeof v === 'boolean')
175+
return v ? 'true' : 'false';
176+
return '';
177+
}
178+
179+
180+
function encodeStringifiedCustom(v, encode) {
181+
return encode(stringifyPrimitive(v));
182+
}
183+
184+
165185
function stringify(obj, sep, eq, options) {
166186
sep = sep || '&';
167187
eq = eq || '=';
@@ -170,6 +190,8 @@ function stringify(obj, sep, eq, options) {
170190
if (options && typeof options.encodeURIComponent === 'function') {
171191
encode = options.encodeURIComponent;
172192
}
193+
const convert =
194+
(encode === qsEscape ? encodeStringified : encodeStringifiedCustom);
173195

174196
if (obj !== null && typeof obj === 'object') {
175197
const keys = ObjectKeys(obj);
@@ -179,7 +201,7 @@ function stringify(obj, sep, eq, options) {
179201
for (let i = 0; i < len; ++i) {
180202
const k = keys[i];
181203
const v = obj[k];
182-
let ks = encode(stringifyPrimitive(k));
204+
let ks = convert(k, encode);
183205
ks += eq;
184206

185207
if (ArrayIsArray(v)) {
@@ -188,13 +210,13 @@ function stringify(obj, sep, eq, options) {
188210
const vlast = vlen - 1;
189211
for (let j = 0; j < vlen; ++j) {
190212
fields += ks;
191-
fields += encode(stringifyPrimitive(v[j]));
213+
fields += convert(v[j], encode);
192214
if (j < vlast)
193215
fields += sep;
194216
}
195217
} else {
196218
fields += ks;
197-
fields += encode(stringifyPrimitive(v));
219+
fields += convert(v, encode);
198220
}
199221

200222
if (i < flast)

0 commit comments

Comments
 (0)