Skip to content

Commit c8cc179

Browse files
mscdexMyles Borins
authored and
Myles Borins
committed
querystring: improve parse() performance
These changes improve parse() performance from ~11-30% on all of the existing querystring benchmarks. PR-URL: #4675 Reviewed-By: Johan Bergström <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 0462b78 commit c8cc179

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

benchmark/querystring/querystring-parse.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ var querystring = require('querystring');
33
var v8 = require('v8');
44

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

@@ -14,7 +14,8 @@ function main(conf) {
1414
var inputs = {
1515
noencode: 'foo=bar&baz=quux&xyzzy=thud',
1616
encodemany: '%66%6F%6F=bar&%62%61%7A=quux&xyzzy=%74h%75d',
17-
encodelast: 'foo=bar&baz=quux&xyzzy=thu%64'
17+
encodelast: 'foo=bar&baz=quux&xyzzy=thu%64',
18+
multivalue: 'foo=bar&foo=baz&foo=quux&quuy=quuz'
1819
};
1920
var input = inputs[type];
2021

lib/querystring.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
209209
return obj;
210210
}
211211

212-
var regexp = /\+/g;
213212
qs = qs.split(sep);
214213

215214
var maxKeys = 1000;
@@ -230,7 +229,9 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
230229

231230
var keys = [];
232231
for (var i = 0; i < len; ++i) {
233-
const x = qs[i].replace(regexp, '%20');
232+
// replacePlus() is used instead of a regexp because it is ~15-30% faster
233+
// with v8 4.7
234+
const x = replacePlus(qs[i]);
234235
const idx = x.indexOf(eq);
235236
var k, v;
236237

@@ -242,10 +243,14 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
242243
v = '';
243244
}
244245

246+
// Use a key array lookup instead of using hasOwnProperty(), which is slower
245247
if (keys.indexOf(k) === -1) {
246248
obj[k] = v;
247249
keys.push(k);
248-
} else if (Array.isArray(obj[k])) {
250+
} else if (obj[k] instanceof Array) {
251+
// `instanceof Array` is used instead of Array.isArray() because it is
252+
// ~15-20% faster with v8 4.7 and is safe to use because we are using it
253+
// with values being created within this function
249254
obj[k].push(v);
250255
} else {
251256
obj[k] = [obj[k], v];
@@ -256,6 +261,25 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
256261
};
257262

258263

264+
function replacePlus(str) {
265+
var ret = '';
266+
var start = 0;
267+
var i = -1;
268+
while ((i = str.indexOf('+', i + 1)) !== -1) {
269+
ret += str.slice(start, i);
270+
ret += '%20';
271+
start = i + 1;
272+
}
273+
if (start === 0)
274+
return str;
275+
if (start < str.length)
276+
ret += str.slice(start);
277+
return ret;
278+
}
279+
280+
281+
// v8 does not optimize functions with try-catch blocks, so we isolate them here
282+
// to minimize the damage
259283
function decodeStr(s, decoder) {
260284
try {
261285
return decoder(s);

0 commit comments

Comments
 (0)