MemoryPoolIterator byref structs

This commit is contained in:
Ben Adams 2016-11-15 12:01:13 +00:00
parent 90c7be1fc0
commit 12e2f30577
5 changed files with 179 additions and 167 deletions

View File

@ -975,7 +975,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
}
method = begin.GetAsciiString(scan);
method = begin.GetAsciiString(ref scan);
if (method == null)
{
@ -1031,7 +1031,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
RejectRequest(RequestRejectionReason.InvalidRequestLine,
Log.IsEnabled(LogLevel.Information) ? start.GetAsciiStringEscaped(end, MaxInvalidRequestLineChars) : string.Empty);
}
queryString = begin.GetAsciiString(scan);
queryString = begin.GetAsciiString(ref scan);
}
var queryEnd = scan;
@ -1081,16 +1081,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
if (needDecode)
{
// Read raw target before mutating memory.
rawTarget = pathBegin.GetAsciiString(queryEnd);
rawTarget = pathBegin.GetAsciiString(ref queryEnd);
// URI was encoded, unescape and then parse as utf8
pathEnd = UrlPathDecoder.Unescape(pathBegin, pathEnd);
requestUrlPath = pathBegin.GetUtf8String(pathEnd);
requestUrlPath = pathBegin.GetUtf8String(ref pathEnd);
}
else
{
// URI wasn't encoded, parse as ASCII
requestUrlPath = pathBegin.GetAsciiString(pathEnd);
requestUrlPath = pathBegin.GetAsciiString(ref pathEnd);
if (queryString.Length == 0)
{
@ -1100,7 +1100,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
}
else
{
rawTarget = pathBegin.GetAsciiString(queryEnd);
rawTarget = pathBegin.GetAsciiString(ref queryEnd);
}
}
@ -1341,7 +1341,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
} while (ch != ByteCR);
var name = beginName.GetArraySegment(endName);
var value = beginValue.GetAsciiString(endValue);
var value = beginValue.GetAsciiString(ref endValue);
consumed = scan;
requestHeaders.Append(name.Array, name.Offset, name.Count, value);

View File

