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
			| 
								 
											3 years ago
										 
									 | 
							
								"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;
							 |