11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors. 21cb0ef41Sopenharmony_ci// 31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a 41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the 51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including 61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish, 71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit 81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the 91cb0ef41Sopenharmony_ci// following conditions: 101cb0ef41Sopenharmony_ci// 111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included 121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software. 131cb0ef41Sopenharmony_ci// 141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE. 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci// Query String Utilities 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci'use strict'; 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ciconst { 271cb0ef41Sopenharmony_ci Array, 281cb0ef41Sopenharmony_ci ArrayIsArray, 291cb0ef41Sopenharmony_ci Int8Array, 301cb0ef41Sopenharmony_ci MathAbs, 311cb0ef41Sopenharmony_ci NumberIsFinite, 321cb0ef41Sopenharmony_ci ObjectCreate, 331cb0ef41Sopenharmony_ci ObjectKeys, 341cb0ef41Sopenharmony_ci String, 351cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt, 361cb0ef41Sopenharmony_ci StringPrototypeSlice, 371cb0ef41Sopenharmony_ci decodeURIComponent, 381cb0ef41Sopenharmony_ci} = primordials; 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ciconst { Buffer } = require('buffer'); 411cb0ef41Sopenharmony_ciconst { 421cb0ef41Sopenharmony_ci encodeStr, 431cb0ef41Sopenharmony_ci hexTable, 441cb0ef41Sopenharmony_ci isHexTable, 451cb0ef41Sopenharmony_ci} = require('internal/querystring'); 461cb0ef41Sopenharmony_ciconst QueryString = module.exports = { 471cb0ef41Sopenharmony_ci unescapeBuffer, 481cb0ef41Sopenharmony_ci // `unescape()` is a JS global, so we need to use a different local name 491cb0ef41Sopenharmony_ci unescape: qsUnescape, 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci // `escape()` is a JS global, so we need to use a different local name 521cb0ef41Sopenharmony_ci escape: qsEscape, 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci stringify, 551cb0ef41Sopenharmony_ci encode: stringify, 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci parse, 581cb0ef41Sopenharmony_ci decode: parse, 591cb0ef41Sopenharmony_ci}; 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ciconst unhexTable = new Int8Array([ 621cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15 631cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31 641cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 - 47 651cb0ef41Sopenharmony_ci +0, +1, +2, +3, +4, +5, +6, +7, +8, +9, -1, -1, -1, -1, -1, -1, // 48 - 63 661cb0ef41Sopenharmony_ci -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 - 79 671cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 - 95 681cb0ef41Sopenharmony_ci -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 - 111 691cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 - 127 701cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 ... 711cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 721cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 731cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 741cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 751cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 761cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 771cb0ef41Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 255 781cb0ef41Sopenharmony_ci]); 791cb0ef41Sopenharmony_ci/** 801cb0ef41Sopenharmony_ci * A safe fast alternative to decodeURIComponent 811cb0ef41Sopenharmony_ci * @param {string} s 821cb0ef41Sopenharmony_ci * @param {boolean} decodeSpaces 831cb0ef41Sopenharmony_ci * @returns {string} 841cb0ef41Sopenharmony_ci */ 851cb0ef41Sopenharmony_cifunction unescapeBuffer(s, decodeSpaces) { 861cb0ef41Sopenharmony_ci const out = Buffer.allocUnsafe(s.length); 871cb0ef41Sopenharmony_ci let index = 0; 881cb0ef41Sopenharmony_ci let outIndex = 0; 891cb0ef41Sopenharmony_ci let currentChar; 901cb0ef41Sopenharmony_ci let nextChar; 911cb0ef41Sopenharmony_ci let hexHigh; 921cb0ef41Sopenharmony_ci let hexLow; 931cb0ef41Sopenharmony_ci const maxLength = s.length - 2; 941cb0ef41Sopenharmony_ci // Flag to know if some hex chars have been decoded 951cb0ef41Sopenharmony_ci let hasHex = false; 961cb0ef41Sopenharmony_ci while (index < s.length) { 971cb0ef41Sopenharmony_ci currentChar = StringPrototypeCharCodeAt(s, index); 981cb0ef41Sopenharmony_ci if (currentChar === 43 /* '+' */ && decodeSpaces) { 991cb0ef41Sopenharmony_ci out[outIndex++] = 32; // ' ' 1001cb0ef41Sopenharmony_ci index++; 1011cb0ef41Sopenharmony_ci continue; 1021cb0ef41Sopenharmony_ci } 1031cb0ef41Sopenharmony_ci if (currentChar === 37 /* '%' */ && index < maxLength) { 1041cb0ef41Sopenharmony_ci currentChar = StringPrototypeCharCodeAt(s, ++index); 1051cb0ef41Sopenharmony_ci hexHigh = unhexTable[currentChar]; 1061cb0ef41Sopenharmony_ci if (!(hexHigh >= 0)) { 1071cb0ef41Sopenharmony_ci out[outIndex++] = 37; // '%' 1081cb0ef41Sopenharmony_ci continue; 1091cb0ef41Sopenharmony_ci } else { 1101cb0ef41Sopenharmony_ci nextChar = StringPrototypeCharCodeAt(s, ++index); 1111cb0ef41Sopenharmony_ci hexLow = unhexTable[nextChar]; 1121cb0ef41Sopenharmony_ci if (!(hexLow >= 0)) { 1131cb0ef41Sopenharmony_ci out[outIndex++] = 37; // '%' 1141cb0ef41Sopenharmony_ci index--; 1151cb0ef41Sopenharmony_ci } else { 1161cb0ef41Sopenharmony_ci hasHex = true; 1171cb0ef41Sopenharmony_ci currentChar = hexHigh * 16 + hexLow; 1181cb0ef41Sopenharmony_ci } 1191cb0ef41Sopenharmony_ci } 1201cb0ef41Sopenharmony_ci } 1211cb0ef41Sopenharmony_ci out[outIndex++] = currentChar; 1221cb0ef41Sopenharmony_ci index++; 1231cb0ef41Sopenharmony_ci } 1241cb0ef41Sopenharmony_ci return hasHex ? out.slice(0, outIndex) : out; 1251cb0ef41Sopenharmony_ci} 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci/** 1281cb0ef41Sopenharmony_ci * @param {string} s 1291cb0ef41Sopenharmony_ci * @param {boolean} decodeSpaces 1301cb0ef41Sopenharmony_ci * @returns {string} 1311cb0ef41Sopenharmony_ci */ 1321cb0ef41Sopenharmony_cifunction qsUnescape(s, decodeSpaces) { 1331cb0ef41Sopenharmony_ci try { 1341cb0ef41Sopenharmony_ci return decodeURIComponent(s); 1351cb0ef41Sopenharmony_ci } catch { 1361cb0ef41Sopenharmony_ci return QueryString.unescapeBuffer(s, decodeSpaces).toString(); 1371cb0ef41Sopenharmony_ci } 1381cb0ef41Sopenharmony_ci} 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci// These characters do not need escaping when generating query strings: 1421cb0ef41Sopenharmony_ci// ! - . _ ~ 1431cb0ef41Sopenharmony_ci// ' ( ) * 1441cb0ef41Sopenharmony_ci// digits 1451cb0ef41Sopenharmony_ci// alpha (uppercase) 1461cb0ef41Sopenharmony_ci// alpha (lowercase) 1471cb0ef41Sopenharmony_ciconst noEscape = new Int8Array([ 1481cb0ef41Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 1491cb0ef41Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 1501cb0ef41Sopenharmony_ci 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 32 - 47 1511cb0ef41Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 1521cb0ef41Sopenharmony_ci 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 1531cb0ef41Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 80 - 95 1541cb0ef41Sopenharmony_ci 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 1551cb0ef41Sopenharmony_ci 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 112 - 127 1561cb0ef41Sopenharmony_ci]); 1571cb0ef41Sopenharmony_ci 1581cb0ef41Sopenharmony_ci/** 1591cb0ef41Sopenharmony_ci * QueryString.escape() replaces encodeURIComponent() 1601cb0ef41Sopenharmony_ci * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 1611cb0ef41Sopenharmony_ci * @param {any} str 1621cb0ef41Sopenharmony_ci * @returns {string} 1631cb0ef41Sopenharmony_ci */ 1641cb0ef41Sopenharmony_cifunction qsEscape(str) { 1651cb0ef41Sopenharmony_ci if (typeof str !== 'string') { 1661cb0ef41Sopenharmony_ci if (typeof str === 'object') 1671cb0ef41Sopenharmony_ci str = String(str); 1681cb0ef41Sopenharmony_ci else 1691cb0ef41Sopenharmony_ci str += ''; 1701cb0ef41Sopenharmony_ci } 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci return encodeStr(str, noEscape, hexTable); 1731cb0ef41Sopenharmony_ci} 1741cb0ef41Sopenharmony_ci 1751cb0ef41Sopenharmony_ci/** 1761cb0ef41Sopenharmony_ci * @param {string | number | bigint | boolean | symbol | undefined | null} v 1771cb0ef41Sopenharmony_ci * @returns {string} 1781cb0ef41Sopenharmony_ci */ 1791cb0ef41Sopenharmony_cifunction stringifyPrimitive(v) { 1801cb0ef41Sopenharmony_ci if (typeof v === 'string') 1811cb0ef41Sopenharmony_ci return v; 1821cb0ef41Sopenharmony_ci if (typeof v === 'number' && NumberIsFinite(v)) 1831cb0ef41Sopenharmony_ci return '' + v; 1841cb0ef41Sopenharmony_ci if (typeof v === 'bigint') 1851cb0ef41Sopenharmony_ci return '' + v; 1861cb0ef41Sopenharmony_ci if (typeof v === 'boolean') 1871cb0ef41Sopenharmony_ci return v ? 'true' : 'false'; 1881cb0ef41Sopenharmony_ci return ''; 1891cb0ef41Sopenharmony_ci} 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci/** 1921cb0ef41Sopenharmony_ci * @param {string | number | bigint | boolean} v 1931cb0ef41Sopenharmony_ci * @param {(v: string) => string} encode 1941cb0ef41Sopenharmony_ci * @returns {string} 1951cb0ef41Sopenharmony_ci */ 1961cb0ef41Sopenharmony_cifunction encodeStringified(v, encode) { 1971cb0ef41Sopenharmony_ci if (typeof v === 'string') 1981cb0ef41Sopenharmony_ci return (v.length ? encode(v) : ''); 1991cb0ef41Sopenharmony_ci if (typeof v === 'number' && NumberIsFinite(v)) { 2001cb0ef41Sopenharmony_ci // Values >= 1e21 automatically switch to scientific notation which requires 2011cb0ef41Sopenharmony_ci // escaping due to the inclusion of a '+' in the output 2021cb0ef41Sopenharmony_ci return (MathAbs(v) < 1e21 ? '' + v : encode('' + v)); 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci if (typeof v === 'bigint') 2051cb0ef41Sopenharmony_ci return '' + v; 2061cb0ef41Sopenharmony_ci if (typeof v === 'boolean') 2071cb0ef41Sopenharmony_ci return v ? 'true' : 'false'; 2081cb0ef41Sopenharmony_ci return ''; 2091cb0ef41Sopenharmony_ci} 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci/** 2121cb0ef41Sopenharmony_ci * @param {string | number | boolean | null} v 2131cb0ef41Sopenharmony_ci * @param {(v: string) => string} encode 2141cb0ef41Sopenharmony_ci * @returns {string} 2151cb0ef41Sopenharmony_ci */ 2161cb0ef41Sopenharmony_cifunction encodeStringifiedCustom(v, encode) { 2171cb0ef41Sopenharmony_ci return encode(stringifyPrimitive(v)); 2181cb0ef41Sopenharmony_ci} 2191cb0ef41Sopenharmony_ci 2201cb0ef41Sopenharmony_ci/** 2211cb0ef41Sopenharmony_ci * @param {Record<string, string | number | boolean 2221cb0ef41Sopenharmony_ci * | ReadonlyArray<string | number | boolean> | null>} obj 2231cb0ef41Sopenharmony_ci * @param {string} [sep] 2241cb0ef41Sopenharmony_ci * @param {string} [eq] 2251cb0ef41Sopenharmony_ci * @param {{ encodeURIComponent?: (v: string) => string }} [options] 2261cb0ef41Sopenharmony_ci * @returns {string} 2271cb0ef41Sopenharmony_ci */ 2281cb0ef41Sopenharmony_cifunction stringify(obj, sep, eq, options) { 2291cb0ef41Sopenharmony_ci sep = sep || '&'; 2301cb0ef41Sopenharmony_ci eq = eq || '='; 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci let encode = QueryString.escape; 2331cb0ef41Sopenharmony_ci if (options && typeof options.encodeURIComponent === 'function') { 2341cb0ef41Sopenharmony_ci encode = options.encodeURIComponent; 2351cb0ef41Sopenharmony_ci } 2361cb0ef41Sopenharmony_ci const convert = 2371cb0ef41Sopenharmony_ci (encode === qsEscape ? encodeStringified : encodeStringifiedCustom); 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ci if (obj !== null && typeof obj === 'object') { 2401cb0ef41Sopenharmony_ci const keys = ObjectKeys(obj); 2411cb0ef41Sopenharmony_ci const len = keys.length; 2421cb0ef41Sopenharmony_ci let fields = ''; 2431cb0ef41Sopenharmony_ci for (let i = 0; i < len; ++i) { 2441cb0ef41Sopenharmony_ci const k = keys[i]; 2451cb0ef41Sopenharmony_ci const v = obj[k]; 2461cb0ef41Sopenharmony_ci let ks = convert(k, encode); 2471cb0ef41Sopenharmony_ci ks += eq; 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ci if (ArrayIsArray(v)) { 2501cb0ef41Sopenharmony_ci const vlen = v.length; 2511cb0ef41Sopenharmony_ci if (vlen === 0) continue; 2521cb0ef41Sopenharmony_ci if (fields) 2531cb0ef41Sopenharmony_ci fields += sep; 2541cb0ef41Sopenharmony_ci for (let j = 0; j < vlen; ++j) { 2551cb0ef41Sopenharmony_ci if (j) 2561cb0ef41Sopenharmony_ci fields += sep; 2571cb0ef41Sopenharmony_ci fields += ks; 2581cb0ef41Sopenharmony_ci fields += convert(v[j], encode); 2591cb0ef41Sopenharmony_ci } 2601cb0ef41Sopenharmony_ci } else { 2611cb0ef41Sopenharmony_ci if (fields) 2621cb0ef41Sopenharmony_ci fields += sep; 2631cb0ef41Sopenharmony_ci fields += ks; 2641cb0ef41Sopenharmony_ci fields += convert(v, encode); 2651cb0ef41Sopenharmony_ci } 2661cb0ef41Sopenharmony_ci } 2671cb0ef41Sopenharmony_ci return fields; 2681cb0ef41Sopenharmony_ci } 2691cb0ef41Sopenharmony_ci return ''; 2701cb0ef41Sopenharmony_ci} 2711cb0ef41Sopenharmony_ci 2721cb0ef41Sopenharmony_ci/** 2731cb0ef41Sopenharmony_ci * @param {string} str 2741cb0ef41Sopenharmony_ci * @returns {number[]} 2751cb0ef41Sopenharmony_ci */ 2761cb0ef41Sopenharmony_cifunction charCodes(str) { 2771cb0ef41Sopenharmony_ci if (str.length === 0) return []; 2781cb0ef41Sopenharmony_ci if (str.length === 1) return [StringPrototypeCharCodeAt(str, 0)]; 2791cb0ef41Sopenharmony_ci const ret = new Array(str.length); 2801cb0ef41Sopenharmony_ci for (let i = 0; i < str.length; ++i) 2811cb0ef41Sopenharmony_ci ret[i] = StringPrototypeCharCodeAt(str, i); 2821cb0ef41Sopenharmony_ci return ret; 2831cb0ef41Sopenharmony_ci} 2841cb0ef41Sopenharmony_ciconst defSepCodes = [38]; // & 2851cb0ef41Sopenharmony_ciconst defEqCodes = [61]; // = 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_cifunction addKeyVal(obj, key, value, keyEncoded, valEncoded, decode) { 2881cb0ef41Sopenharmony_ci if (key.length > 0 && keyEncoded) 2891cb0ef41Sopenharmony_ci key = decodeStr(key, decode); 2901cb0ef41Sopenharmony_ci if (value.length > 0 && valEncoded) 2911cb0ef41Sopenharmony_ci value = decodeStr(value, decode); 2921cb0ef41Sopenharmony_ci 2931cb0ef41Sopenharmony_ci if (obj[key] === undefined) { 2941cb0ef41Sopenharmony_ci obj[key] = value; 2951cb0ef41Sopenharmony_ci } else { 2961cb0ef41Sopenharmony_ci const curValue = obj[key]; 2971cb0ef41Sopenharmony_ci // A simple Array-specific property check is enough here to 2981cb0ef41Sopenharmony_ci // distinguish from a string value and is faster and still safe 2991cb0ef41Sopenharmony_ci // since we are generating all of the values being assigned. 3001cb0ef41Sopenharmony_ci if (curValue.pop) 3011cb0ef41Sopenharmony_ci curValue[curValue.length] = value; 3021cb0ef41Sopenharmony_ci else 3031cb0ef41Sopenharmony_ci obj[key] = [curValue, value]; 3041cb0ef41Sopenharmony_ci } 3051cb0ef41Sopenharmony_ci} 3061cb0ef41Sopenharmony_ci 3071cb0ef41Sopenharmony_ci/** 3081cb0ef41Sopenharmony_ci * Parse a key/val string. 3091cb0ef41Sopenharmony_ci * @param {string} qs 3101cb0ef41Sopenharmony_ci * @param {string} sep 3111cb0ef41Sopenharmony_ci * @param {string} eq 3121cb0ef41Sopenharmony_ci * @param {{ 3131cb0ef41Sopenharmony_ci * maxKeys?: number; 3141cb0ef41Sopenharmony_ci * decodeURIComponent?(v: string): string; 3151cb0ef41Sopenharmony_ci * }} [options] 3161cb0ef41Sopenharmony_ci * @returns {Record<string, string | string[]>} 3171cb0ef41Sopenharmony_ci */ 3181cb0ef41Sopenharmony_cifunction parse(qs, sep, eq, options) { 3191cb0ef41Sopenharmony_ci const obj = ObjectCreate(null); 3201cb0ef41Sopenharmony_ci 3211cb0ef41Sopenharmony_ci if (typeof qs !== 'string' || qs.length === 0) { 3221cb0ef41Sopenharmony_ci return obj; 3231cb0ef41Sopenharmony_ci } 3241cb0ef41Sopenharmony_ci 3251cb0ef41Sopenharmony_ci const sepCodes = (!sep ? defSepCodes : charCodes(String(sep))); 3261cb0ef41Sopenharmony_ci const eqCodes = (!eq ? defEqCodes : charCodes(String(eq))); 3271cb0ef41Sopenharmony_ci const sepLen = sepCodes.length; 3281cb0ef41Sopenharmony_ci const eqLen = eqCodes.length; 3291cb0ef41Sopenharmony_ci 3301cb0ef41Sopenharmony_ci let pairs = 1000; 3311cb0ef41Sopenharmony_ci if (options && typeof options.maxKeys === 'number') { 3321cb0ef41Sopenharmony_ci // -1 is used in place of a value like Infinity for meaning 3331cb0ef41Sopenharmony_ci // "unlimited pairs" because of additional checks V8 (at least as of v5.4) 3341cb0ef41Sopenharmony_ci // has to do when using variables that contain values like Infinity. Since 3351cb0ef41Sopenharmony_ci // `pairs` is always decremented and checked explicitly for 0, -1 works 3361cb0ef41Sopenharmony_ci // effectively the same as Infinity, while providing a significant 3371cb0ef41Sopenharmony_ci // performance boost. 3381cb0ef41Sopenharmony_ci pairs = (options.maxKeys > 0 ? options.maxKeys : -1); 3391cb0ef41Sopenharmony_ci } 3401cb0ef41Sopenharmony_ci 3411cb0ef41Sopenharmony_ci let decode = QueryString.unescape; 3421cb0ef41Sopenharmony_ci if (options && typeof options.decodeURIComponent === 'function') { 3431cb0ef41Sopenharmony_ci decode = options.decodeURIComponent; 3441cb0ef41Sopenharmony_ci } 3451cb0ef41Sopenharmony_ci const customDecode = (decode !== qsUnescape); 3461cb0ef41Sopenharmony_ci 3471cb0ef41Sopenharmony_ci let lastPos = 0; 3481cb0ef41Sopenharmony_ci let sepIdx = 0; 3491cb0ef41Sopenharmony_ci let eqIdx = 0; 3501cb0ef41Sopenharmony_ci let key = ''; 3511cb0ef41Sopenharmony_ci let value = ''; 3521cb0ef41Sopenharmony_ci let keyEncoded = customDecode; 3531cb0ef41Sopenharmony_ci let valEncoded = customDecode; 3541cb0ef41Sopenharmony_ci const plusChar = (customDecode ? '%20' : ' '); 3551cb0ef41Sopenharmony_ci let encodeCheck = 0; 3561cb0ef41Sopenharmony_ci for (let i = 0; i < qs.length; ++i) { 3571cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(qs, i); 3581cb0ef41Sopenharmony_ci 3591cb0ef41Sopenharmony_ci // Try matching key/value pair separator (e.g. '&') 3601cb0ef41Sopenharmony_ci if (code === sepCodes[sepIdx]) { 3611cb0ef41Sopenharmony_ci if (++sepIdx === sepLen) { 3621cb0ef41Sopenharmony_ci // Key/value pair separator match! 3631cb0ef41Sopenharmony_ci const end = i - sepIdx + 1; 3641cb0ef41Sopenharmony_ci if (eqIdx < eqLen) { 3651cb0ef41Sopenharmony_ci // We didn't find the (entire) key/value separator 3661cb0ef41Sopenharmony_ci if (lastPos < end) { 3671cb0ef41Sopenharmony_ci // Treat the substring as part of the key instead of the value 3681cb0ef41Sopenharmony_ci key += StringPrototypeSlice(qs, lastPos, end); 3691cb0ef41Sopenharmony_ci } else if (key.length === 0) { 3701cb0ef41Sopenharmony_ci // We saw an empty substring between separators 3711cb0ef41Sopenharmony_ci if (--pairs === 0) 3721cb0ef41Sopenharmony_ci return obj; 3731cb0ef41Sopenharmony_ci lastPos = i + 1; 3741cb0ef41Sopenharmony_ci sepIdx = eqIdx = 0; 3751cb0ef41Sopenharmony_ci continue; 3761cb0ef41Sopenharmony_ci } 3771cb0ef41Sopenharmony_ci } else if (lastPos < end) { 3781cb0ef41Sopenharmony_ci value += StringPrototypeSlice(qs, lastPos, end); 3791cb0ef41Sopenharmony_ci } 3801cb0ef41Sopenharmony_ci 3811cb0ef41Sopenharmony_ci addKeyVal(obj, key, value, keyEncoded, valEncoded, decode); 3821cb0ef41Sopenharmony_ci 3831cb0ef41Sopenharmony_ci if (--pairs === 0) 3841cb0ef41Sopenharmony_ci return obj; 3851cb0ef41Sopenharmony_ci keyEncoded = valEncoded = customDecode; 3861cb0ef41Sopenharmony_ci key = value = ''; 3871cb0ef41Sopenharmony_ci encodeCheck = 0; 3881cb0ef41Sopenharmony_ci lastPos = i + 1; 3891cb0ef41Sopenharmony_ci sepIdx = eqIdx = 0; 3901cb0ef41Sopenharmony_ci } 3911cb0ef41Sopenharmony_ci } else { 3921cb0ef41Sopenharmony_ci sepIdx = 0; 3931cb0ef41Sopenharmony_ci // Try matching key/value separator (e.g. '=') if we haven't already 3941cb0ef41Sopenharmony_ci if (eqIdx < eqLen) { 3951cb0ef41Sopenharmony_ci if (code === eqCodes[eqIdx]) { 3961cb0ef41Sopenharmony_ci if (++eqIdx === eqLen) { 3971cb0ef41Sopenharmony_ci // Key/value separator match! 3981cb0ef41Sopenharmony_ci const end = i - eqIdx + 1; 3991cb0ef41Sopenharmony_ci if (lastPos < end) 4001cb0ef41Sopenharmony_ci key += StringPrototypeSlice(qs, lastPos, end); 4011cb0ef41Sopenharmony_ci encodeCheck = 0; 4021cb0ef41Sopenharmony_ci lastPos = i + 1; 4031cb0ef41Sopenharmony_ci } 4041cb0ef41Sopenharmony_ci continue; 4051cb0ef41Sopenharmony_ci } else { 4061cb0ef41Sopenharmony_ci eqIdx = 0; 4071cb0ef41Sopenharmony_ci if (!keyEncoded) { 4081cb0ef41Sopenharmony_ci // Try to match an (valid) encoded byte once to minimize unnecessary 4091cb0ef41Sopenharmony_ci // calls to string decoding functions 4101cb0ef41Sopenharmony_ci if (code === 37/* % */) { 4111cb0ef41Sopenharmony_ci encodeCheck = 1; 4121cb0ef41Sopenharmony_ci continue; 4131cb0ef41Sopenharmony_ci } else if (encodeCheck > 0) { 4141cb0ef41Sopenharmony_ci if (isHexTable[code] === 1) { 4151cb0ef41Sopenharmony_ci if (++encodeCheck === 3) 4161cb0ef41Sopenharmony_ci keyEncoded = true; 4171cb0ef41Sopenharmony_ci continue; 4181cb0ef41Sopenharmony_ci } else { 4191cb0ef41Sopenharmony_ci encodeCheck = 0; 4201cb0ef41Sopenharmony_ci } 4211cb0ef41Sopenharmony_ci } 4221cb0ef41Sopenharmony_ci } 4231cb0ef41Sopenharmony_ci } 4241cb0ef41Sopenharmony_ci if (code === 43/* + */) { 4251cb0ef41Sopenharmony_ci if (lastPos < i) 4261cb0ef41Sopenharmony_ci key += StringPrototypeSlice(qs, lastPos, i); 4271cb0ef41Sopenharmony_ci key += plusChar; 4281cb0ef41Sopenharmony_ci lastPos = i + 1; 4291cb0ef41Sopenharmony_ci continue; 4301cb0ef41Sopenharmony_ci } 4311cb0ef41Sopenharmony_ci } 4321cb0ef41Sopenharmony_ci if (code === 43/* + */) { 4331cb0ef41Sopenharmony_ci if (lastPos < i) 4341cb0ef41Sopenharmony_ci value += StringPrototypeSlice(qs, lastPos, i); 4351cb0ef41Sopenharmony_ci value += plusChar; 4361cb0ef41Sopenharmony_ci lastPos = i + 1; 4371cb0ef41Sopenharmony_ci } else if (!valEncoded) { 4381cb0ef41Sopenharmony_ci // Try to match an (valid) encoded byte (once) to minimize unnecessary 4391cb0ef41Sopenharmony_ci // calls to string decoding functions 4401cb0ef41Sopenharmony_ci if (code === 37/* % */) { 4411cb0ef41Sopenharmony_ci encodeCheck = 1; 4421cb0ef41Sopenharmony_ci } else if (encodeCheck > 0) { 4431cb0ef41Sopenharmony_ci if (isHexTable[code] === 1) { 4441cb0ef41Sopenharmony_ci if (++encodeCheck === 3) 4451cb0ef41Sopenharmony_ci valEncoded = true; 4461cb0ef41Sopenharmony_ci } else { 4471cb0ef41Sopenharmony_ci encodeCheck = 0; 4481cb0ef41Sopenharmony_ci } 4491cb0ef41Sopenharmony_ci } 4501cb0ef41Sopenharmony_ci } 4511cb0ef41Sopenharmony_ci } 4521cb0ef41Sopenharmony_ci } 4531cb0ef41Sopenharmony_ci 4541cb0ef41Sopenharmony_ci // Deal with any leftover key or value data 4551cb0ef41Sopenharmony_ci if (lastPos < qs.length) { 4561cb0ef41Sopenharmony_ci if (eqIdx < eqLen) 4571cb0ef41Sopenharmony_ci key += StringPrototypeSlice(qs, lastPos); 4581cb0ef41Sopenharmony_ci else if (sepIdx < sepLen) 4591cb0ef41Sopenharmony_ci value += StringPrototypeSlice(qs, lastPos); 4601cb0ef41Sopenharmony_ci } else if (eqIdx === 0 && key.length === 0) { 4611cb0ef41Sopenharmony_ci // We ended on an empty substring 4621cb0ef41Sopenharmony_ci return obj; 4631cb0ef41Sopenharmony_ci } 4641cb0ef41Sopenharmony_ci 4651cb0ef41Sopenharmony_ci addKeyVal(obj, key, value, keyEncoded, valEncoded, decode); 4661cb0ef41Sopenharmony_ci 4671cb0ef41Sopenharmony_ci return obj; 4681cb0ef41Sopenharmony_ci} 4691cb0ef41Sopenharmony_ci 4701cb0ef41Sopenharmony_ci 4711cb0ef41Sopenharmony_ci/** 4721cb0ef41Sopenharmony_ci * V8 does not optimize functions with try-catch blocks, so we isolate them here 4731cb0ef41Sopenharmony_ci * to minimize the damage (Note: no longer true as of V8 5.4 -- but still will 4741cb0ef41Sopenharmony_ci * not be inlined). 4751cb0ef41Sopenharmony_ci * @param {string} s 4761cb0ef41Sopenharmony_ci * @param {(v: string) => string} decoder 4771cb0ef41Sopenharmony_ci * @returns {string} 4781cb0ef41Sopenharmony_ci */ 4791cb0ef41Sopenharmony_cifunction decodeStr(s, decoder) { 4801cb0ef41Sopenharmony_ci try { 4811cb0ef41Sopenharmony_ci return decoder(s); 4821cb0ef41Sopenharmony_ci } catch { 4831cb0ef41Sopenharmony_ci return QueryString.unescape(s, true); 4841cb0ef41Sopenharmony_ci } 4851cb0ef41Sopenharmony_ci} 486