Spaces:
Runtime error
Runtime error
| ; | |
| const fastDecode = require("fast-decode-uri-component"); | |
| const plusRegex = /\+/g; | |
| const Empty = function () {}; | |
| Empty.prototype = Object.create(null); | |
| /** | |
| * @callback parse | |
| * @param {string} input | |
| */ | |
| 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; | |
| } | |
| let 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) { | |
| key = fastDecode(key) || key; | |
| } | |
| if (hasBothKeyValuePair) { | |
| value = input.slice(equalityIndex + 1, i); | |
| if (valueHasPlus) { | |
| value = value.replace(plusRegex, " "); | |
| } | |
| if (shouldDecodeValue) { | |
| value = fastDecode(value) || value; | |
| } | |
| } | |
| 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; | |
| } | |
| module.exports = parse; | |