// 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); var consumed = sequenceReader.Position; var equalsDelimiter = GetEqualsForEncoding(); var andDelimiter = GetAndForEncoding(); while (!sequenceReader.End) { // TODO seems there is a bug with TryReadTo (advancePastDelimiter: true). It isn't advancing past the delimiter on second read. if (!sequenceReader.TryReadTo(out ReadOnlySequence <byte> key, equalsDelimiter, advancePastDelimiter: false) || !sequenceReader.IsNext(equalsDelimiter, true)) { if (sequenceReader.Consumed > KeyLengthLimit) { ThrowKeyTooLargeException(); } break; } if (key.Length > KeyLengthLimit) { ThrowKeyTooLargeException(); } if (!sequenceReader.TryReadTo(out ReadOnlySequence <byte> value, andDelimiter, false) || !sequenceReader.IsNext(andDelimiter, true)) { if (!isFinalBlock) { if (sequenceReader.Consumed - key.Length > ValueLengthLimit) { ThrowValueTooLargeException(); } break; } value = buffer.Slice(sequenceReader.Position); sequenceReader.Advance(value.Length); } if (value.Length > ValueLengthLimit) { ThrowValueTooLargeException(); } // Need to call ToArray if the key/value spans multiple segments var decodedKey = GetDecodedStringFromReadOnlySequence(key); var decodedValue = GetDecodedStringFromReadOnlySequence(value); AppendAndVerify(ref accumulator, decodedKey, decodedValue); consumed = sequenceReader.Position; } buffer = buffer.Slice(consumed); }
// Fast parsing for single span in ReadOnlySequence private void ParseFormValuesFast(ReadOnlySpan <byte> span, ref KeyValueAccumulator accumulator, bool isFinalBlock, out int consumed) { ReadOnlySpan <byte> key = default; ReadOnlySpan <byte> value = default; consumed = 0; var equalsDelimiter = GetEqualsForEncoding(); var andDelimiter = GetAndForEncoding(); while (span.Length > 0) { var equals = span.IndexOf(equalsDelimiter); if (equals == -1) { if (span.Length > KeyLengthLimit) { ThrowKeyTooLargeException(); } break; } if (equals > KeyLengthLimit) { ThrowKeyTooLargeException(); } key = span.Slice(0, equals); span = span.Slice(key.Length + equalsDelimiter.Length); value = span; var ampersand = span.IndexOf(andDelimiter); if (ampersand == -1) { if (span.Length > ValueLengthLimit) { ThrowValueTooLargeException(); return; } if (!isFinalBlock) { // 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 break; } // If we are on the final block, the remaining content in value is what we want to add to the KVAccumulator. // Clear out the remaining span such that the loop will exit. span = Span <byte> .Empty; } else { if (ampersand > ValueLengthLimit) { ThrowValueTooLargeException(); } value = span.Slice(0, ampersand); span = span.Slice(ampersand + andDelimiter.Length); } var decodedKey = GetDecodedString(key); var decodedValue = GetDecodedString(value); AppendAndVerify(ref accumulator, decodedKey, decodedValue); // Cover case where we don't have an ampersand at the end. consumed += key.Length + value.Length + (ampersand == -1 ? equalsDelimiter.Length : equalsDelimiter.Length + andDelimiter.Length); } }