From 27e323e8c1f9f0578c9e5bf1378c76881cb04473 Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 31 Jan 2016 14:55:34 -0500 Subject: [PATCH] querystring: improve unescapeBuffer() performance Before this, v8 would deopt when an out of bounds `inIndex` would get passed to charCodeAt(). charCodeAt() returns NaN in such cases, so we directly emulate that behavior as well. Also, calls to charCodeAt() for constant strings have been replaced by the raw character codes and parser state is now stored as an integer instead of a string. Both of these provide a slight performance increase. PR-URL: https://github.com/nodejs/node/pull/5012 Reviewed-By: James M Snell Reviewed-By: Roman Reiss Reviewed-By: Matteo Collina --- lib/querystring.js | 58 +++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/lib/querystring.js b/lib/querystring.js index b3b6b84c76632f..e0666a45baeefa 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -6,29 +6,25 @@ const QueryString = exports; const Buffer = require('buffer').Buffer; -function charCode(c) { - return c.charCodeAt(0); -} - - // a safe fast alternative to decodeURIComponent QueryString.unescapeBuffer = function(s, decodeSpaces) { var out = new Buffer(s.length); - var state = 'CHAR'; // states: CHAR, HEX0, HEX1 + var state = 0; var n, m, hexchar; for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) { - var c = s.charCodeAt(inIndex); + var c = inIndex < s.length ? s.charCodeAt(inIndex) : NaN; switch (state) { - case 'CHAR': + case 0: // Any character switch (c) { - case charCode('%'): + case 37: // '%' n = 0; m = 0; - state = 'HEX0'; + state = 1; break; - case charCode('+'): - if (decodeSpaces) c = charCode(' '); + case 43: // '+' + if (decodeSpaces) + c = 32; // ' ' // falls through default: out[outIndex++] = c; @@ -36,33 +32,33 @@ QueryString.unescapeBuffer = function(s, decodeSpaces) { } break; - case 'HEX0': - state = 'HEX1'; + case 1: // First hex digit hexchar = c; - if (charCode('0') <= c && c <= charCode('9')) { - n = c - charCode('0'); - } else if (charCode('a') <= c && c <= charCode('f')) { - n = c - charCode('a') + 10; - } else if (charCode('A') <= c && c <= charCode('F')) { - n = c - charCode('A') + 10; + if (c >= 48/*0*/ && c <= 57/*9*/) { + n = c - 48/*0*/; + } else if (c >= 65/*A*/ && c <= 70/*F*/) { + n = c - 65/*A*/ + 10; + } else if (c >= 97/*a*/ && c <= 102/*f*/) { + n = c - 97/*a*/ + 10; } else { - out[outIndex++] = charCode('%'); + out[outIndex++] = 37/*%*/; out[outIndex++] = c; - state = 'CHAR'; + state = 0; break; } + state = 2; break; - case 'HEX1': - state = 'CHAR'; - if (charCode('0') <= c && c <= charCode('9')) { - m = c - charCode('0'); - } else if (charCode('a') <= c && c <= charCode('f')) { - m = c - charCode('a') + 10; - } else if (charCode('A') <= c && c <= charCode('F')) { - m = c - charCode('A') + 10; + case 2: // Second hex digit + state = 0; + if (c >= 48/*0*/ && c <= 57/*9*/) { + m = c - 48/*0*/; + } else if (c >= 65/*A*/ && c <= 70/*F*/) { + m = c - 65/*A*/ + 10; + } else if (c >= 97/*a*/ && c <= 102/*f*/) { + m = c - 97/*a*/ + 10; } else { - out[outIndex++] = charCode('%'); + out[outIndex++] = 37/*%*/; out[outIndex++] = hexchar; out[outIndex++] = c; break;