| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,35 +3,26 @@ var common = require('../common.js'); | |||
| 3 | 3 | var querystring = require('querystring'); | |
| 4 | 4 | var v8 = require('v8'); | |
| 5 | 5 | ||
| 6 | - var types = [ | ||
| 7 | - 'noencode', | ||
| 8 | - 'multicharsep', | ||
| 9 | - 'encodemany', | ||
| 10 | - 'encodelast', | ||
| 11 | - 'multivalue', | ||
| 12 | - 'multivaluemany', | ||
| 13 | - 'manypairs' | ||
| 14 | - ]; | ||
| 6 | + var inputs = { | ||
| 7 | + noencode: 'foo=bar&baz=quux&xyzzy=thud', | ||
| 8 | + multicharsep: 'foo=bar&&&&&&&&&&baz=quux&&&&&&&&&&xyzzy=thud', | ||
| 9 | + encodefake: 'foo=%©ar&baz=%A©uux&xyzzy=%©ud', | ||
| 10 | + encodemany: '%66%6F%6F=bar&%62%61%7A=quux&xyzzy=%74h%75d', | ||
| 11 | + encodelast: 'foo=bar&baz=quux&xyzzy=thu%64', | ||
| 12 | + multivalue: 'foo=bar&foo=baz&foo=quux&quuy=quuz', | ||
| 13 | + multivaluemany: 'foo=bar&foo=baz&foo=quux&quuy=quuz&foo=abc&foo=def&' + | ||
| 14 | + 'foo=ghi&foo=jkl&foo=mno&foo=pqr&foo=stu&foo=vwxyz', | ||
| 15 | + manypairs: 'a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z' | ||
| 16 | + }; | ||
| 15 | 17 | ||
| 16 | 18 | var bench = common.createBenchmark(main, { | |
| 17 | - type: types, | ||
| 19 | + type: Object.keys(inputs), | ||
| 18 | 20 | n: [1e6], | |
| 19 | 21 | }); | |
| 20 | 22 | ||
| 21 | 23 | function main(conf) { | |
| 22 | 24 | var type = conf.type; | |
| 23 | 25 | var n = conf.n | 0; | |
| 24 | - | ||
| 25 | - var inputs = { | ||
| 26 | - noencode: 'foo=bar&baz=quux&xyzzy=thud', | ||
| 27 | - multicharsep: 'foo=bar&&&&&&&&&&baz=quux&&&&&&&&&&xyzzy=thud', | ||
| 28 | - encodemany: '%66%6F%6F=bar&%62%61%7A=quux&xyzzy=%74h%75d', | ||
| 29 | - encodelast: 'foo=bar&baz=quux&xyzzy=thu%64', | ||
| 30 | - multivalue: 'foo=bar&foo=baz&foo=quux&quuy=quuz', | ||
| 31 | - multivaluemany: 'foo=bar&foo=baz&foo=quux&quuy=quuz&foo=abc&foo=def&' + | ||
| 32 | - 'foo=ghi&foo=jkl&foo=mno&foo=pqr&foo=stu&foo=vwxyz', | ||
| 33 | - manypairs: 'a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z' | ||
| 34 | - }; | ||
| 35 | 26 | var input = inputs[type]; | |
| 36 | 27 | ||
| 37 | 28 | // Force-optimize querystring.parse() so that the benchmark doesn't get | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,8 +1,19 @@ | |||
| 1 | - // Query String Utilities | ||
| 2 | - | ||
| 3 | 1 | 'use strict'; | |
| 4 | 2 | ||
| 5 | - const QueryString = exports; | ||
| 3 | + const QueryString = module.exports = { | ||
| 4 | + unescapeBuffer, | ||
| 5 | + // `unescape()` is a JS global, so we need to use a different local name | ||
| 6 | + unescape: qsUnescape, | ||
| 7 | + | ||
| 8 | + // `escape()` is a JS global, so we need to use a different local name | ||
| 9 | + escape: qsEscape, | ||
| 10 | + | ||
| 11 | + stringify, | ||
| 12 | + encode: stringify, | ||
| 13 | + | ||
| 14 | + parse, | ||
| 15 | + decode: parse | ||
| 16 | + }; | ||
| 6 | 17 | const Buffer = require('buffer').Buffer; | |
| 7 | 18 | ||
| 8 | 19 | // This constructor is used to store parsed query string values. Instantiating | |
@@ -13,7 +24,7 @@ ParsedQueryString.prototype = Object.create(null); | |||
| 13 | 24 | ||
| 14 | 25 | ||
| 15 | 26 | // a safe fast alternative to decodeURIComponent | |
| 16 | - QueryString.unescapeBuffer = function(s, decodeSpaces) { | ||
| 27 | + function unescapeBuffer(s, decodeSpaces) { | ||
| 17 | 28 | var out = Buffer.allocUnsafe(s.length); | |
| 18 | 29 | var state = 0; | |
| 19 | 30 | var n, m, hexchar; | |
@@ -77,7 +88,7 @@ QueryString.unescapeBuffer = function(s, decodeSpaces) { | |||
| 77 | 88 | // TODO support returning arbitrary buffers. | |
| 78 | 89 | ||
| 79 | 90 | return out.slice(0, outIndex - 1); | |
| 80 | - }; | ||
| 91 | + } | ||
| 81 | 92 | ||
| 82 | 93 | ||
| 83 | 94 | function qsUnescape(s, decodeSpaces) { | |
@@ -87,13 +98,12 @@ function qsUnescape(s, decodeSpaces) { | |||
| 87 | 98 | return QueryString.unescapeBuffer(s, decodeSpaces).toString(); | |
| 88 | 99 | } | |
| 89 | 100 | } | |
| 90 | - QueryString.unescape = qsUnescape; | ||
| 91 | 101 | ||
| 92 | 102 | ||
| 93 | 103 | var hexTable = new Array(256); | |
| 94 | 104 | for (var i = 0; i < 256; ++i) | |
| 95 | 105 | hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); | |
| 96 | - QueryString.escape = function(str) { | ||
| 106 | + function qsEscape(str) { | ||
| 97 | 107 | // replaces encodeURIComponent | |
| 98 | 108 | // http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 | |
| 99 | 109 | if (typeof str !== 'string') { | |
@@ -164,20 +174,20 @@ QueryString.escape = function(str) { | |||
| 164 | 174 | if (lastPos < str.length) | |
| 165 | 175 | return out + str.slice(lastPos); | |
| 166 | 176 | return out; | |
| 167 | - }; | ||
| 177 | + } | ||
| 168 | 178 | ||
| 169 | - var stringifyPrimitive = function(v) { | ||
| 179 | + function stringifyPrimitive(v) { | ||
| 170 | 180 | if (typeof v === 'string') | |
| 171 | 181 | return v; | |
| 172 | 182 | if (typeof v === 'number' && isFinite(v)) | |
| 173 | 183 | return '' + v; | |
| 174 | 184 | if (typeof v === 'boolean') | |
| 175 | 185 | return v ? 'true' : 'false'; | |
| 176 | 186 | return ''; | |
| 177 | - }; | ||
| 187 | + } | ||
| 178 | 188 | ||
| 179 | 189 | ||
| 180 | - QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) { | ||
| 190 | + function stringify(obj, sep, eq, options) { | ||
| 181 | 191 | sep = sep || '&'; | |
| 182 | 192 | eq = eq || '='; | |
| 183 | 193 | ||
@@ -215,34 +225,43 @@ QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) { | |||
| 215 | 225 | return fields; | |
| 216 | 226 | } | |
| 217 | 227 | return ''; | |
| 218 | - }; | ||
| 228 | + } | ||
| 219 | 229 | ||
| 220 | - // Parse a key/val string. | ||
| 221 | - QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | ||
| 222 | - sep = sep || '&'; | ||
| 223 | - eq = eq || '='; | ||
| 230 | + function charCodes(str) { | ||
| 231 | + if (str.length === 0) return []; | ||
| 232 | + if (str.length === 1) return [str.charCodeAt(0)]; | ||
| 233 | + const ret = []; | ||
| 234 | + for (var i = 0; i < str.length; ++i) | ||
| 235 | + ret[ret.length] = str.charCodeAt(i); | ||
| 236 | + return ret; | ||
| 237 | + } | ||
| 238 | + const defSepCodes = [38]; // & | ||
| 239 | + const defEqCodes = [61]; // = | ||
| 224 | 240 | ||
| 241 | + // Parse a key/val string. | ||
| 242 | + function parse(qs, sep, eq, options) { | ||
| 225 | 243 | const obj = new ParsedQueryString(); | |
| 226 | 244 | ||
| 227 | 245 | if (typeof qs !== 'string' || qs.length === 0) { | |
| 228 | 246 | return obj; | |
| 229 | 247 | } | |
| 230 | 248 | ||
| 231 | - if (typeof sep !== 'string') | ||
| 232 | - sep += ''; | ||
| 233 | - | ||
| 234 | - const eqLen = eq.length; | ||
| 235 | - const sepLen = sep.length; | ||
| 249 | + var sepCodes = (!sep ? defSepCodes : charCodes(sep + '')); | ||
| 250 | + var eqCodes = (!eq ? defEqCodes : charCodes(eq + '')); | ||
| 251 | + const sepLen = sepCodes.length; | ||
| 252 | + const eqLen = eqCodes.length; | ||
| 236 | 253 | ||
| 237 | - var maxKeys = 1000; | ||
| 254 | + var pairs = 1000; | ||
| 238 | 255 | if (options && typeof options.maxKeys === 'number') { | |
| 239 | - maxKeys = options.maxKeys; | ||
| 256 | + // -1 is used in place of a value like Infinity for meaning | ||
| 257 | + // "unlimited pairs" because of additional checks V8 (at least as of v5.4) | ||
| 258 | + // has to do when using variables that contain values like Infinity. Since | ||
| 259 | + // `pairs` is always decremented and checked explicitly for 0, -1 works | ||
| 260 | + // effectively the same as Infinity, while providing a significant | ||
| 261 | + // performance boost. | ||
| 262 | + pairs = (options.maxKeys > 0 ? options.maxKeys : -1); | ||
| 240 | 263 | } | |
| 241 | 264 | ||
| 242 | - var pairs = Infinity; | ||
| 243 | - if (maxKeys > 0) | ||
| 244 | - pairs = maxKeys; | ||
| 245 | - | ||
| 246 | 265 | var decode = QueryString.unescape; | |
| 247 | 266 | if (options && typeof options.decodeURIComponent === 'function') { | |
| 248 | 267 | decode = options.decodeURIComponent; | |
@@ -262,7 +281,7 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | |||
| 262 | 281 | const code = qs.charCodeAt(i); | |
| 263 | 282 | ||
| 264 | 283 | // Try matching key/value pair separator (e.g. '&') | |
| 265 | - if (code === sep.charCodeAt(sepIdx)) { | ||
| 284 | + if (code === sepCodes[sepIdx]) { | ||
| 266 | 285 | if (++sepIdx === sepLen) { | |
| 267 | 286 | // Key/value pair separator match! | |
| 268 | 287 | const end = i - sepIdx + 1; | |
@@ -284,10 +303,10 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | |||
| 284 | 303 | keys[keys.length] = key; | |
| 285 | 304 | } else { | |
| 286 | 305 | const curValue = obj[key]; | |
| 287 | - // `instanceof Array` is used instead of Array.isArray() because it | ||
| 288 | - // is ~15-20% faster with v8 4.7 and is safe to use because we are | ||
| 289 | - // using it with values being created within this function | ||
| 290 | - if (curValue instanceof Array) | ||
| 306 | + // A simple Array-specific property check is enough here to | ||
| 307 | + // distinguish from a string value and is faster and still safe since | ||
| 308 | + // we are generating all of the values being assigned. | ||
| 309 | + if (curValue.pop) | ||
| 291 | 310 | curValue[curValue.length] = value; | |
| 292 | 311 | else | |
| 293 | 312 | obj[key] = [curValue, value]; | |
@@ -322,7 +341,7 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | |||
| 322 | 341 | ||
| 323 | 342 | // Try matching key/value separator (e.g. '=') if we haven't already | |
| 324 | 343 | if (eqIdx < eqLen) { | |
| 325 | - if (code === eq.charCodeAt(eqIdx)) { | ||
| 344 | + if (code === eqCodes[eqIdx]) { | ||
| 326 | 345 | if (++eqIdx === eqLen) { | |
| 327 | 346 | // Key/value separator match! | |
| 328 | 347 | const end = i - eqIdx + 1; | |
@@ -354,12 +373,12 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | |||
| 354 | 373 | ||
| 355 | 374 | if (code === 43/*+*/) { | |
| 356 | 375 | if (eqIdx < eqLen) { | |
| 357 | - if (i - lastPos > 0) | ||
| 376 | + if (lastPos < i) | ||
| 358 | 377 | key += qs.slice(lastPos, i); | |
| 359 | 378 | key += '%20'; | |
| 360 | 379 | keyEncoded = true; | |
| 361 | 380 | } else { | |
| 362 | - if (i - lastPos > 0) | ||
| 381 | + if (lastPos < i) | ||
| 363 | 382 | value += qs.slice(lastPos, i); | |
| 364 | 383 | value += '%20'; | |
| 365 | 384 | valEncoded = true; | |
@@ -369,7 +388,7 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | |||
| 369 | 388 | } | |
| 370 | 389 | ||
| 371 | 390 | // Check if we have leftover key or value data | |
| 372 | - if (pairs > 0 && (lastPos < qs.length || eqIdx > 0)) { | ||
| 391 | + if (pairs !== 0 && (lastPos < qs.length || eqIdx > 0)) { | ||
| 373 | 392 | if (lastPos < qs.length) { | |
| 374 | 393 | if (eqIdx < eqLen) | |
| 375 | 394 | key += qs.slice(lastPos); | |
@@ -387,22 +406,23 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { | |||
| 387 | 406 | keys[keys.length] = key; | |
| 388 | 407 | } else { | |
| 389 | 408 | const curValue = obj[key]; | |
| 390 | - // `instanceof Array` is used instead of Array.isArray() because it | ||
| 391 | - // is ~15-20% faster with v8 4.7 and is safe to use because we are | ||
| 392 | - // using it with values being created within this function | ||
| 393 | - if (curValue instanceof Array) | ||
| 409 | + // A simple Array-specific property check is enough here to | ||
| 410 | + // distinguish from a string value and is faster and still safe since | ||
| 411 | + // we are generating all of the values being assigned. | ||
| 412 | + if (curValue.pop) | ||
| 394 | 413 | curValue[curValue.length] = value; | |
| 395 | 414 | else | |
| 396 | 415 | obj[key] = [curValue, value]; | |
| 397 | 416 | } | |
| 398 | 417 | } | |
| 399 | 418 | ||
| 400 | 419 | return obj; | |
| 401 | - }; | ||
| 420 | + } | ||
| 402 | 421 | ||
| 403 | 422 | ||
| 404 | 423 | // v8 does not optimize functions with try-catch blocks, so we isolate them here | |
| 405 | - // to minimize the damage | ||
| 424 | + // to minimize the damage (Note: no longer true as of V8 5.4 -- but still will | ||
| 425 | + // not be inlined). | ||
| 406 | 426 | function decodeStr(s, decoder) { | |
| 407 | 427 | try { | |
| 408 | 428 | return decoder(s); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments