Use writable buffer writer (#1564)

This commit is contained in:
Pavel Krymets 2017-04-07 14:40:10 -07:00 committed by GitHub
parent 239b691ff5
commit 53b0eea2ec
10 changed files with 165 additions and 247 deletions

View File

@ -41,19 +41,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
return;
}
var writableBuffer = _pipe.Writer.Alloc();
var writableBuffer = _pipe.Writer.Alloc(1);
var writer = new WritableBufferWriter(writableBuffer);
if (buffer.Count > 0)
{
if (chunk)
{
ChunkWriter.WriteBeginChunkBytes(ref writableBuffer, buffer.Count);
writableBuffer.WriteFast(buffer);
ChunkWriter.WriteEndChunkBytes(ref writableBuffer);
ChunkWriter.WriteBeginChunkBytes(ref writer, buffer.Count);
writer.Write(buffer.Array, buffer.Offset, buffer.Count);
ChunkWriter.WriteEndChunkBytes(ref writer);
}
else
{
writableBuffer.WriteFast(buffer);
writer.Write(buffer.Array, buffer.Offset, buffer.Count);
}
}
@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
return;
}
var buffer = _pipe.Writer.Alloc();
var buffer = _pipe.Writer.Alloc(1);
callback(buffer, state);
buffer.Commit();
}

View File

@ -47,16 +47,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return new ArraySegment<byte>(bytes, offset, 10 - offset);
}
public static int WriteBeginChunkBytes(ref WritableBuffer start, int dataCount)
public static int WriteBeginChunkBytes(ref WritableBufferWriter start, int dataCount)
{
var chunkSegment = BeginChunkBytes(dataCount);
start.WriteFast(chunkSegment);
start.Write(chunkSegment.Array, chunkSegment.Offset, chunkSegment.Count);
return chunkSegment.Count;
}
public static void WriteEndChunkBytes(ref WritableBuffer start)
public static void WriteEndChunkBytes(ref WritableBufferWriter start)
{
start.WriteFast(_endChunkBytes);
start.Write(_endChunkBytes.Array, _endChunkBytes.Offset, _endChunkBytes.Count);
}
}
}

View File

@ -993,12 +993,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private static void WriteResponseHeaders(WritableBuffer writableBuffer, Frame frame)
{
var writer = new WritableBufferWriter(writableBuffer);
var responseHeaders = frame.FrameResponseHeaders;
writableBuffer.WriteFast(_bytesHttpVersion11);
writer.Write(_bytesHttpVersion11);
var statusBytes = ReasonPhrases.ToStatusBytes(frame.StatusCode, frame.ReasonPhrase);
writableBuffer.WriteFast(statusBytes);
responseHeaders.CopyTo(ref writableBuffer);
writableBuffer.WriteFast(_bytesEndHeaders);
writer.Write(statusBytes);
responseHeaders.CopyTo(ref writer);
writer.Write(_bytesEndHeaders);
}
public void ParseRequest(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined)

View File