@ -5,7 +5,9 @@ using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
{
@ -19,6 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
0x02ul << 40 |
0x01ul << 48 ) + 1;
private static readonly Encoding _utf8 = Encoding.UTF8;
private static readonly int _vectorSpan = Vector<byte>.Count;
private MemoryPoolBlock _block;
@ -1026,6 +1029,164 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
_index = blockIndex;
}
public unsafe string GetAsciiString(ref MemoryPoolIterator end)
{
if (IsDefault || end.IsDefault)
{
return null;
}
var length = GetLength(end);
if (length == 0)
{
return null;
}
var inputOffset = Index;
var block = Block;
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)
{
if (IsDefault || end.IsDefault)
{
return default(string);
}
if (end.Block == Block)
{
return _utf8.GetString(Block.Array, Index, end.Index - Index);
}
var decoder = _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 block = Block;
var index = Index;
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)
{
if (IsDefault || end.IsDefault)
{
return default(ArraySegment<byte>);
}
if (end.Block == Block)
{
return new ArraySegment<byte>(Block.Array, Index, end.Index - Index);
}
return GetArraySegmentMultiBlock(ref end);
}
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)
{

View File

@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
@ -71,62 +72,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
}
}
public unsafe static string GetAsciiString(this MemoryPoolIterator start, MemoryPoolIterator end)
{
if (start.IsDefault || end.IsDefault)
{
return null;
}
var length = start.GetLength(end);
if (length == 0)
{
return null;
}
var inputOffset = start.Index;
var block = start.Block;
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 static string GetAsciiStringEscaped(this MemoryPoolIterator start, MemoryPoolIterator end, int maxChars)
{
var sb = new StringBuilder();
@ -147,102 +92,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
return sb.ToString();
}
public static string GetUtf8String(this MemoryPoolIterator start, MemoryPoolIterator end)
{
if (start.IsDefault || end.IsDefault)
{
return default(string);
}
if (end.Block == start.Block)
{
return _utf8.GetString(start.Block.Array, start.Index, end.Index - start.Index);
}
var decoder = _utf8.GetDecoder();
var length = start.GetLength(end);
var charLength = length;
// Worse case is 1 byte = 1 char
var chars = new char[charLength];
var charIndex = 0;
var block = start.Block;
var index = start.Index;
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;
}
}
}
public static ArraySegment<byte> GetArraySegment(this MemoryPoolIterator start, MemoryPoolIterator end)
{
if (start.IsDefault || end.IsDefault)
{
return default(ArraySegment<byte>);
}
if (end.Block == start.Block)
{
return new ArraySegment<byte>(start.Block.Array, start.Index, end.Index - start.Index);
}
var length = start.GetLength(end);
var array = new byte[length];
start.CopyTo(array, 0, length, out length);
return new ArraySegment<byte>(array, 0, length);
}
public static ArraySegment<byte> PeekArraySegment(this MemoryPoolIterator iter)
{
if (iter.IsDefault || iter.IsEnd)
@ -283,6 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
/// <param name="begin">The iterator from which to start the known string lookup.</param>
/// <param name="knownMethod">A reference to a pre-allocated known string, if the input matches any.</param>
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetKnownMethod(this MemoryPoolIterator begin, out string knownMethod)
{
knownMethod = null;
@ -323,6 +173,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure
/// <param name="begin">The iterator from which to start the known string lookup.</param>
/// <param name="knownVersion">A reference to a pre-allocated known string, if the input matches any.</param>
/// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetKnownVersion(this MemoryPoolIterator begin, out string knownVersion)
{
knownVersion = null;

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var begin = mem.GetIterator();
var end = GetIterator(begin, byteRange.Length);
var s = begin.GetAsciiString(end);
var s = begin.GetAsciiString(ref end);
Assert.Equal(s.Length, byteRange.Length);
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var begin = mem.GetIterator();
var end = GetIterator(begin, byteRange.Length);
Assert.Throws<BadHttpRequestException>(() => begin.GetAsciiString(end));
Assert.Throws<BadHttpRequestException>(() => begin.GetAsciiString(ref end));
pool.Return(mem);
}
@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var begin = mem0.GetIterator();
var end = GetIterator(begin, expectedByteRange.Length);
var s = begin.GetAsciiString(end);
var s = begin.GetAsciiString(ref end);
Assert.Equal(s.Length, expectedByteRange.Length);
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var begin = mem0.GetIterator();
var end = GetIterator(begin, expectedByteRange.Length);
var s = begin.GetAsciiString(end);
var s = begin.GetAsciiString(ref end);
Assert.Equal(expectedByteRange.Length, s.Length);

View File

@ -167,7 +167,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var end = GetIterator(begin, rawLength);
var end2 = UrlPathDecoder.Unescape(begin, end);
var result = begin.GetUtf8String(end2);
var result = begin.GetUtf8String(ref end2);
Assert.Equal(expectLength, result.Length);
Assert.Equal(expect, result);
@ -201,7 +201,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var end = GetIterator(begin, raw.Length);
var result = UrlPathDecoder.Unescape(begin, end);
Assert.Equal(expect, begin.GetUtf8String(result));
Assert.Equal(expect, begin.GetUtf8String(ref result));
}
private void PositiveAssert(MemoryPoolBlock mem, string raw)
@ -210,7 +210,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var end = GetIterator(begin, raw.Length);
var result = UrlPathDecoder.Unescape(begin, end);
Assert.NotEqual(raw.Length, begin.GetUtf8String(result).Length);
Assert.NotEqual(raw.Length, begin.GetUtf8String(ref result).Length);
}
private void NegativeAssert(MemoryPoolBlock mem, string raw)
@ -219,7 +219,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var end = GetIterator(begin, raw.Length);
var resultEnd = UrlPathDecoder.Unescape(begin, end);
var result = begin.GetUtf8String(resultEnd);
var result = begin.GetUtf8String(ref resultEnd);
Assert.Equal(raw, result);
}
}