1 // Query String Utilities
5 const QueryString = exports;
6 const Buffer = require('buffer').Buffer;
10 return c.charCodeAt(0);
14 // a safe fast alternative to decodeURIComponent
15 QueryString.unescapeBuffer = function(s, decodeSpaces) {
16 var out = new Buffer(s.length);
17 var state = 'CHAR'; // states: CHAR, HEX0, HEX1
20 for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
21 var c = s.charCodeAt(inIndex);
31 if (decodeSpaces) c = charCode(' ');
42 if (charCode('0') <= c && c <= charCode('9')) {
43 n = c - charCode('0');
44 } else if (charCode('a') <= c && c <= charCode('f')) {
45 n = c - charCode('a') + 10;
46 } else if (charCode('A') <= c && c <= charCode('F')) {
47 n = c - charCode('A') + 10;
49 out[outIndex++] = charCode('%');
58 if (charCode('0') <= c && c <= charCode('9')) {
59 m = c - charCode('0');
60 } else if (charCode('a') <= c && c <= charCode('f')) {
61 m = c - charCode('a') + 10;
62 } else if (charCode('A') <= c && c <= charCode('F')) {
63 m = c - charCode('A') + 10;
65 out[outIndex++] = charCode('%');
66 out[outIndex++] = hexchar;
70 out[outIndex++] = 16 * n + m;
75 // TODO support returning arbitrary buffers.
77 return out.slice(0, outIndex - 1);
81 QueryString.unescape = function(s, decodeSpaces) {
83 return decodeURIComponent(s);
85 return QueryString.unescapeBuffer(s, decodeSpaces).toString();
90 var hexTable = new Array(256);
91 for (var i = 0; i < 256; ++i)
92 hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase();
93 QueryString.escape = function(str) {
94 // replaces encodeURIComponent
95 // http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4
104 for (i = 0; i < len; ++i) {
105 c = str.charCodeAt(i);
107 // These characters do not need escaping (in order):
113 if (c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E ||
114 (c >= 0x27 && c <= 0x2A) ||
115 (c >= 0x30 && c <= 0x39) ||
116 (c >= 0x41 && c <= 0x5A) ||
117 (c >= 0x61 && c <= 0x7A)) {
122 // Other ASCII characters
128 // Multi-byte characters ...
130 out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)];
133 if (c < 0xD800 || c >= 0xE000) {
134 out += hexTable[0xE0 | (c >> 12)] +
135 hexTable[0x80 | ((c >> 6) & 0x3F)] +
136 hexTable[0x80 | (c & 0x3F)];
141 c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
142 out += hexTable[0xF0 | (c >> 18)] +
143 hexTable[0x80 | ((c >> 12) & 0x3F)] +
144 hexTable[0x80 | ((c >> 6) & 0x3F)] +
145 hexTable[0x80 | (c & 0x3F)];
150 var stringifyPrimitive = function(v) {
151 if (typeof v === 'string')
153 if (typeof v === 'number' && isFinite(v))
155 if (typeof v === 'boolean')
156 return v ? 'true' : 'false';
161 QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) {
165 var encode = QueryString.escape;
166 if (options && typeof options.encodeURIComponent === 'function') {
167 encode = options.encodeURIComponent;
170 if (obj !== null && typeof obj === 'object') {
171 var keys = Object.keys(obj);
172 var len = keys.length;
175 for (var i = 0; i < len; ++i) {
178 var ks = encode(stringifyPrimitive(k)) + eq;
180 if (Array.isArray(v)) {
182 var vlast = vlen - 1;
183 for (var j = 0; j < vlen; ++j) {
184 fields += ks + encode(stringifyPrimitive(v[j]));
188 if (vlen && i < flast)
191 fields += ks + encode(stringifyPrimitive(v));
201 // Parse a key=val string.
202 QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
205 const eqLen = eq.length;
208 if (typeof qs !== 'string' || qs.length === 0) {
216 if (options && typeof options.maxKeys === 'number') {
217 maxKeys = options.maxKeys;
221 // maxKeys <= 0 means that we should not limit keys count
222 if (maxKeys > 0 && len > maxKeys) {
226 var decode = QueryString.unescape;
227 if (options && typeof options.decodeURIComponent === 'function') {
228 decode = options.decodeURIComponent;
232 for (var i = 0; i < len; ++i) {
233 var x = qs[i].replace(regexp, '%20'),
238 k = decodeStr(x.substring(0, idx), decode);
239 v = decodeStr(x.substring(idx + eqLen), decode);
241 k = decodeStr(x, decode);
245 if (keys.indexOf(k) === -1) {
248 } else if (Array.isArray(obj[k])) {
251 obj[k] = [obj[k], v];
259 function decodeStr(s, decoder) {
263 return QueryString.unescape(s, true);