// Fast parsing for single span in ReadOnlySequence private void ParseFormValuesFast(ReadOnlySpan <byte> span, ref KeyValueAccumulator accumulator, bool isFinalBlock, out int consumed) { ReadOnlySpan <byte> key; ReadOnlySpan <byte> value; consumed = 0; var equalsDelimiter = GetEqualsForEncoding(); var andDelimiter = GetAndForEncoding(); while (span.Length > 0) { // Find the end of the key=value pair. var ampersand = span.IndexOf(andDelimiter); ReadOnlySpan <byte> keyValuePair; int equals; var foundAmpersand = ampersand != -1; if (foundAmpersand) { keyValuePair = span.Slice(0, ampersand); span = span.Slice(keyValuePair.Length + andDelimiter.Length); consumed += keyValuePair.Length + andDelimiter.Length; } else { // We can't know that what is currently read is the end of the form value, that's only the case if this is the final block // If we're not in the final block, then consume nothing if (!isFinalBlock) { // Don't buffer indefinately if ((uint)span.Length > (uint)KeyLengthLimit + (uint)ValueLengthLimit) { ThrowKeyOrValueTooLargeException(); } return; } keyValuePair = span; span = default; consumed += keyValuePair.Length; } equals = keyValuePair.IndexOf(equalsDelimiter); if (equals == -1) { // Too long for the whole segment to be a key. if (keyValuePair.Length > KeyLengthLimit) { ThrowKeyTooLargeException(); } // There is no more data, this segment must be "key" with no equals or value. key = keyValuePair; value = default; } else { key = keyValuePair.Slice(0, equals); if (key.Length > KeyLengthLimit) { ThrowKeyTooLargeException(); } value = keyValuePair.Slice(equals + equalsDelimiter.Length); if (value.Length > ValueLengthLimit) { ThrowValueTooLargeException(); } } var decodedKey = GetDecodedString(key); var decodedValue = GetDecodedString(value); AppendAndVerify(ref accumulator, decodedKey, decodedValue); } }
// For multi-segment parsing of a read only sequence private void ParseValuesSlow( ref ReadOnlySequence <byte> buffer, ref KeyValueAccumulator accumulator, bool isFinalBlock) { var sequenceReader = new SequenceReader <byte>(buffer); ReadOnlySequence <byte> keyValuePair; var consumed = sequenceReader.Position; var consumedBytes = default(long); var equalsDelimiter = GetEqualsForEncoding(); var andDelimiter = GetAndForEncoding(); while (!sequenceReader.End) { if (!sequenceReader.TryReadTo(out keyValuePair, andDelimiter)) { if (!isFinalBlock) { // Don't buffer indefinately if ((uint)(sequenceReader.Consumed - consumedBytes) > (uint)KeyLengthLimit + (uint)ValueLengthLimit) { ThrowKeyOrValueTooLargeException(); } break; } // This must be the final key=value pair keyValuePair = buffer.Slice(sequenceReader.Position); sequenceReader.Advance(keyValuePair.Length); } if (keyValuePair.IsSingleSegment) { ParseFormValuesFast(keyValuePair.FirstSpan, ref accumulator, isFinalBlock: true, out var segmentConsumed); Debug.Assert(segmentConsumed == keyValuePair.FirstSpan.Length); consumedBytes = sequenceReader.Consumed; consumed = sequenceReader.Position; continue; } var keyValueReader = new SequenceReader <byte>(keyValuePair); ReadOnlySequence <byte> value; if (keyValueReader.TryReadTo(out var key, equalsDelimiter)) { if (key.Length > KeyLengthLimit) { ThrowKeyTooLargeException(); } value = keyValuePair.Slice(keyValueReader.Position); if (value.Length > ValueLengthLimit) { ThrowValueTooLargeException(); } } else { // Too long for the whole segment to be a key. if (keyValuePair.Length > KeyLengthLimit) { ThrowKeyTooLargeException(); } // There is no more data, this segment must be "key" with no equals or value. key = keyValuePair; value = default; } var decodedKey = GetDecodedStringFromReadOnlySequence(key); var decodedValue = GetDecodedStringFromReadOnlySequence(value); AppendAndVerify(ref accumulator, decodedKey, decodedValue); consumedBytes = sequenceReader.Consumed; consumed = sequenceReader.Position; } buffer = buffer.Slice(consumed); }
/// <summary> /// Parse a query string into its component key and value parts. /// </summary> /// <param name="queryString">The raw query string value, with or without the leading '?'.</param> /// <returns>A collection of parsed keys and values, null if there are no entries.</returns> public static Dictionary <string, StringValues> ParseNullableQuery(string queryString) { var accumulator = new KeyValueAccumulator(); if (string.IsNullOrEmpty(queryString) || queryString == "?") { return(null); } int scanIndex = 0; if (queryString[0] == '?') { scanIndex = 1; } int textLength = queryString.Length; int equalIndex = queryString.IndexOf('='); if (equalIndex == -1) { equalIndex = textLength; } while (scanIndex < textLength) { int delimiterIndex = queryString.IndexOf('&', scanIndex); if (delimiterIndex == -1) { delimiterIndex = textLength; } if (equalIndex < delimiterIndex) { while (scanIndex != equalIndex && char.IsWhiteSpace(queryString[scanIndex])) { ++scanIndex; } string name = queryString.Substring(scanIndex, equalIndex - scanIndex); string value = queryString.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1); accumulator.Append( Uri.UnescapeDataString(name.Replace('+', ' ')), Uri.UnescapeDataString(value.Replace('+', ' '))); equalIndex = queryString.IndexOf('=', delimiterIndex); if (equalIndex == -1) { equalIndex = textLength; } } else { if (delimiterIndex > scanIndex) { accumulator.Append(queryString.Substring(scanIndex, delimiterIndex - scanIndex), string.Empty); } } scanIndex = delimiterIndex + 1; } if (!accumulator.HasValues) { return(null); } return(accumulator.GetResults()); }