@ -7753,7 +7753,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return true;
}
protected void CopyToFast(ref WritableBuffer output)
protected void CopyToFast(ref WritableBufferWriter output)
{
var tempBits = _bits | (_contentLength.HasValue ? -9223372036854775808L : 0);
@ -7761,7 +7761,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
if (_headers._rawConnection != null)
{
output.WriteFast(_headers._rawConnection);
output.Write(_headers._rawConnection);
}
else
{
@ -7771,8 +7771,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Connection[i];
if (value != null)
{
output.WriteFast(_headerBytes, 17, 14);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 17, 14);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7787,7 +7787,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
if (_headers._rawDate != null)
{
output.WriteFast(_headers._rawDate);
output.Write(_headers._rawDate);
}
else
{
@ -7797,8 +7797,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Date[i];
if (value != null)
{
output.WriteFast(_headerBytes, 31, 8);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 31, 8);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7818,8 +7818,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ContentType[i];
if (value != null)
{
output.WriteFast(_headerBytes, 133, 16);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 133, 16);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7834,7 +7834,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
if (_headers._rawServer != null)
{
output.WriteFast(_headers._rawServer);
output.Write(_headers._rawServer);
}
else
{
@ -7844,8 +7844,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Server[i];
if (value != null)
{
output.WriteFast(_headerBytes, 350, 10);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 350, 10);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7858,8 +7858,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
if ((tempBits & -9223372036854775808L) != 0)
{
output.WriteFast(_headerBytes, 592, 18);
output.WriteNumeric((ulong)ContentLength.Value);
output.Write(_headerBytes, 592, 18);
PipelineExtensions.WriteNumeric(ref output, (ulong)ContentLength.Value);
if((tempBits & ~-9223372036854775808L) == 0)
{
@ -7876,8 +7876,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._CacheControl[i];
if (value != null)
{
output.WriteFast(_headerBytes, 0, 17);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 0, 17);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7897,8 +7897,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._KeepAlive[i];
if (value != null)
{
output.WriteFast(_headerBytes, 39, 14);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 39, 14);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7918,8 +7918,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Pragma[i];
if (value != null)
{
output.WriteFast(_headerBytes, 53, 10);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 53, 10);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7939,8 +7939,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Trailer[i];
if (value != null)
{
output.WriteFast(_headerBytes, 63, 11);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 63, 11);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7955,7 +7955,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
if (_headers._rawTransferEncoding != null)
{
output.WriteFast(_headers._rawTransferEncoding);
output.Write(_headers._rawTransferEncoding);
}
else
{
@ -7965,8 +7965,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._TransferEncoding[i];
if (value != null)
{
output.WriteFast(_headerBytes, 74, 21);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 74, 21);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -7986,8 +7986,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Upgrade[i];
if (value != null)
{
output.WriteFast(_headerBytes, 95, 11);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 95, 11);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8007,8 +8007,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Via[i];
if (value != null)
{
output.WriteFast(_headerBytes, 106, 7);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 106, 7);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8028,8 +8028,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Warning[i];
if (value != null)
{
output.WriteFast(_headerBytes, 113, 11);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 113, 11);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8049,8 +8049,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Allow[i];
if (value != null)
{
output.WriteFast(_headerBytes, 124, 9);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 124, 9);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8070,8 +8070,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ContentEncoding[i];
if (value != null)
{
output.WriteFast(_headerBytes, 149, 20);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 149, 20);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8091,8 +8091,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ContentLanguage[i];
if (value != null)
{
output.WriteFast(_headerBytes, 169, 20);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 169, 20);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8112,8 +8112,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ContentLocation[i];
if (value != null)
{
output.WriteFast(_headerBytes, 189, 20);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 189, 20);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8133,8 +8133,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ContentMD5[i];
if (value != null)
{
output.WriteFast(_headerBytes, 209, 15);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 209, 15);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8154,8 +8154,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ContentRange[i];
if (value != null)
{
output.WriteFast(_headerBytes, 224, 17);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 224, 17);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8175,8 +8175,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Expires[i];
if (value != null)
{
output.WriteFast(_headerBytes, 241, 11);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 241, 11);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8196,8 +8196,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._LastModified[i];
if (value != null)
{
output.WriteFast(_headerBytes, 252, 17);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 252, 17);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8217,8 +8217,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AcceptRanges[i];
if (value != null)
{
output.WriteFast(_headerBytes, 269, 17);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 269, 17);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8238,8 +8238,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Age[i];
if (value != null)
{
output.WriteFast(_headerBytes, 286, 7);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 286, 7);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8259,8 +8259,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ETag[i];
if (value != null)
{
output.WriteFast(_headerBytes, 293, 8);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 293, 8);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8280,8 +8280,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Location[i];
if (value != null)
{
output.WriteFast(_headerBytes, 301, 12);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 301, 12);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8301,8 +8301,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._ProxyAuthenticate[i];
if (value != null)
{
output.WriteFast(_headerBytes, 313, 22);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 313, 22);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8322,8 +8322,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._RetryAfter[i];
if (value != null)
{
output.WriteFast(_headerBytes, 335, 15);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 335, 15);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8343,8 +8343,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._SetCookie[i];
if (value != null)
{
output.WriteFast(_headerBytes, 360, 14);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 360, 14);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8364,8 +8364,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._Vary[i];
if (value != null)
{
output.WriteFast(_headerBytes, 374, 8);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 374, 8);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8385,8 +8385,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._WWWAuthenticate[i];
if (value != null)
{
output.WriteFast(_headerBytes, 382, 20);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 382, 20);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8406,8 +8406,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AccessControlAllowCredentials[i];
if (value != null)
{
output.WriteFast(_headerBytes, 402, 36);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 402, 36);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8427,8 +8427,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AccessControlAllowHeaders[i];
if (value != null)
{
output.WriteFast(_headerBytes, 438, 32);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 438, 32);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8448,8 +8448,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AccessControlAllowMethods[i];
if (value != null)
{
output.WriteFast(_headerBytes, 470, 32);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 470, 32);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8469,8 +8469,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AccessControlAllowOrigin[i];
if (value != null)
{
output.WriteFast(_headerBytes, 502, 31);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 502, 31);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8490,8 +8490,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AccessControlExposeHeaders[i];
if (value != null)
{
output.WriteFast(_headerBytes, 533, 33);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 533, 33);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}
@ -8511,8 +8511,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._AccessControlMaxAge[i];
if (value != null)
{
output.WriteFast(_headerBytes, 566, 26);
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, 566, 26);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}

View File

@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return GetEnumerator();
}
public void CopyTo(ref WritableBuffer output)
public void CopyTo(ref WritableBufferWriter output)
{
CopyToFast(ref output);
if (MaybeUnknown != null)
@ -45,10 +45,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
if (value != null)
{
output.WriteFast(_CrLf);
output.WriteAsciiNoValidation(kv.Key);
output.WriteFast(_colonSpace);
output.WriteAsciiNoValidation(value);
output.Write(_CrLf);
PipelineExtensions.WriteAsciiNoValidation(ref output, kv.Key);
output.Write(_colonSpace);
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}
}
}

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private readonly IPipeWriter _pipe;
private readonly Frame _frame;
// https://github.com/dotnet/corefxlab/issues/1334
// https://github.com/dotnet/corefxlab/issues/1334
// Pipelines don't support multiple awaiters on flush
// this is temporary until it does
private TaskCompletionSource<object> _flushTcs;
@ -59,20 +59,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return TaskCache.CompletedTask;
}
writableBuffer = _pipe.Alloc();
writableBuffer = _pipe.Alloc(1);
var writer = new WritableBufferWriter(writableBuffer);
if (buffer.Count > 0)
{
if (chunk)
{
ChunkWriter.WriteBeginChunkBytes(ref writableBuffer, buffer.Count);
ChunkWriter.WriteBeginChunkBytes(ref writer, buffer.Count);
}
writableBuffer.WriteFast(buffer);
writer.Write(buffer.Array, buffer.Offset, buffer.Count);
if (chunk)
{
ChunkWriter.WriteEndChunkBytes(ref writableBuffer);
ChunkWriter.WriteEndChunkBytes(ref writer);
}
}
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private Task FlushAsyncAwaited(WritableBufferAwaitable awaitable)
{
// https://github.com/dotnet/corefxlab/issues/1334
// https://github.com/dotnet/corefxlab/issues/1334
// Since the flush awaitable doesn't currently support multiple awaiters
// we need to use a task to track the callbacks.
// All awaiters get the same task
@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return;
}
var buffer = _pipe.Alloc();
var buffer = _pipe.Alloc(1);
callback(buffer, state);
buffer.Commit();
}

View File

@ -95,98 +95,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return result;
}
// Temporary until the fast write implementation propagates from corefx
public unsafe static void WriteFast(this WritableBuffer buffer, byte[] source)
{
buffer.WriteFast(source, 0, source.Length);
}
public unsafe static void WriteFast(this WritableBuffer buffer, ArraySegment<byte> source)
{
buffer.WriteFast(source.Array, source.Offset, source.Count);
}
public unsafe static void WriteFast(this WritableBuffer buffer, byte[] source, int offset, int length)
{
var dest = buffer.Buffer.Span;
var destLength = dest.Length;
if (destLength == 0)
{
buffer.Ensure();
// Get the new span and length
dest = buffer.Buffer.Span;
destLength = dest.Length;
}
var sourceLength = length;
if (sourceLength <= destLength)
{
ref byte pSource = ref source[offset];
ref byte pDest = ref dest.DangerousGetPinnableReference();
Unsafe.CopyBlockUnaligned(ref pDest, ref pSource, (uint)sourceLength);
buffer.Advance(sourceLength);
return;
}
buffer.WriteMultiBuffer(source, offset, length);
}
private static unsafe void WriteMultiBuffer(this WritableBuffer buffer, byte[] source, int offset, int length)
{
var remaining = length;
while (remaining > 0)
{
var writable = Math.Min(remaining, buffer.Buffer.Length);
buffer.Ensure(writable);
if (writable == 0)
{
continue;
}
ref byte pSource = ref source[offset];
ref byte pDest = ref buffer.Buffer.Span.DangerousGetPinnableReference();
Unsafe.CopyBlockUnaligned(ref pDest, ref pSource, (uint)writable);
remaining -= writable;
offset += writable;
buffer.Advance(writable);
}
}
/// <summary>
/// Write string characters as ASCII without validating that characters fall in the ASCII range
/// </summary>
/// <remarks>
/// ASCII character validation is done by <see cref="FrameHeaders.ValidateHeaderCharacters(string)"/>
/// </remarks>
/// <param name="buffer">the buffer</param>
/// <param name="data">The string to write</param>
public unsafe static void WriteAsciiNoValidation(this WritableBuffer buffer, string data)
public unsafe static void WriteAsciiNoValidation(ref WritableBufferWriter buffer, string data)
{
if (string.IsNullOrEmpty(data))
{
return;
}
var dest = buffer.Buffer.Span;
var dest = buffer.Span;
var destLength = dest.Length;
var sourceLength = data.Length;
if (destLength == 0)
{
buffer.Ensure();
dest = buffer.Buffer.Span;
destLength = dest.Length;
}
// Fast path, try copying to the available memory directly
if (sourceLength <= destLength)
{
@ -200,26 +119,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
else
{
buffer.WriteAsciiMultiWrite(data);
WriteAsciiMultiWrite(ref buffer, data);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void WriteNumeric(this WritableBuffer buffer, ulong number)
public unsafe static void WriteNumeric(ref WritableBufferWriter buffer, ulong number)
{
const byte AsciiDigitStart = (byte)'0';
var span = buffer.Buffer.Span;
var span = buffer.Span;
var bytesLeftInBlock = span.Length;
if (bytesLeftInBlock == 0)
{
buffer.Ensure();
span = buffer.Buffer.Span;
bytesLeftInBlock = span.Length;
}
// Fast path, try copying to the available memory directly
var simpleWrite = true;
fixed (byte* output = &span.DangerousGetPinnableReference())
@ -258,12 +169,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
if (!simpleWrite)
{
buffer.WriteNumericMultiWrite(number);
WriteNumericMultiWrite(ref buffer, number);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static unsafe void WriteNumericMultiWrite(this WritableBuffer buffer, ulong number)
private static void WriteNumericMultiWrite(ref WritableBufferWriter buffer, ulong number)
{
const byte AsciiDigitStart = (byte)'0';
@ -280,11 +191,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
while (value != 0);
var length = _maxULongByteLength - position;
buffer.WriteFast(new ArraySegment<byte>(byteBuffer, position, length));
buffer.Write(byteBuffer, position, length);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private unsafe static void WriteAsciiMultiWrite(this WritableBuffer buffer, string data)
private unsafe static void WriteAsciiMultiWrite(ref WritableBufferWriter buffer, string data)
{
var remaining = data.Length;
@ -294,16 +205,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
while (remaining > 0)
{
var writable = Math.Min(remaining, buffer.Buffer.Length);
buffer.Ensure(writable);
var writable = Math.Min(remaining, buffer.Span.Length);
if (writable == 0)
{
buffer.Ensure();
continue;
}
fixed (byte* output = &buffer.Buffer.Span.DangerousGetPinnableReference())
fixed (byte* output = &buffer.Span.DangerousGetPinnableReference())
{
EncodeAsciiCharsToBytes(inputSlice, output, writable);
}

View File

@ -33,13 +33,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData(ulong.MinValue)]
[InlineData(ulong.MaxValue)]
[InlineData(4_8_15_16_23_42)]
public async Task WritesNumericToAscii(ulong number)
public void WritesNumericToAscii(ulong number)
{
var writer = _pipe.Writer.Alloc();
writer.WriteNumeric(number);
await writer.FlushAsync();
var writerBuffer = _pipe.Writer.Alloc();
var writer = new WritableBufferWriter(writerBuffer);
PipelineExtensions.WriteNumeric(ref writer, number);
writerBuffer.FlushAsync().GetAwaiter().GetResult();
var reader = await _pipe.Reader.ReadAsync();
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
var numAsStr = number.ToString();
var expected = Encoding.ASCII.GetBytes(numAsStr);
AssertExtensions.Equal(expected, reader.Buffer.Slice(0, numAsStr.Length).ToArray());
@ -51,16 +52,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData(_ulongMaxValueLength - 1)]
public void WritesNumericAcrossSpanBoundaries(int gapSize)
{
var writer = _pipe.Writer.Alloc(100);
var writerBuffer = _pipe.Writer.Alloc(100);
var writer = new WritableBufferWriter(writerBuffer);
// almost fill up the first block
var spacer = new Span<byte>(new byte[writer.Buffer.Length - gapSize]);
var spacer = new byte[writer.Span.Length - gapSize];
writer.Write(spacer);
var bufferLength = writer.Buffer.Length;
writer.WriteNumeric(ulong.MaxValue);
Assert.NotEqual(bufferLength, writer.Buffer.Length);
var bufferLength = writer.Span.Length;
PipelineExtensions.WriteNumeric(ref writer, ulong.MaxValue);
Assert.NotEqual(bufferLength, writer.Span.Length);
writer.FlushAsync().GetAwaiter().GetResult();
writerBuffer.FlushAsync().GetAwaiter().GetResult();
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
var numAsString = ulong.MaxValue.ToString();
@ -79,12 +81,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// null or empty
[InlineData("", new byte[0])]
[InlineData(null, new byte[0])]
public async Task EncodesAsAscii(string input, byte[] expected)
public void EncodesAsAscii(string input, byte[] expected)
{
var writer = _pipe.Writer.Alloc();
writer.WriteAsciiNoValidation(input);
await writer.FlushAsync();
var reader = await _pipe.Reader.ReadAsync();
var writerBuffer = _pipe.Writer.Alloc();
var writer = new WritableBufferWriter(writerBuffer);
PipelineExtensions.WriteAsciiNoValidation(ref writer, input);
writerBuffer.FlushAsync().GetAwaiter().GetResult();
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
if (expected.Length > 0)
{
@ -103,30 +106,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[InlineData("𤭢𐐝")]
// non-ascii characters stored in 16 bits
[InlineData("ñ٢⛄⛵")]
public async Task WriteAsciiNoValidationWritesOnlyOneBytePerChar(string input)
public void WriteAsciiNoValidationWritesOnlyOneBytePerChar(string input)
{
// WriteAscii doesn't validate if characters are in the ASCII range
// but it shouldn't produce more than one byte per character
var writer = _pipe.Writer.Alloc();
writer.WriteAsciiNoValidation(input);
await writer.FlushAsync();
var reader = await _pipe.Reader.ReadAsync();
var writerBuffer = _pipe.Writer.Alloc();
var writer = new WritableBufferWriter(writerBuffer);
PipelineExtensions.WriteAsciiNoValidation(ref writer, input);
writerBuffer.FlushAsync().GetAwaiter().GetResult();
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
Assert.Equal(input.Length, reader.Buffer.Length);
}
[Fact]
public async Task WriteAsciiNoValidation()
public void WriteAsciiNoValidation()
{
const byte maxAscii = 0x7f;
var writer = _pipe.Writer.Alloc();
var writerBuffer = _pipe.Writer.Alloc();
var writer = new WritableBufferWriter(writerBuffer);
for (var i = 0; i < maxAscii; i++)
{
writer.WriteAsciiNoValidation(new string((char)i, 1));
PipelineExtensions.WriteAsciiNoValidation(ref writer, new string((char)i, 1));
}
await writer.FlushAsync();
writerBuffer.FlushAsync().GetAwaiter().GetResult();
var reader = await _pipe.Reader.ReadAsync();
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
var data = reader.Buffer.Slice(0, maxAscii).ToArray();
for (var i = 0; i < maxAscii; i++)
{
@ -147,17 +152,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public void WritesAsciiAcrossBlockBoundaries(int stringLength, int gapSize)
{
var testString = new string(' ', stringLength);
var writer = _pipe.Writer.Alloc(100);
var writerBuffer = _pipe.Writer.Alloc(100);
var writer = new WritableBufferWriter(writerBuffer);
// almost fill up the first block
var spacer = new Span<byte>(new byte[writer.Buffer.Length - gapSize]);
var spacer = new byte[writer.Span.Length - gapSize];
writer.Write(spacer);
Assert.Equal(gapSize, writer.Buffer.Span.Length);
Assert.Equal(gapSize, writer.Span.Length);
var bufferLength = writer.Buffer.Length;
writer.WriteAsciiNoValidation(testString);
Assert.NotEqual(bufferLength, writer.Buffer.Length);
var bufferLength = writer.Span.Length;
PipelineExtensions.WriteAsciiNoValidation(ref writer, testString);
Assert.NotEqual(bufferLength, writer.Span.Length);
writer.FlushAsync().GetAwaiter().GetResult();
writerBuffer.FlushAsync().GetAwaiter().GetResult();
var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult();
var written = reader.Buffer.Slice(spacer.Length, stringLength);

View File

@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
private void InsertData(byte[] bytes)
{
var buffer = Pipe.Writer.Alloc(2048);
buffer.WriteFast(bytes);
buffer.Write(bytes);
// There should not be any backpressure and task completes immediately
buffer.FlushAsync().GetAwaiter().GetResult();
}

View File

@ -522,7 +522,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return true;
}}
{(loop.ClassName == "FrameResponseHeaders" ? $@"
protected void CopyToFast(ref WritableBuffer output)
protected void CopyToFast(ref WritableBufferWriter output)
{{
var tempBits = _bits | (_contentLength.HasValue ? {1L << 63}L : 0);
{Each(loop.Headers.Where(header => header.Identifier != "ContentLength").OrderBy(h => !h.PrimaryHeader), header => $@"
@ -530,7 +530,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{{ {(header.EnhancedSetter == false ? "" : $@"
if (_headers._raw{header.Identifier} != null)
{{
output.WriteFast(_headers._raw{header.Identifier});
output.Write(_headers._raw{header.Identifier});
}}
else ")}
{{
@ -540,8 +540,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
var value = _headers._{header.Identifier}[i];
if (value != null)
{{
output.WriteFast(_headerBytes, {header.BytesOffset}, {header.BytesCount});
output.WriteAsciiNoValidation(value);
output.Write(_headerBytes, {header.BytesOffset}, {header.BytesCount});
PipelineExtensions.WriteAsciiNoValidation(ref output, value);
}}
}}
}}
@ -554,8 +554,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}}{(header.Identifier == "Server" ? $@"
if ((tempBits & {1L << 63}L) != 0)
{{
output.WriteFast(_headerBytes, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesOffset}, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesCount});
output.WriteNumeric((ulong)ContentLength.Value);
output.Write(_headerBytes, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesOffset}, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesCount});
PipelineExtensions.WriteNumeric(ref output, (ulong)ContentLength.Value);
if((tempBits & ~{1L << 63}L) == 0)
{{