mirror of
https://github.com/nodejs/node.git
synced 2025-05-06 18:29:01 +00:00
querystring: optimize parse and stringify
parse optimizations: * Move try-catch to separate function to keep entire function from being deoptimized. * Use key array lookup instead of using hasOwnProperty. * Avoid decoding known empty strings. * Avoid possibly unnecessary switch to slower decoder for values if key decoding throws. stringify optimizations: * Use manual loop for default encoder instead of encodeURIComponent. * Use string concatenation instead of joining an array of strings. * Avoid caching result of typeof. PR-URL: https://github.com/iojs/io.js/pull/847 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
parent
65d0a8eca8
commit
85a92a37ef
@ -4,13 +4,6 @@
|
|||||||
|
|
||||||
const QueryString = exports;
|
const QueryString = exports;
|
||||||
|
|
||||||
// If obj.hasOwnProperty has been overridden, then calling
|
|
||||||
// obj.hasOwnProperty(prop) will break.
|
|
||||||
// See: https://github.com/joyent/node/issues/1707
|
|
||||||
function hasOwnProperty(obj, prop) {
|
|
||||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function charCode(c) {
|
function charCode(c) {
|
||||||
return c.charCodeAt(0);
|
return c.charCodeAt(0);
|
||||||
@ -93,19 +86,68 @@ QueryString.unescape = function(s, decodeSpaces) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var hexTable = new Array(256);
|
||||||
|
for (var i = 0; i < 256; ++i)
|
||||||
|
hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase();
|
||||||
QueryString.escape = function(str) {
|
QueryString.escape = function(str) {
|
||||||
return encodeURIComponent(str);
|
var len = str.length;
|
||||||
|
var out = '';
|
||||||
|
var i, c;
|
||||||
|
|
||||||
|
if (len === 0)
|
||||||
|
return str;
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
c = str.charCodeAt(i);
|
||||||
|
|
||||||
|
// These characters do not need escaping (in order):
|
||||||
|
// ! - . _ ~
|
||||||
|
// ' ( ) *
|
||||||
|
// digits
|
||||||
|
// alpha (uppercase)
|
||||||
|
// alpha (lowercase)
|
||||||
|
if (c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E ||
|
||||||
|
(c >= 0x27 && c <= 0x2A) ||
|
||||||
|
(c >= 0x30 && c <= 0x39) ||
|
||||||
|
(c >= 0x41 && c <= 0x5A) ||
|
||||||
|
(c >= 0x61 && c <= 0x7A)) {
|
||||||
|
out += str[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other ASCII characters
|
||||||
|
if (c < 0x80) {
|
||||||
|
out += hexTable[c];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-byte characters ...
|
||||||
|
if (c < 0x800) {
|
||||||
|
out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c < 0xD800 || c >= 0xE000) {
|
||||||
|
out += hexTable[0xE0 | (c >> 12)] +
|
||||||
|
hexTable[0x80 | ((c >> 6) & 0x3F)] +
|
||||||
|
hexTable[0x80 | (c & 0x3F)];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Surrogate pair
|
||||||
|
++i;
|
||||||
|
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
|
||||||
|
out += hexTable[0xF0 | (c >> 18)] +
|
||||||
|
hexTable[0x80 | ((c >> 12) & 0x3F)] +
|
||||||
|
hexTable[0x80 | ((c >> 6) & 0x3F)] +
|
||||||
|
hexTable[0x80 | (c & 0x3F)];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
var stringifyPrimitive = function(v) {
|
var stringifyPrimitive = function(v) {
|
||||||
let type = typeof v;
|
if (typeof v === 'string' || (typeof v === 'number' && isFinite(v)))
|
||||||
|
|
||||||
if (type === 'string')
|
|
||||||
return v;
|
return v;
|
||||||
if (type === 'boolean')
|
if (typeof v === 'boolean')
|
||||||
return v ? 'true' : 'false';
|
return v ? 'true' : 'false';
|
||||||
if (type === 'number')
|
|
||||||
return isFinite(v) ? v : '';
|
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,21 +163,31 @@ QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) {
|
|||||||
|
|
||||||
if (obj !== null && typeof obj === 'object') {
|
if (obj !== null && typeof obj === 'object') {
|
||||||
var keys = Object.keys(obj);
|
var keys = Object.keys(obj);
|
||||||
var fields = [];
|
var len = keys.length;
|
||||||
|
var flast = len - 1;
|
||||||
for (var i = 0; i < keys.length; i++) {
|
var fields = '';
|
||||||
|
for (var i = 0; i < len; ++i) {
|
||||||
var k = keys[i];
|
var k = keys[i];
|
||||||
var v = obj[k];
|
var v = obj[k];
|
||||||
var ks = encode(stringifyPrimitive(k)) + eq;
|
var ks = encode(stringifyPrimitive(k)) + eq;
|
||||||
|
|
||||||
if (Array.isArray(v)) {
|
if (Array.isArray(v)) {
|
||||||
for (var j = 0; j < v.length; j++)
|
var vlen = v.length;
|
||||||
fields.push(ks + encode(stringifyPrimitive(v[j])));
|
var vlast = vlen - 1;
|
||||||
|
for (var j = 0; j < vlen; ++j) {
|
||||||
|
fields += ks + encode(stringifyPrimitive(v[j]));
|
||||||
|
if (j < vlast)
|
||||||
|
fields += sep;
|
||||||
|
}
|
||||||
|
if (vlen && i < flast)
|
||||||
|
fields += sep;
|
||||||
} else {
|
} else {
|
||||||
fields.push(ks + encode(stringifyPrimitive(v)));
|
fields += ks + encode(stringifyPrimitive(v));
|
||||||
|
if (i < flast)
|
||||||
|
fields += sep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fields.join(sep);
|
return fields;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
@ -169,29 +221,23 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
|
|||||||
decode = options.decodeURIComponent;
|
decode = options.decodeURIComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var keys = [];
|
||||||
for (var i = 0; i < len; ++i) {
|
for (var i = 0; i < len; ++i) {
|
||||||
var x = qs[i].replace(regexp, '%20'),
|
var x = qs[i].replace(regexp, '%20'),
|
||||||
idx = x.indexOf(eq),
|
idx = x.indexOf(eq),
|
||||||
kstr, vstr, k, v;
|
k, v;
|
||||||
|
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
kstr = x.substr(0, idx);
|
k = decodeStr(x.substring(0, idx), decode);
|
||||||
vstr = x.substr(idx + 1);
|
v = decodeStr(x.substring(idx + 1), decode);
|
||||||
} else {
|
} else {
|
||||||
kstr = x;
|
k = decodeStr(x, decode);
|
||||||
vstr = '';
|
v = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (keys.indexOf(k) === -1) {
|
||||||
k = decode(kstr);
|
|
||||||
v = decode(vstr);
|
|
||||||
} catch (e) {
|
|
||||||
k = QueryString.unescape(kstr, true);
|
|
||||||
v = QueryString.unescape(vstr, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasOwnProperty(obj, k)) {
|
|
||||||
obj[k] = v;
|
obj[k] = v;
|
||||||
|
keys.push(k);
|
||||||
} else if (Array.isArray(obj[k])) {
|
} else if (Array.isArray(obj[k])) {
|
||||||
obj[k].push(v);
|
obj[k].push(v);
|
||||||
} else {
|
} else {
|
||||||
@ -201,3 +247,12 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
|
|||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function decodeStr(s, decoder) {
|
||||||
|
try {
|
||||||
|
return decoder(s);
|
||||||
|
} catch (e) {
|
||||||
|
return QueryString.unescape(s, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user