strong-tie/inbound-calls
0
1"use strict";2 3const fastDecode = require("fast-decode-uri-component");4 5const plusRegex = /\+/g;6const Empty = function () {};7Empty.prototype = Object.create(null);8 9/**10 * @callback parse11 * @param {string} input12 */13function parse(input) {14 // Optimization: Use new Empty() instead of Object.create(null) for performance15 // v8 has a better optimization for initializing functions compared to Object16 const result = new Empty();17 18 if (typeof input !== "string") {19 return result;20 }21 22 let inputLength = input.length;23 let key = "";24 let value = "";25 let startingIndex = -1;26 let equalityIndex = -1;27 let shouldDecodeKey = false;28 let shouldDecodeValue = false;29 let keyHasPlus = false;30 let valueHasPlus = false;31 let hasBothKeyValuePair = false;32 let c = 0;33 34 // Have a boundary of input.length + 1 to access last pair inside the loop.35 for (let i = 0; i < inputLength + 1; i++) {36 c = i !== inputLength ? input.charCodeAt(i) : 38;37 38 // Handle '&' and end of line to pass the current values to result39 if (c === 38) {40 hasBothKeyValuePair = equalityIndex > startingIndex;41 42 // Optimization: Reuse equality index to store the end of key43 if (!hasBothKeyValuePair) {44 equalityIndex = i;45 }46 47 key = input.slice(startingIndex + 1, equalityIndex);48 49 // Add key/value pair only if the range size is greater than 1; a.k.a. contains at least "="50 if (hasBothKeyValuePair || key.length > 0) {51 // Optimization: Replace '+' with space52 if (keyHasPlus) {53 key = key.replace(plusRegex, " ");54 }55 56 // Optimization: Do not decode if it's not necessary.57 if (shouldDecodeKey) {58 key = fastDecode(key) || key;59 }60 61 if (hasBothKeyValuePair) {62 value = input.slice(equalityIndex + 1, i);63 64 if (valueHasPlus) {65 value = value.replace(plusRegex, " ");66 }67 68 if (shouldDecodeValue) {69 value = fastDecode(value) || value;70 }71 }72 const currentValue = result[key];73 74 if (currentValue === undefined) {75 result[key] = value;76 } else {77 // Optimization: value.pop is faster than Array.isArray(value)78 if (currentValue.pop) {79 currentValue.push(value);80 } else {81 result[key] = [currentValue, value];82 }83 }84 }85 86 // Reset reading key value pairs87 value = "";88 startingIndex = i;89 equalityIndex = i;90 shouldDecodeKey = false;91 shouldDecodeValue = false;92 keyHasPlus = false;93 valueHasPlus = false;94 }95 // Check '='96 else if (c === 61) {97 if (equalityIndex <= startingIndex) {98 equalityIndex = i;99 }100 // If '=' character occurs again, we should decode the input.101 else {102 shouldDecodeValue = true;103 }104 }105 // Check '+', and remember to replace it with empty space.106 else if (c === 43) {107 if (equalityIndex > startingIndex) {108 valueHasPlus = true;109 } else {110 keyHasPlus = true;111 }112 }113 // Check '%' character for encoding114 else if (c === 37) {115 if (equalityIndex > startingIndex) {116 shouldDecodeValue = true;117 } else {118 shouldDecodeKey = true;119 }120 }121 }122 123 return result;124}125 126module.exports = parse;127 