| /** | |
| * @since 1.0.0 | |
| */ | |
| // Taken from https://github.com/anonrig/fast-querystring under MIT License | |
| const plusRegex = /\+/g | |
| const Empty: new () => Record<string, any> = function () {} as any | |
| Empty.prototype = Object.create(null) | |
| /** | |
| * @category parsing | |
| * @since 1.0.0 | |
| */ | |
| export function parse(input: string) { | |
| // 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: any) { | |
| 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: Record<string, any>): string { | |
| 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 = 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 = 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: string) { | |
| 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 | |
| } | |
Xet Storage Details
- Size:
- 7.75 kB
- Xet hash:
- 7ad86ca9dd54894968a373e2e9f15b47fb0ebf82e3ba8a6d22f5f2d3f51e398c
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.