Cleanup unused code (#1458)
This commit is contained in:
parent
02a4342908
commit
1294c00618
|
|
@ -1,312 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
|
||||||
{
|
|
||||||
public class UrlPathDecoder
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unescapes the string between given memory iterators in place.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="start">The iterator points to the beginning of the sequence.</param>
|
|
||||||
/// <param name="end">The iterator points to the byte behind the end of the sequence.</param>
|
|
||||||
/// <returns>The iterator points to the byte behind the end of the processed sequence.</returns>
|
|
||||||
public static MemoryPoolIterator Unescape(MemoryPoolIterator start, MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
// the slot to read the input
|
|
||||||
var reader = start;
|
|
||||||
|
|
||||||
// the slot to write the unescaped byte
|
|
||||||
var writer = reader;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (CompareIterators(ref reader, ref end))
|
|
||||||
{
|
|
||||||
return writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.Peek() == '%')
|
|
||||||
{
|
|
||||||
var decodeReader = reader;
|
|
||||||
|
|
||||||
// If decoding process succeeds, the writer iterator will be moved
|
|
||||||
// to the next write-ready location. On the other hand if the scanned
|
|
||||||
// percent-encodings cannot be interpreted as sequence of UTF-8 octets,
|
|
||||||
// these bytes should be copied to output as is.
|
|
||||||
// The decodeReader iterator is always moved to the first byte not yet
|
|
||||||
// be scanned after the process. A failed decoding means the chars
|
|
||||||
// between the reader and decodeReader can be copied to output untouched.
|
|
||||||
if (!DecodeCore(ref decodeReader, ref writer, end))
|
|
||||||
{
|
|
||||||
Copy(reader, decodeReader, ref writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
reader = decodeReader;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writer.Put((byte)reader.Take());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unescape the percent-encodings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reader">The iterator point to the first % char</param>
|
|
||||||
/// <param name="writer">The place to write to</param>
|
|
||||||
/// <param name="end">The end of the sequence</param>
|
|
||||||
private static bool DecodeCore(ref MemoryPoolIterator reader, ref MemoryPoolIterator writer, MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
// preserves the original head. if the percent-encodings cannot be interpreted as sequence of UTF-8 octets,
|
|
||||||
// bytes from this till the last scanned one will be copied to the memory pointed by writer.
|
|
||||||
var byte1 = UnescapePercentEncoding(ref reader, end);
|
|
||||||
|
|
||||||
if (byte1 == 0)
|
|
||||||
{
|
|
||||||
throw BadHttpRequestException.GetException(RequestRejectionReason.PathContainsNullCharacters);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte1 == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte1 <= 0x7F)
|
|
||||||
{
|
|
||||||
// first byte < U+007f, it is a single byte ASCII
|
|
||||||
writer.Put((byte)byte1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int byte2 = 0, byte3 = 0, byte4 = 0;
|
|
||||||
|
|
||||||
// anticipate more bytes
|
|
||||||
var currentDecodeBits = 0;
|
|
||||||
var byteCount = 1;
|
|
||||||
var expectValueMin = 0;
|
|
||||||
if ((byte1 & 0xE0) == 0xC0)
|
|
||||||
{
|
|
||||||
// 110x xxxx, expect one more byte
|
|
||||||
currentDecodeBits = byte1 & 0x1F;
|
|
||||||
byteCount = 2;
|
|
||||||
expectValueMin = 0x80;
|
|
||||||
}
|
|
||||||
else if ((byte1 & 0xF0) == 0xE0)
|
|
||||||
{
|
|
||||||
// 1110 xxxx, expect two more bytes
|
|
||||||
currentDecodeBits = byte1 & 0x0F;
|
|
||||||
byteCount = 3;
|
|
||||||
expectValueMin = 0x800;
|
|
||||||
}
|
|
||||||
else if ((byte1 & 0xF8) == 0xF0)
|
|
||||||
{
|
|
||||||
// 1111 0xxx, expect three more bytes
|
|
||||||
currentDecodeBits = byte1 & 0x07;
|
|
||||||
byteCount = 4;
|
|
||||||
expectValueMin = 0x10000;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// invalid first byte
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var remainingBytes = byteCount - 1;
|
|
||||||
while (remainingBytes > 0)
|
|
||||||
{
|
|
||||||
// read following three chars
|
|
||||||
if (CompareIterators(ref reader, ref end))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextItr = reader;
|
|
||||||
var nextByte = UnescapePercentEncoding(ref nextItr, end);
|
|
||||||
if (nextByte == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((nextByte & 0xC0) != 0x80)
|
|
||||||
{
|
|
||||||
// the follow up byte is not in form of 10xx xxxx
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDecodeBits = (currentDecodeBits << 6) | (nextByte & 0x3F);
|
|
||||||
remainingBytes--;
|
|
||||||
|
|
||||||
if (remainingBytes == 1 && currentDecodeBits >= 0x360 && currentDecodeBits <= 0x37F)
|
|
||||||
{
|
|
||||||
// this is going to end up in the range of 0xD800-0xDFFF UTF-16 surrogates that
|
|
||||||
// are not allowed in UTF-8;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingBytes == 2 && currentDecodeBits >= 0x110)
|
|
||||||
{
|
|
||||||
// this is going to be out of the upper Unicode bound 0x10FFFF.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
reader = nextItr;
|
|
||||||
if (byteCount - remainingBytes == 2)
|
|
||||||
{
|
|
||||||
byte2 = nextByte;
|
|
||||||
}
|
|
||||||
else if (byteCount - remainingBytes == 3)
|
|
||||||
{
|
|
||||||
byte3 = nextByte;
|
|
||||||
}
|
|
||||||
else if (byteCount - remainingBytes == 4)
|
|
||||||
{
|
|
||||||
byte4 = nextByte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentDecodeBits < expectValueMin)
|
|
||||||
{
|
|
||||||
// overlong encoding (e.g. using 2 bytes to encode something that only needed 1).
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all bytes are verified, write to the output
|
|
||||||
if (byteCount > 0)
|
|
||||||
{
|
|
||||||
writer.Put((byte)byte1);
|
|
||||||
}
|
|
||||||
if (byteCount > 1)
|
|
||||||
{
|
|
||||||
writer.Put((byte)byte2);
|
|
||||||
}
|
|
||||||
if (byteCount > 2)
|
|
||||||
{
|
|
||||||
writer.Put((byte)byte3);
|
|
||||||
}
|
|
||||||
if (byteCount > 3)
|
|
||||||
{
|
|
||||||
writer.Put((byte)byte4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Copy(MemoryPoolIterator head, MemoryPoolIterator tail, ref MemoryPoolIterator writer)
|
|
||||||
{
|
|
||||||
while (!CompareIterators(ref head, ref tail))
|
|
||||||
{
|
|
||||||
writer.Put((byte)head.Take());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the percent-encoding and try unescape it.
|
|
||||||
///
|
|
||||||
/// The operation first peek at the character the <paramref name="scan"/>
|
|
||||||
/// iterator points at. If it is % the <paramref name="scan"/> is then
|
|
||||||
/// moved on to scan the following to characters. If the two following
|
|
||||||
/// characters are hexadecimal literals they will be unescaped and the
|
|
||||||
/// value will be returned.
|
|
||||||
///
|
|
||||||
/// If the first character is not % the <paramref name="scan"/> iterator
|
|
||||||
/// will be removed beyond the location of % and -1 will be returned.
|
|
||||||
///
|
|
||||||
/// If the following two characters can't be successfully unescaped the
|
|
||||||
/// <paramref name="scan"/> iterator will be move behind the % and -1
|
|
||||||
/// will be returned.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scan">The value to read</param>
|
|
||||||
/// <param name="end">The end of the sequence</param>
|
|
||||||
/// <returns>The unescaped byte if success. Otherwise return -1.</returns>
|
|
||||||
private static int UnescapePercentEncoding(ref MemoryPoolIterator scan, MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
if (scan.Take() != '%')
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var probe = scan;
|
|
||||||
|
|
||||||
int value1 = ReadHex(ref probe, end);
|
|
||||||
if (value1 == -1)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int value2 = ReadHex(ref probe, end);
|
|
||||||
if (value2 == -1)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SkipUnescape(value1, value2))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
scan = probe;
|
|
||||||
return (value1 << 4) + value2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the next char and convert it into hexadecimal value.
|
|
||||||
///
|
|
||||||
/// The <paramref name="scan"/> iterator will be moved to the next
|
|
||||||
/// byte no matter no matter whether the operation successes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scan">The value to read</param>
|
|
||||||
/// <param name="end">The end of the sequence</param>
|
|
||||||
/// <returns>The hexadecimal value if successes, otherwise -1.</returns>
|
|
||||||
private static int ReadHex(ref MemoryPoolIterator scan, MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
if (CompareIterators(ref scan, ref end))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = scan.Take();
|
|
||||||
var isHead = (((value >= '0') && (value <= '9')) ||
|
|
||||||
((value >= 'A') && (value <= 'F')) ||
|
|
||||||
((value >= 'a') && (value <= 'f')));
|
|
||||||
|
|
||||||
if (!isHead)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value <= '9')
|
|
||||||
{
|
|
||||||
return value - '0';
|
|
||||||
}
|
|
||||||
else if (value <= 'F')
|
|
||||||
{
|
|
||||||
return (value - 'A') + 10;
|
|
||||||
}
|
|
||||||
else // a - f
|
|
||||||
{
|
|
||||||
return (value - 'a') + 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool SkipUnescape(int value1, int value2)
|
|
||||||
{
|
|
||||||
// skip %2F
|
|
||||||
if (value1 == 2 && value2 == 15)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CompareIterators(ref MemoryPoolIterator lhs, ref MemoryPoolIterator rhs)
|
|
||||||
{
|
|
||||||
// uses ref parameter to save cost of copying
|
|
||||||
return (lhs.Block == rhs.Block) && (lhs.Index == rhs.Index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,15 +14,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
public struct MemoryPoolIterator
|
public struct MemoryPoolIterator
|
||||||
{
|
{
|
||||||
private const int _maxULongByteLength = 20;
|
private const int _maxULongByteLength = 20;
|
||||||
private const ulong _xorPowerOfTwoToHighByte = (0x07ul |
|
|
||||||
0x06ul << 8 |
|
|
||||||
0x05ul << 16 |
|
|
||||||
0x04ul << 24 |
|
|
||||||
0x03ul << 32 |
|
|
||||||
0x02ul << 40 |
|
|
||||||
0x01ul << 48 ) + 1;
|
|
||||||
|
|
||||||
private static readonly int _vectorSpan = Vector<byte>.Count;
|
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static byte[] _numericBytesScratch;
|
private static byte[] _numericBytesScratch;
|
||||||
|
|
@ -135,668 +126,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
}
|
}
|
||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Skip(int bytesToSkip)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null && bytesToSkip > 0)
|
|
||||||
{
|
|
||||||
ThrowInvalidOperationException_SkipMoreThanAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always set wasLastBlock before checking .End to avoid race which may cause data loss
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var following = block.End - _index;
|
|
||||||
|
|
||||||
if (following >= bytesToSkip)
|
|
||||||
{
|
|
||||||
_index += bytesToSkip;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wasLastBlock)
|
|
||||||
{
|
|
||||||
ThrowInvalidOperationException_SkipMoreThanAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
SkipMultiBlock(bytesToSkip, following);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private void SkipMultiBlock(int bytesToSkip, int following)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
bytesToSkip -= following;
|
|
||||||
block = block.Next;
|
|
||||||
var index = block.Start;
|
|
||||||
|
|
||||||
// Always set wasLastBlock before checking .End to avoid race which may cause data loss
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
following = block.End - index;
|
|
||||||
|
|
||||||
if (following >= bytesToSkip)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index + bytesToSkip;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wasLastBlock)
|
|
||||||
{
|
|
||||||
ThrowInvalidOperationException_SkipMoreThanAvailable();
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ThrowInvalidOperationException_SkipMoreThanAvailable()
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Attempted to skip more bytes than available.");
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public int Peek()
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
|
|
||||||
// Always set wasLastBlock before checking .End to avoid race which may cause data loss
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
if (index < block.End)
|
|
||||||
{
|
|
||||||
return block.Array[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
return wasLastBlock ? -1 : PeekMultiBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private int PeekMultiBlock()
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
block = block.Next;
|
|
||||||
var index = block.Start;
|
|
||||||
|
|
||||||
// Always set wasLastBlock before checking .End to avoid race which may cause data loss
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
|
|
||||||
if (index < block.End)
|
|
||||||
{
|
|
||||||
return block.Array[index];
|
|
||||||
}
|
|
||||||
if (wasLastBlock)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Little-endian only!
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public unsafe bool TryPeekLong(out ulong longValue)
|
|
||||||
{
|
|
||||||
longValue = 0;
|
|
||||||
|
|
||||||
var block = _block;
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always set wasLastBlock before checking .End to avoid race which may cause data loss
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var blockBytes = block.End - _index;
|
|
||||||
|
|
||||||
if (blockBytes >= sizeof(ulong))
|
|
||||||
{
|
|
||||||
longValue = *(ulong*)(block.DataFixedPtr + _index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wasLastBlock ? false : TryPeekLongMultiBlock(ref longValue, blockBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private unsafe bool TryPeekLongMultiBlock(ref ulong longValue, int blockBytes)
|
|
||||||
{
|
|
||||||
// Each block will be filled with at least 2048 bytes before the Next pointer is set, so a long
|
|
||||||
// will cross at most one block boundary assuming there are at least 8 bytes following the iterator.
|
|
||||||
var nextBytes = sizeof(ulong) - blockBytes;
|
|
||||||
|
|
||||||
var block = _block;
|
|
||||||
if (block.Next.End - block.Next.Start < nextBytes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextLong = *(ulong*)(block.Next.DataFixedPtr + block.Next.Start);
|
|
||||||
|
|
||||||
if (blockBytes == 0)
|
|
||||||
{
|
|
||||||
// This case can not fall through to the else block since that would cause a 64-bit right shift
|
|
||||||
// on blockLong which is equivalent to no shift at all instead of shifting in all zeros.
|
|
||||||
// https://msdn.microsoft.com/en-us/library/xt18et0d.aspx
|
|
||||||
longValue = nextLong;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var blockLong = *(ulong*)(block.DataFixedPtr + block.End - sizeof(ulong));
|
|
||||||
|
|
||||||
// Ensure that the right shift has a ulong operand so a logical shift is performed.
|
|
||||||
longValue = (blockLong >> nextBytes * 8) | (nextLong << blockBytes * 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Seek(byte byte0)
|
|
||||||
{
|
|
||||||
int bytesScanned;
|
|
||||||
return Seek(byte0, out bytesScanned);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe int Seek(
|
|
||||||
byte byte0,
|
|
||||||
out int bytesScanned,
|
|
||||||
int limit = int.MaxValue)
|
|
||||||
{
|
|
||||||
bytesScanned = 0;
|
|
||||||
|
|
||||||
var block = _block;
|
|
||||||
if (block == null || limit <= 0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var following = block.End - index;
|
|
||||||
byte[] array;
|
|
||||||
var byte0Vector = GetVector(byte0);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
while (following == 0)
|
|
||||||
{
|
|
||||||
if (bytesScanned >= limit || wasLastBlock)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
block = block.Next;
|
|
||||||
index = block.Start;
|
|
||||||
wasLastBlock = block.Next == null;
|
|
||||||
following = block.End - index;
|
|
||||||
}
|
|
||||||
array = block.Array;
|
|
||||||
while (following > 0)
|
|
||||||
{
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
// Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079
|
|
||||||
if (Vector.IsHardwareAccelerated)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
if (following >= _vectorSpan)
|
|
||||||
{
|
|
||||||
var byte0Equals = Vector.Equals(new Vector<byte>(array, index), byte0Vector);
|
|
||||||
|
|
||||||
if (byte0Equals.Equals(Vector<byte>.Zero))
|
|
||||||
{
|
|
||||||
if (bytesScanned + _vectorSpan >= limit)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = index + (limit - bytesScanned);
|
|
||||||
bytesScanned = limit;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesScanned += _vectorSpan;
|
|
||||||
following -= _vectorSpan;
|
|
||||||
index += _vectorSpan;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_block = block;
|
|
||||||
|
|
||||||
var firstEqualByteIndex = LocateFirstFoundByte(byte0Equals);
|
|
||||||
var vectorBytesScanned = firstEqualByteIndex + 1;
|
|
||||||
|
|
||||||
if (bytesScanned + vectorBytesScanned > limit)
|
|
||||||
{
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = index + (limit - bytesScanned);
|
|
||||||
bytesScanned = limit;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_index = index + firstEqualByteIndex;
|
|
||||||
bytesScanned += vectorBytesScanned;
|
|
||||||
|
|
||||||
return byte0;
|
|
||||||
}
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var pCurrent = (block.DataFixedPtr + index);
|
|
||||||
var pEnd = pCurrent + Math.Min(following, limit - bytesScanned);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
bytesScanned++;
|
|
||||||
if (*pCurrent == byte0)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte0;
|
|
||||||
}
|
|
||||||
pCurrent++;
|
|
||||||
index++;
|
|
||||||
} while (pCurrent < pEnd);
|
|
||||||
|
|
||||||
following = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe int Seek(
|
|
||||||
byte byte0,
|
|
||||||
ref MemoryPoolIterator limit)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var following = block.End - index;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
while (following == 0)
|
|
||||||
{
|
|
||||||
if ((block == limit.Block && index > limit.Index) ||
|
|
||||||
wasLastBlock)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
block = block.Next;
|
|
||||||
index = block.Start;
|
|
||||||
wasLastBlock = block.Next == null;
|
|
||||||
following = block.End - index;
|
|
||||||
}
|
|
||||||
var array = block.Array;
|
|
||||||
while (following > 0)
|
|
||||||
{
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
// Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079
|
|
||||||
if (Vector.IsHardwareAccelerated)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
if (following >= _vectorSpan)
|
|
||||||
{
|
|
||||||
var byte0Equals = Vector.Equals(new Vector<byte>(array, index), GetVector(byte0));
|
|
||||||
|
|
||||||
if (byte0Equals.Equals(Vector<byte>.Zero))
|
|
||||||
{
|
|
||||||
if (block == limit.Block && index + _vectorSpan > limit.Index)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
following -= _vectorSpan;
|
|
||||||
index += _vectorSpan;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_block = block;
|
|
||||||
|
|
||||||
var firstEqualByteIndex = LocateFirstFoundByte(byte0Equals);
|
|
||||||
|
|
||||||
if (_block == limit.Block && index + firstEqualByteIndex > limit.Index)
|
|
||||||
{
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_index = index + firstEqualByteIndex;
|
|
||||||
|
|
||||||
return byte0;
|
|
||||||
}
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var pCurrent = (block.DataFixedPtr + index);
|
|
||||||
var pEnd = block == limit.Block ? block.DataFixedPtr + limit.Index + 1 : pCurrent + following;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (*pCurrent == byte0)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte0;
|
|
||||||
}
|
|
||||||
pCurrent++;
|
|
||||||
index++;
|
|
||||||
} while (pCurrent < pEnd);
|
|
||||||
|
|
||||||
following = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Seek(byte byte0, byte byte1)
|
|
||||||
{
|
|
||||||
var limit = new MemoryPoolIterator();
|
|
||||||
return Seek(byte0, byte1, ref limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe int Seek(
|
|
||||||
byte byte0,
|
|
||||||
byte byte1,
|
|
||||||
ref MemoryPoolIterator limit)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var following = block.End - index;
|
|
||||||
int byteIndex = int.MaxValue;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
while (following == 0)
|
|
||||||
{
|
|
||||||
if ((block == limit.Block && index > limit.Index) ||
|
|
||||||
wasLastBlock)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
block = block.Next;
|
|
||||||
index = block.Start;
|
|
||||||
wasLastBlock = block.Next == null;
|
|
||||||
following = block.End - index;
|
|
||||||
}
|
|
||||||
var array = block.Array;
|
|
||||||
while (following > 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
// Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079
|
|
||||||
if (Vector.IsHardwareAccelerated)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
if (following >= _vectorSpan)
|
|
||||||
{
|
|
||||||
var data = new Vector<byte>(array, index);
|
|
||||||
|
|
||||||
var byteEquals = Vector.Equals(data, GetVector(byte0));
|
|
||||||
byteEquals = Vector.ConditionalSelect(byteEquals, byteEquals, Vector.Equals(data, GetVector(byte1)));
|
|
||||||
|
|
||||||
if (!byteEquals.Equals(Vector<byte>.Zero))
|
|
||||||
{
|
|
||||||
byteIndex = LocateFirstFoundByte(byteEquals);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byteIndex == int.MaxValue)
|
|
||||||
{
|
|
||||||
following -= _vectorSpan;
|
|
||||||
index += _vectorSpan;
|
|
||||||
|
|
||||||
if (block == limit.Block && index > limit.Index)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_block = block;
|
|
||||||
|
|
||||||
_index = index + byteIndex;
|
|
||||||
|
|
||||||
if (block == limit.Block && _index > limit.Index)
|
|
||||||
{
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_index = index + byteIndex;
|
|
||||||
|
|
||||||
if (block == limit.Block && _index > limit.Index)
|
|
||||||
{
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return block.Array[index + byteIndex];
|
|
||||||
}
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
var pCurrent = (block.DataFixedPtr + index);
|
|
||||||
var pEnd = block == limit.Block ? block.DataFixedPtr + limit.Index + 1 : pCurrent + following;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (*pCurrent == byte0)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte0;
|
|
||||||
}
|
|
||||||
if (*pCurrent == byte1)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte1;
|
|
||||||
}
|
|
||||||
pCurrent++;
|
|
||||||
index++;
|
|
||||||
} while (pCurrent != pEnd);
|
|
||||||
|
|
||||||
following = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Seek(byte byte0, byte byte1, byte byte2)
|
|
||||||
{
|
|
||||||
var limit = new MemoryPoolIterator();
|
|
||||||
return Seek(byte0, byte1, byte2, ref limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe int Seek(
|
|
||||||
byte byte0,
|
|
||||||
byte byte1,
|
|
||||||
byte byte2,
|
|
||||||
ref MemoryPoolIterator limit)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var following = block.End - index;
|
|
||||||
int byteIndex = int.MaxValue;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
while (following == 0)
|
|
||||||
{
|
|
||||||
if ((block == limit.Block && index > limit.Index) ||
|
|
||||||
wasLastBlock)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
block = block.Next;
|
|
||||||
index = block.Start;
|
|
||||||
wasLastBlock = block.Next == null;
|
|
||||||
following = block.End - index;
|
|
||||||
}
|
|
||||||
var array = block.Array;
|
|
||||||
while (following > 0)
|
|
||||||
{
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
// Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079
|
|
||||||
if (Vector.IsHardwareAccelerated)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
if (following >= _vectorSpan)
|
|
||||||
{
|
|
||||||
var data = new Vector<byte>(array, index);
|
|
||||||
|
|
||||||
var byteEquals = Vector.Equals(data, GetVector(byte0));
|
|
||||||
byteEquals = Vector.ConditionalSelect(byteEquals, byteEquals, Vector.Equals(data, GetVector(byte1)));
|
|
||||||
byteEquals = Vector.ConditionalSelect(byteEquals, byteEquals, Vector.Equals(data, GetVector(byte2)));
|
|
||||||
|
|
||||||
if (!byteEquals.Equals(Vector<byte>.Zero))
|
|
||||||
{
|
|
||||||
byteIndex = LocateFirstFoundByte(byteEquals);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byteIndex == int.MaxValue)
|
|
||||||
{
|
|
||||||
following -= _vectorSpan;
|
|
||||||
index += _vectorSpan;
|
|
||||||
|
|
||||||
if (block == limit.Block && index > limit.Index)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_block = block;
|
|
||||||
|
|
||||||
_index = index + byteIndex;
|
|
||||||
|
|
||||||
if (block == limit.Block && _index > limit.Index)
|
|
||||||
{
|
|
||||||
// Ensure iterator is left at limit position
|
|
||||||
_index = limit.Index;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return block.Array[index + byteIndex];
|
|
||||||
}
|
|
||||||
// Need unit tests to test Vector path
|
|
||||||
#if !DEBUG
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
var pCurrent = (block.DataFixedPtr + index);
|
|
||||||
var pEnd = block == limit.Block ? block.DataFixedPtr + limit.Index + 1 : pCurrent + following;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (*pCurrent == byte0)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte0;
|
|
||||||
}
|
|
||||||
if (*pCurrent == byte1)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte1;
|
|
||||||
}
|
|
||||||
if (*pCurrent == byte2)
|
|
||||||
{
|
|
||||||
_block = block;
|
|
||||||
_index = index;
|
|
||||||
return byte2;
|
|
||||||
}
|
|
||||||
pCurrent++;
|
|
||||||
index++;
|
|
||||||
} while (pCurrent != pEnd);
|
|
||||||
|
|
||||||
following = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Locate the first of the found bytes
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="byteEquals"></param >
|
|
||||||
/// <returns>The first index of the result vector</returns>
|
|
||||||
// Force inlining (64 IL bytes, 91 bytes asm) Issue: https://github.com/dotnet/coreclr/issues/7386
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static int LocateFirstFoundByte(Vector<byte> byteEquals)
|
|
||||||
{
|
|
||||||
var vector64 = Vector.AsVectorUInt64(byteEquals);
|
|
||||||
ulong longValue = 0;
|
|
||||||
var i = 0;
|
|
||||||
// Pattern unrolled by jit https://github.com/dotnet/coreclr/pull/8001
|
|
||||||
for (; i < Vector<ulong>.Count; i++)
|
|
||||||
{
|
|
||||||
longValue = vector64[i];
|
|
||||||
if (longValue == 0) continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag least significant power of two bit
|
|
||||||
var powerOfTwoFlag = (longValue ^ (longValue - 1));
|
|
||||||
// Shift all powers of two into the high byte and extract
|
|
||||||
var foundByteIndex = (int)((powerOfTwoFlag * _xorPowerOfTwoToHighByte) >> 57);
|
|
||||||
// Single LEA instruction with jitted const (using function result)
|
|
||||||
return i * 8 + foundByteIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the data at the current location then move to the next available space.
|
/// Save the data at the current location then move to the next available space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -914,57 +244,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryPoolIterator CopyTo(byte[] array, int offset, int count, out int actual)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null)
|
|
||||||
{
|
|
||||||
actual = 0;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
var remaining = count;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// Determine if we might attempt to copy data from block.Next before
|
|
||||||
// calculating "following" so we don't risk skipping data that could
|
|
||||||
// be added after block.End when we decide to copy from block.Next.
|
|
||||||
// block.End will always be advanced before block.Next is set.
|
|
||||||
var wasLastBlock = block.Next == null;
|
|
||||||
var following = block.End - index;
|
|
||||||
if (remaining <= following)
|
|
||||||
{
|
|
||||||
actual = count;
|
|
||||||
if (array != null)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(block.Array, index, array, offset, remaining);
|
|
||||||
}
|
|
||||||
return new MemoryPoolIterator(block, index + remaining);
|
|
||||||
}
|
|
||||||
else if (wasLastBlock)
|
|
||||||
{
|
|
||||||
actual = count - remaining + following;
|
|
||||||
if (array != null)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(block.Array, index, array, offset, following);
|
|
||||||
}
|
|
||||||
return new MemoryPoolIterator(block, index + following);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (array != null)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(block.Array, index, array, offset, following);
|
|
||||||
}
|
|
||||||
offset += following;
|
|
||||||
remaining -= following;
|
|
||||||
block = block.Next;
|
|
||||||
index = block.Start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyFrom(byte[] data)
|
public void CopyFrom(byte[] data)
|
||||||
{
|
{
|
||||||
CopyFrom(data, 0, data.Length);
|
CopyFrom(data, 0, data.Length);
|
||||||
|
|
@ -1180,177 +459,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
|
||||||
|
|
||||||
CopyFrom(byteBuffer, position, _maxULongByteLength - position);
|
CopyFrom(byteBuffer, position, _maxULongByteLength - position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe string GetAsciiString(ref MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null || end.IsDefault)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var length = GetLength(end);
|
|
||||||
|
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var inputOffset = _index;
|
|
||||||
|
|
||||||
var asciiString = new string('\0', length);
|
|
||||||
|
|
||||||
fixed (char* outputStart = asciiString)
|
|
||||||
{
|
|
||||||
var output = outputStart;
|
|
||||||
var remaining = length;
|
|
||||||
|
|
||||||
var endBlock = end.Block;
|
|
||||||
var endIndex = end.Index;
|
|
||||||
|
|
||||||
var outputOffset = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int following = (block != endBlock ? block.End : endIndex) - inputOffset;
|
|
||||||
|
|
||||||
if (following > 0)
|
|
||||||
{
|
|
||||||
if (!AsciiUtilities.TryGetAsciiString(block.DataFixedPtr + inputOffset, output + outputOffset, following))
|
|
||||||
{
|
|
||||||
throw BadHttpRequestException.GetException(RequestRejectionReason.NonAsciiOrNullCharactersInInputString);
|
|
||||||
}
|
|
||||||
|
|
||||||
outputOffset += following;
|
|
||||||
remaining -= following;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remaining == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
block = block.Next;
|
|
||||||
inputOffset = block.Start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return asciiString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetUtf8String(ref MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null || end.IsDefault)
|
|
||||||
{
|
|
||||||
return default(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
if (end.Block == block)
|
|
||||||
{
|
|
||||||
return Encoding.UTF8.GetString(block.Array, index, end.Index - index);
|
|
||||||
}
|
|
||||||
|
|
||||||
var decoder = Encoding.UTF8.GetDecoder();
|
|
||||||
|
|
||||||
var length = GetLength(end);
|
|
||||||
var charLength = length;
|
|
||||||
// Worse case is 1 byte = 1 char
|
|
||||||
var chars = new char[charLength];
|
|
||||||
var charIndex = 0;
|
|
||||||
|
|
||||||
var remaining = length;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int bytesUsed;
|
|
||||||
int charsUsed;
|
|
||||||
bool completed;
|
|
||||||
var following = block.End - index;
|
|
||||||
if (remaining <= following)
|
|
||||||
{
|
|
||||||
decoder.Convert(
|
|
||||||
block.Array,
|
|
||||||
index,
|
|
||||||
remaining,
|
|
||||||
chars,
|
|
||||||
charIndex,
|
|
||||||
charLength - charIndex,
|
|
||||||
true,
|
|
||||||
out bytesUsed,
|
|
||||||
out charsUsed,
|
|
||||||
out completed);
|
|
||||||
return new string(chars, 0, charIndex + charsUsed);
|
|
||||||
}
|
|
||||||
else if (block.Next == null)
|
|
||||||
{
|
|
||||||
decoder.Convert(
|
|
||||||
block.Array,
|
|
||||||
index,
|
|
||||||
following,
|
|
||||||
chars,
|
|
||||||
charIndex,
|
|
||||||
charLength - charIndex,
|
|
||||||
true,
|
|
||||||
out bytesUsed,
|
|
||||||
out charsUsed,
|
|
||||||
out completed);
|
|
||||||
return new string(chars, 0, charIndex + charsUsed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
decoder.Convert(
|
|
||||||
block.Array,
|
|
||||||
index,
|
|
||||||
following,
|
|
||||||
chars,
|
|
||||||
charIndex,
|
|
||||||
charLength - charIndex,
|
|
||||||
false,
|
|
||||||
out bytesUsed,
|
|
||||||
out charsUsed,
|
|
||||||
out completed);
|
|
||||||
charIndex += charsUsed;
|
|
||||||
remaining -= following;
|
|
||||||
block = block.Next;
|
|
||||||
index = block.Start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ArraySegment<byte> GetArraySegment(MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
var block = _block;
|
|
||||||
if (block == null || end.IsDefault)
|
|
||||||
{
|
|
||||||
return default(ArraySegment<byte>);
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = _index;
|
|
||||||
if (end.Block == block)
|
|
||||||
{
|
|
||||||
return new ArraySegment<byte>(block.Array, index, end.Index - index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetArraySegmentMultiBlock(ref end);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
private ArraySegment<byte> GetArraySegmentMultiBlock(ref MemoryPoolIterator end)
|
|
||||||
{
|
|
||||||
var length = GetLength(end);
|
|
||||||
var array = new byte[length];
|
|
||||||
CopyTo(array, 0, length, out length);
|
|
||||||
return new ArraySegment<byte>(array, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static Vector<byte> GetVector(byte vectorByte)
|
|
||||||
{
|
|
||||||
// Vector<byte> .ctor doesn't become an intrinsic due to detection issue
|
|
||||||
// However this does cause it to become an intrinsic (with additional multiply and reg->reg copy)
|
|
||||||
// https://github.com/dotnet/coreclr/issues/7459#issuecomment-253965670
|
|
||||||
return Vector.AsVectorByte(new Vector<uint>(vectorByte * 0x01010101u));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel;
|
using Microsoft.AspNetCore.Server.Kestrel;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
|
|
@ -14,27 +15,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
private void FullAsciiRangeSupported()
|
private void FullAsciiRangeSupported()
|
||||||
{
|
{
|
||||||
var byteRange = Enumerable.Range(1, 127).Select(x => (byte)x).ToArray();
|
var byteRange = Enumerable.Range(1, 127).Select(x => (byte)x).ToArray();
|
||||||
using (var pool = new MemoryPool())
|
var s = new Span<byte>(byteRange).GetAsciiStringNonNullCharacters();
|
||||||
|
|
||||||
|
Assert.Equal(s.Length, byteRange.Length);
|
||||||
|
|
||||||
|
for (var i = 1; i < byteRange.Length; i++)
|
||||||
{
|
{
|
||||||
var mem = pool.Lease();
|
var sb = (byte)s[i];
|
||||||
mem.GetIterator().CopyFrom(byteRange);
|
var b = byteRange[i];
|
||||||
|
|
||||||
var begin = mem.GetIterator();
|
Assert.Equal(sb, b);
|
||||||
var end = GetIterator(begin, byteRange.Length);
|
|
||||||
|
|
||||||
var s = begin.GetAsciiString(ref end);
|
|
||||||
|
|
||||||
Assert.Equal(s.Length, byteRange.Length);
|
|
||||||
|
|
||||||
for (var i = 1; i < byteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var sb = (byte)s[i];
|
|
||||||
var b = byteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(sb, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,123 +39,29 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
var byteRange = Enumerable.Range(1, length).Select(x => (byte)x).ToArray();
|
var byteRange = Enumerable.Range(1, length).Select(x => (byte)x).ToArray();
|
||||||
byteRange[position] = b;
|
byteRange[position] = b;
|
||||||
|
|
||||||
using (var pool = new MemoryPool())
|
Assert.Throws<InvalidOperationException>(() => new Span<byte>(byteRange).GetAsciiStringNonNullCharacters());
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
mem.GetIterator().CopyFrom(byteRange);
|
|
||||||
|
|
||||||
var begin = mem.GetIterator();
|
|
||||||
var end = GetIterator(begin, byteRange.Length);
|
|
||||||
|
|
||||||
Assert.Throws<BadHttpRequestException>(() => begin.GetAsciiString(ref end));
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
private void MultiBlockProducesCorrectResults()
|
|
||||||
{
|
|
||||||
var byteRange = Enumerable.Range(0, 512 + 64).Select(x => (byte)((x & 0x7f) | 0x01)).ToArray();
|
|
||||||
var expectedByteRange = byteRange
|
|
||||||
.Concat(byteRange)
|
|
||||||
.Concat(byteRange)
|
|
||||||
.Concat(byteRange)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem0 = pool.Lease();
|
|
||||||
var mem1 = pool.Lease();
|
|
||||||
var mem2 = pool.Lease();
|
|
||||||
var mem3 = pool.Lease();
|
|
||||||
mem0.GetIterator().CopyFrom(byteRange);
|
|
||||||
mem1.GetIterator().CopyFrom(byteRange);
|
|
||||||
mem2.GetIterator().CopyFrom(byteRange);
|
|
||||||
mem3.GetIterator().CopyFrom(byteRange);
|
|
||||||
|
|
||||||
mem0.Next = mem1;
|
|
||||||
mem1.Next = mem2;
|
|
||||||
mem2.Next = mem3;
|
|
||||||
|
|
||||||
var begin = mem0.GetIterator();
|
|
||||||
var end = GetIterator(begin, expectedByteRange.Length);
|
|
||||||
|
|
||||||
var s = begin.GetAsciiString(ref end);
|
|
||||||
|
|
||||||
Assert.Equal(s.Length, expectedByteRange.Length);
|
|
||||||
|
|
||||||
for (var i = 0; i < expectedByteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var sb = (byte)((s[i] & 0x7f) | 0x01);
|
|
||||||
var b = expectedByteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(sb, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Return(mem0);
|
|
||||||
pool.Return(mem1);
|
|
||||||
pool.Return(mem2);
|
|
||||||
pool.Return(mem3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
private void LargeAllocationProducesCorrectResults()
|
private void LargeAllocationProducesCorrectResults()
|
||||||
{
|
{
|
||||||
var byteRange = Enumerable.Range(0, 16384 + 64).Select(x => (byte)((x & 0x7f) | 0x01)).ToArray();
|
var byteRange = Enumerable.Range(0, 16384 + 64).Select(x => (byte)((x & 0x7f) | 0x01)).ToArray();
|
||||||
var expectedByteRange = byteRange.Concat(byteRange).ToArray();
|
var expectedByteRange = byteRange.Concat(byteRange).ToArray();
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
|
var s = new Span<byte>(expectedByteRange).GetAsciiStringNonNullCharacters();
|
||||||
|
|
||||||
|
Assert.Equal(expectedByteRange.Length, s.Length);
|
||||||
|
|
||||||
|
for (var i = 0; i < expectedByteRange.Length; i++)
|
||||||
{
|
{
|
||||||
var mem0 = pool.Lease();
|
var sb = (byte)((s[i] & 0x7f) | 0x01);
|
||||||
var mem1 = pool.Lease();
|
var b = expectedByteRange[i];
|
||||||
mem0.GetIterator().CopyFrom(byteRange);
|
|
||||||
mem1.GetIterator().CopyFrom(byteRange);
|
|
||||||
|
|
||||||
var lastBlock = mem0;
|
Assert.Equal(sb, b);
|
||||||
while (lastBlock.Next != null)
|
|
||||||
{
|
|
||||||
lastBlock = lastBlock.Next;
|
|
||||||
}
|
|
||||||
lastBlock.Next = mem1;
|
|
||||||
|
|
||||||
var begin = mem0.GetIterator();
|
|
||||||
var end = GetIterator(begin, expectedByteRange.Length);
|
|
||||||
|
|
||||||
var s = begin.GetAsciiString(ref end);
|
|
||||||
|
|
||||||
Assert.Equal(expectedByteRange.Length, s.Length);
|
|
||||||
|
|
||||||
for (var i = 0; i < expectedByteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var sb = (byte)((s[i] & 0x7f) | 0x01);
|
|
||||||
var b = expectedByteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(sb, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
var block = mem0;
|
|
||||||
while (block != null)
|
|
||||||
{
|
|
||||||
var returnBlock = block;
|
|
||||||
block = block.Next;
|
|
||||||
pool.Return(returnBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MemoryPoolIterator GetIterator(MemoryPoolIterator begin, int displacement)
|
|
||||||
{
|
|
||||||
var result = begin;
|
|
||||||
for (int i = 0; i < displacement; ++i)
|
|
||||||
{
|
|
||||||
result.Take();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,105 +8,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
public class MemoryPoolBlockTests
|
public class MemoryPoolBlockTests
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public void SeekWorks()
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var block = pool.Lease();
|
|
||||||
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
|
|
||||||
{
|
|
||||||
block.Array[block.End++] = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
var iterator = block.GetIterator();
|
|
||||||
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
|
|
||||||
{
|
|
||||||
var hit = iterator;
|
|
||||||
hit.Seek(ch);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(ch, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(byte.MaxValue, ch);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(ch, byte.MaxValue, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(byte.MaxValue, ch, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(ch, byte.MaxValue, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Return(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SeekWorksAcrossBlocks()
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var block1 = pool.Lease();
|
|
||||||
var block2 = block1.Next = pool.Lease();
|
|
||||||
var block3 = block2.Next = pool.Lease();
|
|
||||||
|
|
||||||
foreach (var ch in Enumerable.Range(0, 34).Select(x => (byte)x))
|
|
||||||
{
|
|
||||||
block1.Array[block1.End++] = ch;
|
|
||||||
}
|
|
||||||
foreach (var ch in Enumerable.Range(34, 25).Select(x => (byte)x))
|
|
||||||
{
|
|
||||||
block2.Array[block2.End++] = ch;
|
|
||||||
}
|
|
||||||
foreach (var ch in Enumerable.Range(59, 197).Select(x => (byte)x))
|
|
||||||
{
|
|
||||||
block3.Array[block3.End++] = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
var iterator = block1.GetIterator();
|
|
||||||
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
|
|
||||||
{
|
|
||||||
var hit = iterator;
|
|
||||||
hit.Seek(ch);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(ch, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(byte.MaxValue, ch);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(ch, byte.MaxValue, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(byte.MaxValue, ch, byte.MaxValue);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
|
|
||||||
hit = iterator;
|
|
||||||
hit.Seek(byte.MaxValue, byte.MaxValue, ch);
|
|
||||||
Assert.Equal(ch, iterator.GetLength(hit));
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Return(block1);
|
|
||||||
pool.Return(block2);
|
|
||||||
pool.Return(block3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetLengthBetweenIteratorsWorks()
|
public void GetLengthBetweenIteratorsWorks()
|
||||||
{
|
{
|
||||||
|
|
@ -192,87 +94,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
pool.Return(block2);
|
pool.Return(block2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CopyToCorrectlyTraversesBlocks()
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var block1 = pool.Lease();
|
|
||||||
var block2 = block1.Next = pool.Lease();
|
|
||||||
|
|
||||||
for (int i = 0; i < 128; i++)
|
|
||||||
{
|
|
||||||
block1.Array[block1.End++] = (byte)i;
|
|
||||||
}
|
|
||||||
for (int i = 128; i < 256; i++)
|
|
||||||
{
|
|
||||||
block2.Array[block2.End++] = (byte)i;
|
|
||||||
}
|
|
||||||
|
|
||||||
var beginIterator = block1.GetIterator();
|
|
||||||
|
|
||||||
var array = new byte[256];
|
|
||||||
int actual;
|
|
||||||
var endIterator = beginIterator.CopyTo(array, 0, 256, out actual);
|
|
||||||
|
|
||||||
Assert.Equal(256, actual);
|
|
||||||
|
|
||||||
for (int i = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
Assert.Equal(i, array[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
endIterator.CopyTo(array, 0, 256, out actual);
|
|
||||||
Assert.Equal(0, actual);
|
|
||||||
|
|
||||||
pool.Return(block1);
|
|
||||||
pool.Return(block2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CopyFromCorrectlyTraversesBlocks()
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var block1 = pool.Lease();
|
|
||||||
var start = block1.GetIterator();
|
|
||||||
var end = start;
|
|
||||||
var bufferSize = block1.Data.Count * 3;
|
|
||||||
var buffer = new byte[bufferSize];
|
|
||||||
|
|
||||||
for (int i = 0; i < bufferSize; i++)
|
|
||||||
{
|
|
||||||
buffer[i] = (byte)(i % 73);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Null(block1.Next);
|
|
||||||
|
|
||||||
end.CopyFrom(new ArraySegment<byte>(buffer));
|
|
||||||
|
|
||||||
Assert.NotNull(block1.Next);
|
|
||||||
|
|
||||||
for (int i = 0; i < bufferSize; i++)
|
|
||||||
{
|
|
||||||
Assert.Equal(i % 73, start.Take());
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(-1, start.Take());
|
|
||||||
Assert.Equal(start.Block, end.Block);
|
|
||||||
Assert.Equal(start.Index, end.Index);
|
|
||||||
|
|
||||||
var block = block1;
|
|
||||||
while (block != null)
|
|
||||||
{
|
|
||||||
var returnBlock = block;
|
|
||||||
block = block.Next;
|
|
||||||
|
|
||||||
pool.Return(returnBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsEndCorrectlyTraversesBlocks()
|
public void IsEndCorrectlyTraversesBlocks()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
public static MemoryPoolIterator Add(this MemoryPoolIterator iterator, int count)
|
public static MemoryPoolIterator Add(this MemoryPoolIterator iterator, int count)
|
||||||
{
|
{
|
||||||
int actual;
|
for (int i = 0; i < count; i++)
|
||||||
return iterator.CopyTo(new byte[count], 0, count, out actual);
|
{
|
||||||
|
iterator.Take();
|
||||||
|
}
|
||||||
|
return iterator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,80 +30,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
_pool.Dispose();
|
_pool.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestFindFirstEqualByte()
|
|
||||||
{
|
|
||||||
var bytes = Enumerable.Repeat<byte>(0xff, Vector<byte>.Count).ToArray();
|
|
||||||
for (int i = 0; i < Vector<byte>.Count; i++)
|
|
||||||
{
|
|
||||||
Vector<byte> vector = new Vector<byte>(bytes);
|
|
||||||
Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(vector));
|
|
||||||
bytes[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Vector<byte>.Count; i++)
|
|
||||||
{
|
|
||||||
bytes[i] = 1;
|
|
||||||
Vector<byte> vector = new Vector<byte>(bytes);
|
|
||||||
Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(vector));
|
|
||||||
bytes[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("a", "a", 'a', 0)]
|
|
||||||
[InlineData("ab", "a", 'a', 0)]
|
|
||||||
[InlineData("aab", "a", 'a', 0)]
|
|
||||||
[InlineData("acab", "a", 'a', 0)]
|
|
||||||
[InlineData("acab", "c", 'c', 1)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyz", "lo", 'l', 11)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyz", "ol", 'l', 11)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyz", "ll", 'l', 11)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyz", "rml", 'l', 11)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyz", "mlr", 'l', 11)]
|
|
||||||
[InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)]
|
|
||||||
[InlineData("aaaaaaaaaaalmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'l', 11)]
|
|
||||||
[InlineData("aaaaaaaaaaacmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'm', 12)]
|
|
||||||
[InlineData("aaaaaaaaaaarmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", "lmr", 'r', 11)]
|
|
||||||
[InlineData("/localhost:5000/PATH/%2FPATH2/ HTTP/1.1", " %?", '%', 21)]
|
|
||||||
[InlineData("/localhost:5000/PATH/%2FPATH2/?key=value HTTP/1.1", " %?", '%', 21)]
|
|
||||||
[InlineData("/localhost:5000/PATH/PATH2/?key=value HTTP/1.1", " %?", '?', 27)]
|
|
||||||
[InlineData("/localhost:5000/PATH/PATH2/ HTTP/1.1", " %?", ' ', 27)]
|
|
||||||
public void MemorySeek(string raw, string search, char expectResult, int expectIndex)
|
|
||||||
{
|
|
||||||
var block = _pool.Lease();
|
|
||||||
var chars = raw.ToCharArray().Select(c => (byte)c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars, 0, block.Array, block.Start, chars.Length);
|
|
||||||
block.End += chars.Length;
|
|
||||||
|
|
||||||
var begin = block.GetIterator();
|
|
||||||
var searchFor = search.ToCharArray();
|
|
||||||
|
|
||||||
int found = -1;
|
|
||||||
if (searchFor.Length == 1)
|
|
||||||
{
|
|
||||||
found = begin.Seek((byte)searchFor[0]);
|
|
||||||
}
|
|
||||||
else if (searchFor.Length == 2)
|
|
||||||
{
|
|
||||||
found = begin.Seek((byte)searchFor[0], (byte)searchFor[1]);
|
|
||||||
}
|
|
||||||
else if (searchFor.Length == 3)
|
|
||||||
{
|
|
||||||
found = begin.Seek((byte)searchFor[0], (byte)searchFor[1], (byte)searchFor[2]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.False(true, "Invalid test sample.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(expectResult, found);
|
|
||||||
Assert.Equal(expectIndex, begin.Index - block.Start);
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Put()
|
public void Put()
|
||||||
{
|
{
|
||||||
|
|
@ -241,728 +167,20 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void PeekLong()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var block = _pool.Lease();
|
|
||||||
var bytes = BitConverter.GetBytes(0x0102030405060708UL);
|
|
||||||
Buffer.BlockCopy(bytes, 0, block.Array, block.Start, bytes.Length);
|
|
||||||
block.End += bytes.Length;
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
var originalIndex = scan.Index;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
ulong result;
|
|
||||||
Assert.True(scan.TryPeekLong(out result));
|
|
||||||
Assert.Equal(0x0102030405060708UL, result);
|
|
||||||
Assert.Equal(originalIndex, scan.Index);
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(0)]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(2)]
|
|
||||||
[InlineData(3)]
|
|
||||||
[InlineData(4)]
|
|
||||||
[InlineData(5)]
|
|
||||||
[InlineData(6)]
|
|
||||||
[InlineData(7)]
|
|
||||||
public void PeekLongNotEnoughBytes(int totalBytes)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var block = _pool.Lease();
|
|
||||||
var bytes = BitConverter.GetBytes(0x0102030405060708UL);
|
|
||||||
var bytesLength = totalBytes;
|
|
||||||
Buffer.BlockCopy(bytes, 0, block.Array, block.Start, bytesLength);
|
|
||||||
block.End += bytesLength;
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
var originalIndex = scan.Index;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
ulong result;
|
|
||||||
Assert.False(scan.TryPeekLong(out result));
|
|
||||||
Assert.Equal(originalIndex, scan.Index);
|
|
||||||
_pool.Return(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(0)]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(2)]
|
|
||||||
[InlineData(3)]
|
|
||||||
[InlineData(4)]
|
|
||||||
[InlineData(5)]
|
|
||||||
[InlineData(6)]
|
|
||||||
[InlineData(7)]
|
|
||||||
public void PeekLongNotEnoughBytesAtBlockBoundary(int firstBlockBytes)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedResult = 0x0102030405060708UL;
|
|
||||||
var nextBlockBytes = 7 - firstBlockBytes;
|
|
||||||
|
|
||||||
var block = _pool.Lease();
|
|
||||||
block.End += firstBlockBytes;
|
|
||||||
|
|
||||||
var nextBlock = _pool.Lease();
|
|
||||||
nextBlock.End += nextBlockBytes;
|
|
||||||
|
|
||||||
block.Next = nextBlock;
|
|
||||||
|
|
||||||
var bytes = BitConverter.GetBytes(expectedResult);
|
|
||||||
Buffer.BlockCopy(bytes, 0, block.Array, block.Start, firstBlockBytes);
|
|
||||||
Buffer.BlockCopy(bytes, firstBlockBytes, nextBlock.Array, nextBlock.Start, nextBlockBytes);
|
|
||||||
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
var originalIndex = scan.Index;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
ulong result;
|
|
||||||
Assert.False(scan.TryPeekLong(out result));
|
|
||||||
Assert.Equal(originalIndex, scan.Index);
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
_pool.Return(nextBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(0)]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(2)]
|
|
||||||
[InlineData(3)]
|
|
||||||
[InlineData(4)]
|
|
||||||
[InlineData(5)]
|
|
||||||
[InlineData(6)]
|
|
||||||
[InlineData(7)]
|
|
||||||
[InlineData(8)]
|
|
||||||
public void PeekLongAtBlockBoundary(int firstBlockBytes)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedResult = 0x0102030405060708UL;
|
|
||||||
var nonZeroData = 0xFF00FFFF0000FFFFUL;
|
|
||||||
var nextBlockBytes = 8 - firstBlockBytes;
|
|
||||||
|
|
||||||
var block = _pool.Lease();
|
|
||||||
block.Start += 8;
|
|
||||||
block.End = block.Start + firstBlockBytes;
|
|
||||||
|
|
||||||
var nextBlock = _pool.Lease();
|
|
||||||
nextBlock.Start += 8;
|
|
||||||
nextBlock.End = nextBlock.Start + nextBlockBytes;
|
|
||||||
|
|
||||||
block.Next = nextBlock;
|
|
||||||
|
|
||||||
var bytes = BitConverter.GetBytes(expectedResult);
|
|
||||||
Buffer.BlockCopy(bytes, 0, block.Array, block.Start, firstBlockBytes);
|
|
||||||
Buffer.BlockCopy(bytes, firstBlockBytes, nextBlock.Array, nextBlock.Start, nextBlockBytes);
|
|
||||||
|
|
||||||
// Fill in surrounding bytes with non-zero data
|
|
||||||
var nonZeroBytes = BitConverter.GetBytes(nonZeroData);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, block.Array, block.Start - 8, 8);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, block.Array, block.End, 8);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, nextBlock.Array, nextBlock.Start - 8, 8);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, nextBlock.Array, nextBlock.End, 8);
|
|
||||||
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
var originalIndex = scan.Index;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
ulong result;
|
|
||||||
Assert.True(scan.TryPeekLong(out result));
|
|
||||||
Assert.Equal(expectedResult, result);
|
|
||||||
Assert.Equal(originalIndex, scan.Index);
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
_pool.Return(nextBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(0)]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(2)]
|
|
||||||
[InlineData(3)]
|
|
||||||
[InlineData(4)]
|
|
||||||
[InlineData(5)]
|
|
||||||
[InlineData(6)]
|
|
||||||
[InlineData(7)]
|
|
||||||
[InlineData(8)]
|
|
||||||
public void PeekLongAtBlockBoundarayWithMostSignificatBitsSet(int firstBlockBytes)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var expectedResult = 0xFF02030405060708UL;
|
|
||||||
var nonZeroData = 0xFF00FFFF0000FFFFUL;
|
|
||||||
var nextBlockBytes = 8 - firstBlockBytes;
|
|
||||||
|
|
||||||
var block = _pool.Lease();
|
|
||||||
block.Start += 8;
|
|
||||||
block.End = block.Start + firstBlockBytes;
|
|
||||||
|
|
||||||
var nextBlock = _pool.Lease();
|
|
||||||
nextBlock.Start += 8;
|
|
||||||
nextBlock.End = nextBlock.Start + nextBlockBytes;
|
|
||||||
|
|
||||||
block.Next = nextBlock;
|
|
||||||
|
|
||||||
var expectedBytes = BitConverter.GetBytes(expectedResult);
|
|
||||||
Buffer.BlockCopy(expectedBytes, 0, block.Array, block.Start, firstBlockBytes);
|
|
||||||
Buffer.BlockCopy(expectedBytes, firstBlockBytes, nextBlock.Array, nextBlock.Start, nextBlockBytes);
|
|
||||||
|
|
||||||
// Fill in surrounding bytes with non-zero data
|
|
||||||
var nonZeroBytes = BitConverter.GetBytes(nonZeroData);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, block.Array, block.Start - 8, 8);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, block.Array, block.End, 8);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, nextBlock.Array, nextBlock.Start - 8, 8);
|
|
||||||
Buffer.BlockCopy(nonZeroBytes, 0, nextBlock.Array, nextBlock.End, 8);
|
|
||||||
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
var originalIndex = scan.Index;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
ulong result;
|
|
||||||
Assert.True(scan.TryPeekLong(out result));
|
|
||||||
Assert.Equal(expectedResult, result);
|
|
||||||
Assert.Equal(originalIndex, scan.Index);
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
_pool.Return(nextBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(0)]
|
|
||||||
[InlineData(1)]
|
|
||||||
[InlineData(2)]
|
|
||||||
[InlineData(3)]
|
|
||||||
[InlineData(4)]
|
|
||||||
[InlineData(5)]
|
|
||||||
[InlineData(6)]
|
|
||||||
[InlineData(7)]
|
|
||||||
[InlineData(8)]
|
|
||||||
[InlineData(9)]
|
|
||||||
public void SkipAtBlockBoundary(int blockBytes)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var nextBlockBytes = 10 - blockBytes;
|
|
||||||
|
|
||||||
var block = _pool.Lease();
|
|
||||||
block.End += blockBytes;
|
|
||||||
|
|
||||||
var nextBlock = _pool.Lease();
|
|
||||||
nextBlock.End += nextBlockBytes;
|
|
||||||
|
|
||||||
block.Next = nextBlock;
|
|
||||||
|
|
||||||
var bytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
||||||
Buffer.BlockCopy(bytes, 0, block.Array, block.Start, blockBytes);
|
|
||||||
Buffer.BlockCopy(bytes, blockBytes, nextBlock.Array, nextBlock.Start, nextBlockBytes);
|
|
||||||
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
var originalIndex = scan.Index;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
scan.Skip(8);
|
|
||||||
var result = scan.Take();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(0x08, result);
|
|
||||||
Assert.NotEqual(originalIndex, scan.Index);
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
_pool.Return(nextBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SkipThrowsWhenSkippingMoreBytesThanAvailableInSingleBlock()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var block = _pool.Lease();
|
|
||||||
block.End += 5;
|
|
||||||
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
|
|
||||||
// Act/Assert
|
|
||||||
Assert.ThrowsAny<InvalidOperationException>(() => scan.Skip(8));
|
|
||||||
|
|
||||||
_pool.Return(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SkipThrowsWhenSkippingMoreBytesThanAvailableInMultipleBlocks()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var firstBlock = _pool.Lease();
|
|
||||||
firstBlock.End += 3;
|
|
||||||
|
|
||||||
var middleBlock = _pool.Lease();
|
|
||||||
middleBlock.End += 1;
|
|
||||||
firstBlock.Next = middleBlock;
|
|
||||||
|
|
||||||
var finalBlock = _pool.Lease();
|
|
||||||
finalBlock.End += 2;
|
|
||||||
middleBlock.Next = finalBlock;
|
|
||||||
|
|
||||||
var scan = firstBlock.GetIterator();
|
|
||||||
|
|
||||||
// Act/Assert
|
|
||||||
Assert.ThrowsAny<InvalidOperationException>(() => scan.Skip(8));
|
|
||||||
|
|
||||||
_pool.Return(firstBlock);
|
|
||||||
_pool.Return(middleBlock);
|
|
||||||
_pool.Return(finalBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(SeekByteLimitData))]
|
|
||||||
public void TestSeekByteLimitWithinSameBlock(string input, char seek, int limit, int expectedBytesScanned, int expectedReturnValue)
|
|
||||||
{
|
|
||||||
MemoryPoolBlock block = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
|
|
||||||
block = _pool.Lease();
|
|
||||||
var chars = input.ToString().ToCharArray().Select(c => (byte) c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars, 0, block.Array, block.Start, chars.Length);
|
|
||||||
block.End += chars.Length;
|
|
||||||
var scan = block.GetIterator();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
int bytesScanned;
|
|
||||||
var returnValue = scan.Seek((byte)seek, out bytesScanned, limit);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedBytesScanned, bytesScanned);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue);
|
|
||||||
|
|
||||||
Assert.Same(block, scan.Block);
|
|
||||||
var expectedEndIndex = expectedReturnValue != -1 ?
|
|
||||||
block.Start + input.IndexOf(seek) :
|
|
||||||
block.Start + expectedBytesScanned;
|
|
||||||
Assert.Equal(expectedEndIndex, scan.Index);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Cleanup
|
|
||||||
if (block != null) _pool.Return(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(SeekByteLimitData))]
|
|
||||||
public void TestSeekByteLimitAcrossBlocks(string input, char seek, int limit, int expectedBytesScanned, int expectedReturnValue)
|
|
||||||
{
|
|
||||||
MemoryPoolBlock block1 = null;
|
|
||||||
MemoryPoolBlock block2 = null;
|
|
||||||
MemoryPoolBlock emptyBlock = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var input1 = input.Substring(0, input.Length / 2);
|
|
||||||
block1 = _pool.Lease();
|
|
||||||
var chars1 = input1.ToCharArray().Select(c => (byte)c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars1, 0, block1.Array, block1.Start, chars1.Length);
|
|
||||||
block1.End += chars1.Length;
|
|
||||||
|
|
||||||
emptyBlock = _pool.Lease();
|
|
||||||
block1.Next = emptyBlock;
|
|
||||||
|
|
||||||
var input2 = input.Substring(input.Length / 2);
|
|
||||||
block2 = _pool.Lease();
|
|
||||||
var chars2 = input2.ToCharArray().Select(c => (byte)c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars2, 0, block2.Array, block2.Start, chars2.Length);
|
|
||||||
block2.End += chars2.Length;
|
|
||||||
emptyBlock.Next = block2;
|
|
||||||
|
|
||||||
var scan = block1.GetIterator();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
int bytesScanned;
|
|
||||||
var returnValue = scan.Seek((byte)seek, out bytesScanned, limit);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedBytesScanned, bytesScanned);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue);
|
|
||||||
|
|
||||||
var seekCharIndex = input.IndexOf(seek);
|
|
||||||
var expectedEndBlock = limit <= input.Length / 2 ?
|
|
||||||
block1 :
|
|
||||||
(seekCharIndex != -1 && seekCharIndex < input.Length / 2 ? block1 : block2);
|
|
||||||
Assert.Same(expectedEndBlock, scan.Block);
|
|
||||||
var expectedEndIndex = expectedReturnValue != -1 ?
|
|
||||||
expectedEndBlock.Start + (expectedEndBlock == block1 ? input1.IndexOf(seek) : input2.IndexOf(seek)) :
|
|
||||||
expectedEndBlock.Start + (expectedEndBlock == block1 ? expectedBytesScanned : expectedBytesScanned - (input.Length / 2));
|
|
||||||
Assert.Equal(expectedEndIndex, scan.Index);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Cleanup
|
|
||||||
if (block1 != null) _pool.Return(block1);
|
|
||||||
if (emptyBlock != null) _pool.Return(emptyBlock);
|
|
||||||
if (block2 != null) _pool.Return(block2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(SeekIteratorLimitData))]
|
|
||||||
public void TestSeekIteratorLimitWithinSameBlock(string input, char seek, char limitAt, int expectedReturnValue)
|
|
||||||
{
|
|
||||||
MemoryPoolBlock block = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var afterSeek = (byte)'B';
|
|
||||||
|
|
||||||
block = _pool.Lease();
|
|
||||||
var chars = input.ToCharArray().Select(c => (byte)c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars, 0, block.Array, block.Start, chars.Length);
|
|
||||||
block.End += chars.Length;
|
|
||||||
var scan1 = block.GetIterator();
|
|
||||||
var scan2_1 = scan1;
|
|
||||||
var scan2_2 = scan1;
|
|
||||||
var scan3_1 = scan1;
|
|
||||||
var scan3_2 = scan1;
|
|
||||||
var scan3_3 = scan1;
|
|
||||||
var end = scan1;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var endReturnValue = end.Seek((byte)limitAt);
|
|
||||||
var returnValue1 = scan1.Seek((byte)seek, ref end);
|
|
||||||
var returnValue2_1 = scan2_1.Seek((byte)seek, afterSeek, ref end);
|
|
||||||
var returnValue2_2 = scan2_2.Seek(afterSeek, (byte)seek, ref end);
|
|
||||||
var returnValue3_1 = scan3_1.Seek((byte)seek, afterSeek, afterSeek, ref end);
|
|
||||||
var returnValue3_2 = scan3_2.Seek(afterSeek, (byte)seek, afterSeek, ref end);
|
|
||||||
var returnValue3_3 = scan3_3.Seek(afterSeek, afterSeek, (byte)seek, ref end);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(input.Contains(limitAt) ? limitAt : -1, endReturnValue);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue1);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue2_1);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue2_2);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue3_1);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue3_2);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue3_3);
|
|
||||||
|
|
||||||
Assert.Same(block, scan1.Block);
|
|
||||||
Assert.Same(block, scan2_1.Block);
|
|
||||||
Assert.Same(block, scan2_2.Block);
|
|
||||||
Assert.Same(block, scan3_1.Block);
|
|
||||||
Assert.Same(block, scan3_2.Block);
|
|
||||||
Assert.Same(block, scan3_3.Block);
|
|
||||||
|
|
||||||
var expectedEndIndex = expectedReturnValue != -1 ? block.Start + input.IndexOf(seek) : end.Index;
|
|
||||||
Assert.Equal(expectedEndIndex, scan1.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan2_1.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan2_2.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan3_1.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan3_2.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan3_3.Index);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Cleanup
|
|
||||||
if (block != null) _pool.Return(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(SeekIteratorLimitData))]
|
|
||||||
public void TestSeekIteratorLimitAcrossBlocks(string input, char seek, char limitAt, int expectedReturnValue)
|
|
||||||
{
|
|
||||||
MemoryPoolBlock block1 = null;
|
|
||||||
MemoryPoolBlock block2 = null;
|
|
||||||
MemoryPoolBlock emptyBlock = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var afterSeek = (byte)'B';
|
|
||||||
|
|
||||||
var input1 = input.Substring(0, input.Length / 2);
|
|
||||||
block1 = _pool.Lease();
|
|
||||||
var chars1 = input1.ToCharArray().Select(c => (byte)c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars1, 0, block1.Array, block1.Start, chars1.Length);
|
|
||||||
block1.End += chars1.Length;
|
|
||||||
|
|
||||||
emptyBlock = _pool.Lease();
|
|
||||||
block1.Next = emptyBlock;
|
|
||||||
|
|
||||||
var input2 = input.Substring(input.Length / 2);
|
|
||||||
block2 = _pool.Lease();
|
|
||||||
var chars2 = input2.ToCharArray().Select(c => (byte)c).ToArray();
|
|
||||||
Buffer.BlockCopy(chars2, 0, block2.Array, block2.Start, chars2.Length);
|
|
||||||
block2.End += chars2.Length;
|
|
||||||
emptyBlock.Next = block2;
|
|
||||||
|
|
||||||
var scan1 = block1.GetIterator();
|
|
||||||
var scan2_1 = scan1;
|
|
||||||
var scan2_2 = scan1;
|
|
||||||
var scan3_1 = scan1;
|
|
||||||
var scan3_2 = scan1;
|
|
||||||
var scan3_3 = scan1;
|
|
||||||
var end = scan1;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var endReturnValue = end.Seek((byte)limitAt);
|
|
||||||
var returnValue1 = scan1.Seek((byte)seek, ref end);
|
|
||||||
var returnValue2_1 = scan2_1.Seek((byte)seek, afterSeek, ref end);
|
|
||||||
var returnValue2_2 = scan2_2.Seek(afterSeek, (byte)seek, ref end);
|
|
||||||
var returnValue3_1 = scan3_1.Seek((byte)seek, afterSeek, afterSeek, ref end);
|
|
||||||
var returnValue3_2 = scan3_2.Seek(afterSeek, (byte)seek, afterSeek, ref end);
|
|
||||||
var returnValue3_3 = scan3_3.Seek(afterSeek, afterSeek, (byte)seek, ref end);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(input.Contains(limitAt) ? limitAt : -1, endReturnValue);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue1);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue2_1);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue2_2);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue3_1);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue3_2);
|
|
||||||
Assert.Equal(expectedReturnValue, returnValue3_3);
|
|
||||||
|
|
||||||
var seekCharIndex = input.IndexOf(seek);
|
|
||||||
var limitAtIndex = input.IndexOf(limitAt);
|
|
||||||
var expectedEndBlock = seekCharIndex != -1 && seekCharIndex < input.Length / 2 ?
|
|
||||||
block1 :
|
|
||||||
(limitAtIndex != -1 && limitAtIndex < input.Length / 2 ? block1 : block2);
|
|
||||||
Assert.Same(expectedEndBlock, scan1.Block);
|
|
||||||
Assert.Same(expectedEndBlock, scan2_1.Block);
|
|
||||||
Assert.Same(expectedEndBlock, scan2_2.Block);
|
|
||||||
Assert.Same(expectedEndBlock, scan3_1.Block);
|
|
||||||
Assert.Same(expectedEndBlock, scan3_2.Block);
|
|
||||||
Assert.Same(expectedEndBlock, scan3_3.Block);
|
|
||||||
|
|
||||||
var expectedEndIndex = expectedReturnValue != -1 ?
|
|
||||||
expectedEndBlock.Start + (expectedEndBlock == block1 ? input1.IndexOf(seek) : input2.IndexOf(seek)) :
|
|
||||||
end.Index;
|
|
||||||
Assert.Equal(expectedEndIndex, scan1.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan2_1.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan2_2.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan3_1.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan3_2.Index);
|
|
||||||
Assert.Equal(expectedEndIndex, scan3_3.Index);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Cleanup
|
|
||||||
if (block1 != null) _pool.Return(block1);
|
|
||||||
if (emptyBlock != null) _pool.Return(emptyBlock);
|
|
||||||
if (block2 != null) _pool.Return(block2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EmptyIteratorBehaviourIsValid()
|
public void EmptyIteratorBehaviourIsValid()
|
||||||
{
|
{
|
||||||
const byte byteCr = (byte)'\n';
|
const byte byteCr = (byte)'\n';
|
||||||
ulong longValue;
|
|
||||||
var end = default(MemoryPoolIterator);
|
var end = default(MemoryPoolIterator);
|
||||||
|
|
||||||
Assert.False(default(MemoryPoolIterator).TryPeekLong(out longValue));
|
|
||||||
Assert.Null(default(MemoryPoolIterator).GetAsciiString(ref end));
|
|
||||||
Assert.Null(default(MemoryPoolIterator).GetUtf8String(ref end));
|
|
||||||
// Assert.Equal doesn't work for default(ArraySegments)
|
|
||||||
Assert.True(default(MemoryPoolIterator).GetArraySegment(end).Equals(default(ArraySegment<byte>)));
|
|
||||||
Assert.True(default(MemoryPoolIterator).IsDefault);
|
Assert.True(default(MemoryPoolIterator).IsDefault);
|
||||||
Assert.True(default(MemoryPoolIterator).IsEnd);
|
Assert.True(default(MemoryPoolIterator).IsEnd);
|
||||||
Assert.Equal(default(MemoryPoolIterator).Take(), -1);
|
|
||||||
Assert.Equal(default(MemoryPoolIterator).Peek(), -1);
|
|
||||||
Assert.Equal(default(MemoryPoolIterator).Seek(byteCr), -1);
|
|
||||||
Assert.Equal(default(MemoryPoolIterator).Seek(byteCr, ref end), -1);
|
|
||||||
Assert.Equal(default(MemoryPoolIterator).Seek(byteCr, byteCr), -1);
|
|
||||||
Assert.Equal(default(MemoryPoolIterator).Seek(byteCr, byteCr, byteCr), -1);
|
|
||||||
|
|
||||||
default(MemoryPoolIterator).CopyFrom(default(ArraySegment<byte>));
|
default(MemoryPoolIterator).CopyFrom(default(ArraySegment<byte>));
|
||||||
default(MemoryPoolIterator).CopyFromAscii("");
|
default(MemoryPoolIterator).CopyFromAscii("");
|
||||||
Assert.ThrowsAny<InvalidOperationException>(() => default(MemoryPoolIterator).Put(byteCr));
|
Assert.ThrowsAny<InvalidOperationException>(() => default(MemoryPoolIterator).Put(byteCr));
|
||||||
Assert.ThrowsAny<InvalidOperationException>(() => default(MemoryPoolIterator).GetLength(end));
|
Assert.ThrowsAny<InvalidOperationException>(() => default(MemoryPoolIterator).GetLength(end));
|
||||||
Assert.ThrowsAny<InvalidOperationException>(() => default(MemoryPoolIterator).Skip(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestGetArraySegment()
|
|
||||||
{
|
|
||||||
MemoryPoolBlock block0 = null;
|
|
||||||
MemoryPoolBlock block1 = null;
|
|
||||||
|
|
||||||
var byteRange = Enumerable.Range(1, 127).Select(x => (byte)x).ToArray();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
block0 = _pool.Lease();
|
|
||||||
block1 = _pool.Lease();
|
|
||||||
|
|
||||||
block0.GetIterator().CopyFrom(byteRange);
|
|
||||||
block1.GetIterator().CopyFrom(byteRange);
|
|
||||||
|
|
||||||
block0.Next = block1;
|
|
||||||
|
|
||||||
var begin = block0.GetIterator();
|
|
||||||
var end0 = begin;
|
|
||||||
var end1 = begin;
|
|
||||||
|
|
||||||
end0.Skip(byteRange.Length);
|
|
||||||
end1.Skip(byteRange.Length * 2);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var as0 = begin.GetArraySegment(end0);
|
|
||||||
var as1 = begin.GetArraySegment(end1);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(as0.Count, byteRange.Length);
|
|
||||||
Assert.Equal(as1.Count, byteRange.Length * 2);
|
|
||||||
|
|
||||||
for (var i = 1; i < byteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var asb0 = as0.Array[i + as0.Offset];
|
|
||||||
var asb1 = as1.Array[i + as1.Offset];
|
|
||||||
var b = byteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(asb0, b);
|
|
||||||
Assert.Equal(asb1, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 1 + byteRange.Length; i < byteRange.Length * 2; i++)
|
|
||||||
{
|
|
||||||
var asb1 = as1.Array[i + as1.Offset];
|
|
||||||
var b = byteRange[i - byteRange.Length];
|
|
||||||
|
|
||||||
Assert.Equal(asb1, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (block0 != null) _pool.Return(block0);
|
|
||||||
if (block1 != null) _pool.Return(block1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestTake()
|
|
||||||
{
|
|
||||||
MemoryPoolBlock block0 = null;
|
|
||||||
MemoryPoolBlock block1 = null;
|
|
||||||
MemoryPoolBlock block2 = null;
|
|
||||||
MemoryPoolBlock emptyBlock0 = null;
|
|
||||||
MemoryPoolBlock emptyBlock1 = null;
|
|
||||||
|
|
||||||
var byteRange = Enumerable.Range(1, 127).Select(x => (byte)x).ToArray();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
block0 = _pool.Lease();
|
|
||||||
block1 = _pool.Lease();
|
|
||||||
block2 = _pool.Lease();
|
|
||||||
emptyBlock0 = _pool.Lease();
|
|
||||||
emptyBlock1 = _pool.Lease();
|
|
||||||
|
|
||||||
block0.GetIterator().CopyFrom(byteRange);
|
|
||||||
block1.GetIterator().CopyFrom(byteRange);
|
|
||||||
block2.GetIterator().CopyFrom(byteRange);
|
|
||||||
|
|
||||||
var begin = block0.GetIterator();
|
|
||||||
|
|
||||||
// Single block
|
|
||||||
for (var i = 0; i < byteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var t = begin.Take();
|
|
||||||
var b = byteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(t, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(begin.Take(), -1);
|
|
||||||
|
|
||||||
// Dual block
|
|
||||||
block0.Next = block1;
|
|
||||||
begin = block0.GetIterator();
|
|
||||||
|
|
||||||
for (var block = 0; block < 2; block++)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < byteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var t = begin.Take();
|
|
||||||
var b = byteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(t, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(begin.Take(), -1);
|
|
||||||
|
|
||||||
// Multi block
|
|
||||||
block1.Next = emptyBlock0;
|
|
||||||
emptyBlock0.Next = emptyBlock1;
|
|
||||||
emptyBlock1.Next = block2;
|
|
||||||
begin = block0.GetIterator();
|
|
||||||
|
|
||||||
for (var block = 0; block < 3; block++)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < byteRange.Length; i++)
|
|
||||||
{
|
|
||||||
var t = begin.Take();
|
|
||||||
var b = byteRange[i];
|
|
||||||
|
|
||||||
Assert.Equal(t, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(begin.Take(), -1);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (block0 != null) _pool.Return(block0);
|
|
||||||
if (block1 != null) _pool.Return(block1);
|
|
||||||
if (block2 != null) _pool.Return(block2);
|
|
||||||
if (emptyBlock0 != null) _pool.Return(emptyBlock0);
|
|
||||||
if (emptyBlock1 != null) _pool.Return(emptyBlock1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TestTakeEmptyBlocks()
|
|
||||||
{
|
|
||||||
MemoryPoolBlock emptyBlock0 = null;
|
|
||||||
MemoryPoolBlock emptyBlock1 = null;
|
|
||||||
MemoryPoolBlock emptyBlock2 = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
emptyBlock0 = _pool.Lease();
|
|
||||||
emptyBlock1 = _pool.Lease();
|
|
||||||
emptyBlock2 = _pool.Lease();
|
|
||||||
|
|
||||||
var beginEmpty = emptyBlock0.GetIterator();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
|
|
||||||
// No blocks
|
|
||||||
Assert.Equal(default(MemoryPoolIterator).Take(), -1);
|
|
||||||
|
|
||||||
// Single empty block
|
|
||||||
Assert.Equal(beginEmpty.Take(), -1);
|
|
||||||
|
|
||||||
// Dual empty block
|
|
||||||
emptyBlock0.Next = emptyBlock1;
|
|
||||||
beginEmpty = emptyBlock0.GetIterator();
|
|
||||||
Assert.Equal(beginEmpty.Take(), -1);
|
|
||||||
|
|
||||||
// Multi empty block
|
|
||||||
emptyBlock1.Next = emptyBlock2;
|
|
||||||
beginEmpty = emptyBlock0.GetIterator();
|
|
||||||
Assert.Equal(beginEmpty.Take(), -1);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (emptyBlock0 != null) _pool.Return(emptyBlock0);
|
|
||||||
if (emptyBlock1 != null) _pool.Return(emptyBlock1);
|
|
||||||
if (emptyBlock2 != null) _pool.Return(emptyBlock2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -973,23 +191,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[InlineData("abcde", "abcde", 6)]
|
[InlineData("abcde", "abcde", 6)]
|
||||||
public void TestGetAsciiStringEscaped(string input, string expected, int maxChars)
|
public void TestGetAsciiStringEscaped(string input, string expected, int maxChars)
|
||||||
{
|
{
|
||||||
MemoryPoolBlock block = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
// Arrange
|
||||||
var buffer = new Span<byte>(Encoding.ASCII.GetBytes(input));
|
var buffer = new Span<byte>(Encoding.ASCII.GetBytes(input));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = buffer.GetAsciiStringEscaped(maxChars);
|
var result = buffer.GetAsciiStringEscaped(maxChars);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, result);
|
Assert.Equal(expected, result);
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (block != null) _pool.Return(block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -1,226 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
|
||||||
{
|
|
||||||
public class UrlPathDecoderTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Empty()
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, string.Empty, string.Empty);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void WhiteSpace()
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, " ", " ");
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("/foo/bar", "/foo/bar")]
|
|
||||||
[InlineData("/foo/BAR", "/foo/BAR")]
|
|
||||||
[InlineData("/foo/", "/foo/")]
|
|
||||||
[InlineData("/", "/")]
|
|
||||||
public void NormalCases(string raw, string expect)
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, raw, expect);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("%2F", "%2F")]
|
|
||||||
[InlineData("/foo%2Fbar", "/foo%2Fbar")]
|
|
||||||
[InlineData("/foo%2F%20bar", "/foo%2F bar")]
|
|
||||||
public void SkipForwardSlash(string raw, string expect)
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, raw, expect);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("%D0%A4", "Ф")]
|
|
||||||
[InlineData("%d0%a4", "Ф")]
|
|
||||||
[InlineData("%E0%A4%AD", "भ")]
|
|
||||||
[InlineData("%e0%A4%Ad", "भ")]
|
|
||||||
[InlineData("%F0%A4%AD%A2", "𤭢")]
|
|
||||||
[InlineData("%F0%a4%Ad%a2", "𤭢")]
|
|
||||||
[InlineData("%48%65%6C%6C%6F%20%57%6F%72%6C%64", "Hello World")]
|
|
||||||
[InlineData("%48%65%6C%6C%6F%2D%C2%B5%40%C3%9F%C3%B6%C3%A4%C3%BC%C3%A0%C3%A1", "Hello-µ@ßöäüàá")]
|
|
||||||
// Test the borderline cases of overlong UTF8.
|
|
||||||
[InlineData("%C2%80", "\u0080")]
|
|
||||||
[InlineData("%E0%A0%80", "\u0800")]
|
|
||||||
[InlineData("%F0%90%80%80", "\U00010000")]
|
|
||||||
[InlineData("%63", "c")]
|
|
||||||
[InlineData("%32", "2")]
|
|
||||||
[InlineData("%20", " ")]
|
|
||||||
public void ValidUTF8(string raw, string expect)
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, raw, expect);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("%C3%84ra%20Benetton", "Ära Benetton")]
|
|
||||||
[InlineData("%E6%88%91%E8%87%AA%E6%A8%AA%E5%88%80%E5%90%91%E5%A4%A9%E7%AC%91%E5%8E%BB%E7%95%99%E8%82%9D%E8%83%86%E4%B8%A4%E6%98%86%E4%BB%91", "我自横刀向天笑去留肝胆两昆仑")]
|
|
||||||
public void Internationalized(string raw, string expect)
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, raw, expect);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
// Overlong ASCII
|
|
||||||
[InlineData("%C0%A4", "%C0%A4")]
|
|
||||||
[InlineData("%C1%BF", "%C1%BF")]
|
|
||||||
[InlineData("%E0%80%AF", "%E0%80%AF")]
|
|
||||||
[InlineData("%E0%9F%BF", "%E0%9F%BF")]
|
|
||||||
[InlineData("%F0%80%80%AF", "%F0%80%80%AF")]
|
|
||||||
[InlineData("%F0%8F%8F%BF", "%F0%8F%8F%BF")]
|
|
||||||
// Incomplete
|
|
||||||
[InlineData("%", "%")]
|
|
||||||
[InlineData("%%", "%%")]
|
|
||||||
[InlineData("%A", "%A")]
|
|
||||||
[InlineData("%Y", "%Y")]
|
|
||||||
// Mixed
|
|
||||||
[InlineData("%%32", "%2")]
|
|
||||||
[InlineData("%%20", "% ")]
|
|
||||||
[InlineData("%C0%A4%32", "%C0%A42")]
|
|
||||||
[InlineData("%32%C0%A4%32", "2%C0%A42")]
|
|
||||||
[InlineData("%C0%32%A4", "%C02%A4")]
|
|
||||||
public void InvalidUTF8(string raw, string expect)
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
PositiveAssert(mem, raw, expect);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("/foo%2Fbar", 10, "/foo%2Fbar", 10)]
|
|
||||||
[InlineData("/foo%2Fbar", 9, "/foo%2Fba", 9)]
|
|
||||||
[InlineData("/foo%2Fbar", 8, "/foo%2Fb", 8)]
|
|
||||||
[InlineData("%D0%A4", 6, "Ф", 1)]
|
|
||||||
[InlineData("%D0%A4", 5, "%D0%A", 5)]
|
|
||||||
[InlineData("%D0%A4", 4, "%D0%", 4)]
|
|
||||||
[InlineData("%D0%A4", 3, "%D0", 3)]
|
|
||||||
[InlineData("%D0%A4", 2, "%D", 2)]
|
|
||||||
[InlineData("%D0%A4", 1, "%", 1)]
|
|
||||||
[InlineData("%D0%A4", 0, "", 0)]
|
|
||||||
[InlineData("%C2%B5%40%C3%9F%C3%B6%C3%A4%C3%BC%C3%A0%C3%A1", 45, "µ@ßöäüàá", 8)]
|
|
||||||
[InlineData("%C2%B5%40%C3%9F%C3%B6%C3%A4%C3%BC%C3%A0%C3%A1", 44, "µ@ßöäüà%C3%A", 12)]
|
|
||||||
public void DecodeWithBoundary(string raw, int rawLength, string expect, int expectLength)
|
|
||||||
{
|
|
||||||
using (var pool = new MemoryPool())
|
|
||||||
{
|
|
||||||
var mem = pool.Lease();
|
|
||||||
|
|
||||||
var begin = BuildSample(mem, raw);
|
|
||||||
var end = GetIterator(begin, rawLength);
|
|
||||||
|
|
||||||
var end2 = UrlPathDecoder.Unescape(begin, end);
|
|
||||||
var result = begin.GetUtf8String(ref end2);
|
|
||||||
|
|
||||||
Assert.Equal(expectLength, result.Length);
|
|
||||||
Assert.Equal(expect, result);
|
|
||||||
|
|
||||||
pool.Return(mem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MemoryPoolIterator BuildSample(MemoryPoolBlock mem, string data)
|
|
||||||
{
|
|
||||||
var store = data.Select(c => (byte)c).ToArray();
|
|
||||||
mem.GetIterator().CopyFrom(new ArraySegment<byte>(store));
|
|
||||||
|
|
||||||
return mem.GetIterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MemoryPoolIterator GetIterator(MemoryPoolIterator begin, int displacement)
|
|
||||||
{
|
|
||||||
var result = begin;
|
|
||||||
for (int i = 0; i < displacement; ++i)
|
|
||||||
{
|
|
||||||
result.Take();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PositiveAssert(MemoryPoolBlock mem, string raw, string expect)
|
|
||||||
{
|
|
||||||
var begin = BuildSample(mem, raw);
|
|
||||||
var end = GetIterator(begin, raw.Length);
|
|
||||||
|
|
||||||
var result = UrlPathDecoder.Unescape(begin, end);
|
|
||||||
Assert.Equal(expect, begin.GetUtf8String(ref result));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PositiveAssert(MemoryPoolBlock mem, string raw)
|
|
||||||
{
|
|
||||||
var begin = BuildSample(mem, raw);
|
|
||||||
var end = GetIterator(begin, raw.Length);
|
|
||||||
|
|
||||||
var result = UrlPathDecoder.Unescape(begin, end);
|
|
||||||
Assert.NotEqual(raw.Length, begin.GetUtf8String(ref result).Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NegativeAssert(MemoryPoolBlock mem, string raw)
|
|
||||||
{
|
|
||||||
var begin = BuildSample(mem, raw);
|
|
||||||
var end = GetIterator(begin, raw.Length);
|
|
||||||
|
|
||||||
var resultEnd = UrlPathDecoder.Unescape(begin, end);
|
|
||||||
var result = begin.GetUtf8String(ref resultEnd);
|
|
||||||
Assert.Equal(raw, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue