Skip to content

Commit 00638ac

Browse files
committed
querystring: improve escape() performance
This commit improves escape() performance by up to 15% with the existing querystring-stringify benchmarks by reducing the number of string concatentations. A potential deopt is also avoided by making sure the index passed to charCodeAt() is within bounds. PR-URL: #5012 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Roman Reiss <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent c8e650d commit 00638ac

File tree

2 files changed

+24
-11
lines changed

2 files changed

+24
-11
lines changed

benchmark/querystring/querystring-stringify.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var v8 = require('v8');
44

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

1010
function main(conf) {
@@ -37,6 +37,7 @@ function main(conf) {
3737

3838
v8.setFlagsFromString('--allow_natives_syntax');
3939
eval('%OptimizeFunctionOnNextCall(querystring.stringify)');
40+
querystring.stringify(input);
4041

4142
bench.start();
4243
for (var i = 0; i < n; i += 1)

lib/querystring.js

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,13 @@ for (var i = 0; i < 256; ++i)
9090
QueryString.escape = function(str) {
9191
// replaces encodeURIComponent
9292
// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
93-
str = '' + str;
94-
var len = str.length;
93+
if (typeof str !== 'string')
94+
str += '';
9595
var out = '';
96-
var i, c;
97-
98-
if (len === 0)
99-
return str;
96+
var lastPos = 0;
10097

101-
for (i = 0; i < len; ++i) {
102-
c = str.charCodeAt(i);
98+
for (var i = 0; i < str.length; ++i) {
99+
var c = str.charCodeAt(i);
103100

104101
// These characters do not need escaping (in order):
105102
// ! - . _ ~
@@ -112,35 +109,50 @@ QueryString.escape = function(str) {
112109
(c >= 0x30 && c <= 0x39) ||
113110
(c >= 0x41 && c <= 0x5A) ||
114111
(c >= 0x61 && c <= 0x7A)) {
115-
out += str[i];
116112
continue;
117113
}
118114

115+
if (i - lastPos > 0)
116+
out += str.slice(lastPos, i);
117+
119118
// Other ASCII characters
120119
if (c < 0x80) {
120+
lastPos = i + 1;
121121
out += hexTable[c];
122122
continue;
123123
}
124124

125125
// Multi-byte characters ...
126126
if (c < 0x800) {
127+
lastPos = i + 1;
127128
out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)];
128129
continue;
129130
}
130131
if (c < 0xD800 || c >= 0xE000) {
132+
lastPos = i + 1;
131133
out += hexTable[0xE0 | (c >> 12)] +
132134
hexTable[0x80 | ((c >> 6) & 0x3F)] +
133135
hexTable[0x80 | (c & 0x3F)];
134136
continue;
135137
}
136138
// Surrogate pair
137139
++i;
138-
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
140+
var c2;
141+
if (i < str.length)
142+
c2 = str.charCodeAt(i) & 0x3FF;
143+
else
144+
c2 = 0;
145+
lastPos = i + 1;
146+
c = 0x10000 + (((c & 0x3FF) << 10) | c2);
139147
out += hexTable[0xF0 | (c >> 18)] +
140148
hexTable[0x80 | ((c >> 12) & 0x3F)] +
141149
hexTable[0x80 | ((c >> 6) & 0x3F)] +
142150
hexTable[0x80 | (c & 0x3F)];
143151
}
152+
if (lastPos === 0)
153+
return str;
154+
if (lastPos < str.length)
155+
return out + str.slice(lastPos);
144156
return out;
145157
};
146158

0 commit comments

Comments
 (0)