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