You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							127 lines
						
					
					
						
							3.3 KiB
						
					
					
				
			
		
		
	
	
							127 lines
						
					
					
						
							3.3 KiB
						
					
					
				| "use strict";
 | |
| 
 | |
| 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;
 |