EdgeAIG's picture
download
raw
7.46 kB
/**
* @since 1.0.0
*/
// Taken from https://github.com/anonrig/fast-querystring under MIT License
const plusRegex = /\+/g;
const Empty = function () {};
Empty.prototype = /*#__PURE__*/Object.create(null);
/**
* @category parsing
* @since 1.0.0
*/
export function parse(input) {
// Optimization: Use new Empty() instead of Object.create(null) for performance
// v8 has a better optimization for initializing functions compared to Object
const result = new Empty();
if (typeof input !== "string") {
return result;
}
const inputLength = input.length;
let key = "";
let value = "";
let startingIndex = -1;
let equalityIndex = -1;
let shouldDecodeKey = false;
let shouldDecodeValue = false;
let keyHasPlus = false;
let valueHasPlus = false;
let hasBothKeyValuePair = false;
let c = 0;
// Have a boundary of input.length + 1 to access last pair inside the loop.
for (let i = 0; i < inputLength + 1; i++) {
c = i !== inputLength ? input.charCodeAt(i) : 38;
// Handle '&' and end of line to pass the current values to result
if (c === 38) {
hasBothKeyValuePair = equalityIndex > startingIndex;
// Optimization: Reuse equality index to store the end of key
if (!hasBothKeyValuePair) {
equalityIndex = i;
}
key = input.slice(startingIndex + 1, equalityIndex);
// Add key/value pair only if the range size is greater than 1; a.k.a. contains at least "="
if (hasBothKeyValuePair || key.length > 0) {
// Optimization: Replace '+' with space
if (keyHasPlus) {
key = key.replace(plusRegex, " ");
}
// Optimization: Do not decode if it's not necessary.
if (shouldDecodeKey) {
try {
key = decodeURIComponent(key) || key;
} catch {}
}
if (hasBothKeyValuePair) {
value = input.slice(equalityIndex + 1, i);
if (valueHasPlus) {
value = value.replace(plusRegex, " ");
}
if (shouldDecodeValue) {
try {
value = decodeURIComponent(value) || value;
} catch {}
}
}
const currentValue = result[key];
if (currentValue === undefined) {
result[key] = value;
} else {
// Optimization: value.pop is faster than Array.isArray(value)
if (currentValue.pop) {
currentValue.push(value);
} else {
result[key] = [currentValue, value];
}
}
}
// Reset reading key value pairs
value = "";
startingIndex = i;
equalityIndex = i;
shouldDecodeKey = false;
shouldDecodeValue = false;
keyHasPlus = false;
valueHasPlus = false;
}
// Check '='
else if (c === 61) {
if (equalityIndex <= startingIndex) {
equalityIndex = i;
}
// If '=' character occurs again, we should decode the input.
else {
shouldDecodeValue = true;
}
}
// Check '+', and remember to replace it with empty space.
else if (c === 43) {
if (equalityIndex > startingIndex) {
valueHasPlus = true;
} else {
keyHasPlus = true;
}
}
// Check '%' character for encoding
else if (c === 37) {
if (equalityIndex > startingIndex) {
shouldDecodeValue = true;
} else {
shouldDecodeKey = true;
}
}
}
return result;
}
function getAsPrimitive(value) {
const type = typeof value;
if (type === "string") {
// Length check is handled inside encodeString function
return encodeString(value);
} else if (type === "bigint" || type === "boolean") {
return "" + value;
} else if (type === "number" && Number.isFinite(value)) {
return value < 1e21 ? "" + value : encodeString("" + value);
}
return "";
}
/**
* @category encoding
* @since 1.0.0
*/
export function stringify(input) {
let result = "";
if (input === null || typeof input !== "object") {
return result;
}
const separator = "&";
const keys = Object.keys(input);
const keyLength = keys.length;
let valueLength = 0;
for (let i = 0; i < keyLength; i++) {
const key = keys[i];
const value = input[key];
const encodedKey = encodeString(key) + "=";
if (i) {
result += separator;
}
if (Array.isArray(value)) {
valueLength = value.length;
for (let j = 0; j < valueLength; j++) {
if (j) {
result += separator;
}
// Optimization: Dividing into multiple lines improves the performance.
// Since v8 does not need to care about the '+' character if it was one-liner.
result += encodedKey;
result += getAsPrimitive(value[j]);
}
} else {
result += encodedKey;
result += getAsPrimitive(value);
}
}
return result;
}
// -----------------------------------------------------------------------------
// This has been taken from Node.js project.
// Full implementation can be found from https://github.com/nodejs/node/blob/main/lib/internal/querystring.js
const hexTable = /*#__PURE__*/Array.from({
length: 256
}, (_, i) => "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase());
// These characters do not need escaping when generating query strings:
// ! - . _ ~
// ' ( ) *
// digits
// alpha (uppercase)
// alpha (lowercase)
// biome-ignore format: the array should not be formatted
const noEscape = /*#__PURE__*/new Int8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0 - 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 16 - 31
0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0,
// 32 - 47
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
// 48 - 63
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// 64 - 79
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
// 80 - 95
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// 96 - 111
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 // 112 - 127
]);
function encodeString(str) {
const len = str.length;
if (len === 0) return "";
let out = "";
let lastPos = 0;
let i = 0;
outer: for (; i < len; i++) {
let c = str.charCodeAt(i);
// ASCII
while (c < 0x80) {
if (noEscape[c] !== 1) {
if (lastPos < i) out += str.slice(lastPos, i);
lastPos = i + 1;
out += hexTable[c];
}
if (++i === len) break outer;
c = str.charCodeAt(i);
}
if (lastPos < i) out += str.slice(lastPos, i);
// Multi-byte characters ...
if (c < 0x800) {
lastPos = i + 1;
out += hexTable[0xc0 | c >> 6] + hexTable[0x80 | c & 0x3f];
continue;
}
if (c < 0xd800 || c >= 0xe000) {
lastPos = i + 1;
out += hexTable[0xe0 | c >> 12] + hexTable[0x80 | c >> 6 & 0x3f] + hexTable[0x80 | c & 0x3f];
continue;
}
// Surrogate pair
++i;
// This branch should never happen because all URLSearchParams entries
// should already be converted to USVString. But, included for
// completion's sake anyway.
if (i >= len) {
throw new Error("URI malformed");
}
const c2 = str.charCodeAt(i) & 0x3ff;
lastPos = i + 1;
c = 0x10000 + ((c & 0x3ff) << 10 | c2);
out += hexTable[0xf0 | c >> 18] + hexTable[0x80 | c >> 12 & 0x3f] + hexTable[0x80 | c >> 6 & 0x3f] + hexTable[0x80 | c & 0x3f];
}
if (lastPos === 0) return str;
if (lastPos < len) return out + str.slice(lastPos);
return out;
}
//# sourceMappingURL=QueryString.js.map

Xet Storage Details

Size:
7.46 kB
·
Xet hash:
f234317c82ee0bd4da3b05bf922e191e6f3d94bf53ab1910fcff25b017e741cc

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.