diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/UrlPathDecoder.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/UrlPathDecoder.cs
deleted file mode 100644
index 67004cf24f..0000000000
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Http/UrlPathDecoder.cs
+++ /dev/null
@@ -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
- {
- ///
- /// Unescapes the string between given memory iterators in place.
- ///
- /// The iterator points to the beginning of the sequence.
- /// The iterator points to the byte behind the end of the sequence.
- /// The iterator points to the byte behind the end of the processed sequence.
- 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());
- }
- }
- }
-
- ///
- /// Unescape the percent-encodings
- ///
- /// The iterator point to the first % char
- /// The place to write to
- /// The end of the sequence
- 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());
- }
- }
-
- ///
- /// Read the percent-encoding and try unescape it.
- ///
- /// The operation first peek at the character the
- /// iterator points at. If it is % the 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 iterator
- /// will be removed beyond the location of % and -1 will be returned.
- ///
- /// If the following two characters can't be successfully unescaped the
- /// iterator will be move behind the % and -1
- /// will be returned.
- ///
- /// The value to read
- /// The end of the sequence
- /// The unescaped byte if success. Otherwise return -1.
- 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;
- }
-
- ///
- /// Read the next char and convert it into hexadecimal value.
- ///
- /// The iterator will be moved to the next
- /// byte no matter no matter whether the operation successes.
- ///
- /// The value to read
- /// The end of the sequence
- /// The hexadecimal value if successes, otherwise -1.
- 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);
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/MemoryPoolIterator.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/MemoryPoolIterator.cs
index 07491b7377..0f4d3cebe5 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/MemoryPoolIterator.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/MemoryPoolIterator.cs
@@ -14,15 +14,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
public struct MemoryPoolIterator
{
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.Count;
[ThreadStatic]
private static byte[] _numericBytesScratch;
@@ -135,668 +126,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
} 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(array, index), byte0Vector);
-
- if (byte0Equals.Equals(Vector.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(array, index), GetVector(byte0));
-
- if (byte0Equals.Equals(Vector.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(array, index);
-
- var byteEquals = Vector.Equals(data, GetVector(byte0));
- byteEquals = Vector.ConditionalSelect(byteEquals, byteEquals, Vector.Equals(data, GetVector(byte1)));
-
- if (!byteEquals.Equals(Vector.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(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.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;
- }
- }
- }
-
- ///
- /// Locate the first of the found bytes
- ///
- ///
- /// The first index of the result vector
- // Force inlining (64 IL bytes, 91 bytes asm) Issue: https://github.com/dotnet/coreclr/issues/7386
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static int LocateFirstFoundByte(Vector 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.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;
- }
-
+
///
/// Save the data at the current location then move to the next available space.
///
@@ -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)
{
CopyFrom(data, 0, data.Length);
@@ -1180,177 +459,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
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 GetArraySegment(MemoryPoolIterator end)
- {
- var block = _block;
- if (block == null || end.IsDefault)
- {
- return default(ArraySegment);
- }
-
- var index = _index;
- if (end.Block == block)
- {
- return new ArraySegment(block.Array, index, end.Index - index);
- }
-
- return GetArraySegmentMultiBlock(ref end);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- private ArraySegment GetArraySegmentMultiBlock(ref MemoryPoolIterator end)
- {
- var length = GetLength(end);
- var array = new byte[length];
- CopyTo(array, 0, length, out length);
- return new ArraySegment(array, 0, length);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Vector GetVector(byte vectorByte)
- {
- // Vector .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(vectorByte * 0x01010101u));
- }
}
}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs
index 220ae3d80b..5252a96e9a 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs
@@ -1,6 +1,7 @@
// 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;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
@@ -14,27 +15,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
private void FullAsciiRangeSupported()
{
var byteRange = Enumerable.Range(1, 127).Select(x => (byte)x).ToArray();
- using (var pool = new MemoryPool())
+ var s = new Span(byteRange).GetAsciiStringNonNullCharacters();
+
+ Assert.Equal(s.Length, byteRange.Length);
+
+ for (var i = 1; i < byteRange.Length; i++)
{
- var mem = pool.Lease();
- mem.GetIterator().CopyFrom(byteRange);
+ var sb = (byte)s[i];
+ var b = byteRange[i];
- var begin = mem.GetIterator();
- 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);
+ Assert.Equal(sb, b);
}
}
@@ -49,123 +39,29 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
var byteRange = Enumerable.Range(1, length).Select(x => (byte)x).ToArray();
byteRange[position] = b;
-
- using (var pool = new MemoryPool())
- {
- var mem = pool.Lease();
- mem.GetIterator().CopyFrom(byteRange);
-
- var begin = mem.GetIterator();
- var end = GetIterator(begin, byteRange.Length);
-
- Assert.Throws(() => begin.GetAsciiString(ref end));
-
- pool.Return(mem);
- }
+
+ Assert.Throws(() => new Span(byteRange).GetAsciiStringNonNullCharacters());
}
}
}
- [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]
private void LargeAllocationProducesCorrectResults()
{
var byteRange = Enumerable.Range(0, 16384 + 64).Select(x => (byte)((x & 0x7f) | 0x01)).ToArray();
var expectedByteRange = byteRange.Concat(byteRange).ToArray();
- using (var pool = new MemoryPool())
+
+ var s = new Span(expectedByteRange).GetAsciiStringNonNullCharacters();
+
+ Assert.Equal(expectedByteRange.Length, s.Length);
+
+ for (var i = 0; i < expectedByteRange.Length; i++)
{
- var mem0 = pool.Lease();
- var mem1 = pool.Lease();
- mem0.GetIterator().CopyFrom(byteRange);
- mem1.GetIterator().CopyFrom(byteRange);
+ var sb = (byte)((s[i] & 0x7f) | 0x01);
+ var b = expectedByteRange[i];
- var lastBlock = mem0;
- 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);
- }
+ Assert.Equal(sb, b);
}
}
-
- private MemoryPoolIterator GetIterator(MemoryPoolIterator begin, int displacement)
- {
- var result = begin;
- for (int i = 0; i < displacement; ++i)
- {
- result.Take();
- }
-
- return result;
- }
}
}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs
index 4cde6c079a..2213f81f07 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolBlockTests.cs
@@ -8,105 +8,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
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]
public void GetLengthBetweenIteratorsWorks()
{
@@ -192,87 +94,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
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(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]
public void IsEndCorrectlyTraversesBlocks()
{
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolExtensions.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolExtensions.cs
index 6348258ce9..1d62dd2793 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolExtensions.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolExtensions.cs
@@ -6,8 +6,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
public static MemoryPoolIterator Add(this MemoryPoolIterator iterator, int count)
{
- int actual;
- return iterator.CopyTo(new byte[count], 0, count, out actual);
+ for (int i = 0; i < count; i++)
+ {
+ iterator.Take();
+ }
+ return iterator;
}
}
}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs
index 83cf36c4c8..4643b5236b 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/MemoryPoolIteratorTests.cs
@@ -30,80 +30,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
_pool.Dispose();
}
- [Fact]
- public void TestFindFirstEqualByte()
- {
- var bytes = Enumerable.Repeat(0xff, Vector.Count).ToArray();
- for (int i = 0; i < Vector.Count; i++)
- {
- Vector vector = new Vector(bytes);
- Assert.Equal(i, MemoryPoolIterator.LocateFirstFoundByte(vector));
- bytes[i] = 0;
- }
-
- for (int i = 0; i < Vector.Count; i++)
- {
- bytes[i] = 1;
- Vector vector = new Vector(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]
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(() => 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(() => 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]
public void EmptyIteratorBehaviourIsValid()
{
const byte byteCr = (byte)'\n';
- ulong longValue;
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)));
+
Assert.True(default(MemoryPoolIterator).IsDefault);
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));
default(MemoryPoolIterator).CopyFromAscii("");
Assert.ThrowsAny(() => default(MemoryPoolIterator).Put(byteCr));
Assert.ThrowsAny(() => default(MemoryPoolIterator).GetLength(end));
- Assert.ThrowsAny(() => 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]
@@ -973,23 +191,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[InlineData("abcde", "abcde", 6)]
public void TestGetAsciiStringEscaped(string input, string expected, int maxChars)
{
- MemoryPoolBlock block = null;
-
- try
- {
// Arrange
- var buffer = new Span(Encoding.ASCII.GetBytes(input));
+ var buffer = new Span(Encoding.ASCII.GetBytes(input));
- // Act
- var result = buffer.GetAsciiStringEscaped(maxChars);
+ // Act
+ var result = buffer.GetAsciiStringEscaped(maxChars);
- // Assert
- Assert.Equal(expected, result);
- }
- finally
- {
- if (block != null) _pool.Return(block);
- }
+ // Assert
+ Assert.Equal(expected, result);
}
[Fact]
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs
deleted file mode 100644
index 0acfc27fe8..0000000000
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/UrlPathDecoder.cs
+++ /dev/null
@@ -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(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);
- }
- }
-}