Fast-path async in HttpResponseStreamWriter

This commit is contained in:
Ben Adams 2018-09-01 03:19:43 +01:00 committed by John Luo
parent f6e20a38e2
commit 800c79c0e4
1 changed files with 86 additions and 12 deletions

View File

@ -3,7 +3,9 @@
using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
@ -150,34 +152,66 @@ namespace Microsoft.AspNetCore.WebUtilities
}
}
public override async Task WriteAsync(char value)
public override Task WriteAsync(char value)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
return GetObjectDisposedTask();
}
if (_charBufferCount == _charBufferSize)
{
await FlushInternalAsync(flushEncoder: false);
return WriteAsyncAwaited(value);
}
else
{
// Enough room in buffer, no need to go async
_charBuffer[_charBufferCount] = value;
_charBufferCount++;
return Task.CompletedTask;
}
}
private async Task WriteAsyncAwaited(char value)
{
Debug.Assert(_charBufferCount == _charBufferSize);
await FlushInternalAsync(flushEncoder: false);
_charBuffer[_charBufferCount] = value;
_charBufferCount++;
}
public override async Task WriteAsync(char[] values, int index, int count)
public override Task WriteAsync(char[] values, int index, int count)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
return GetObjectDisposedTask();
}
if (values == null)
if (values == null || count == 0)
{
return;
return Task.CompletedTask;
}
var remaining = _charBufferSize - _charBufferCount;
if (remaining >= count)
{
// Enough room in buffer, no need to go async
CopyToCharBuffer(values, ref index, ref count);
return Task.CompletedTask;
}
else
{
return WriteAsyncAwaited(values, index, count);
}
}
private async Task WriteAsyncAwaited(char[] values, int index, int count)
{
Debug.Assert(count > 0);
Debug.Assert(_charBufferSize - _charBufferCount > count);
while (count > 0)
{
if (_charBufferCount == _charBufferSize)
@ -186,22 +220,43 @@ namespace Microsoft.AspNetCore.WebUtilities
}
CopyToCharBuffer(values, ref index, ref count);
Debug.Assert(count == 0);
}
}
public override async Task WriteAsync(string value)
public override Task WriteAsync(string value)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
return GetObjectDisposedTask();
}
if (value == null)
var count = value?.Length ?? 0;
if (count == 0)
{
return;
return Task.CompletedTask;
}
var remaining = _charBufferSize - _charBufferCount;
if (remaining >= count)
{
// Enough room in buffer, no need to go async
CopyToCharBuffer(value);
return Task.CompletedTask;
}
else
{
return WriteAsyncAwaited(value);
}
}
private async Task WriteAsyncAwaited(string value)
{
var count = value.Length;
Debug.Assert(count > 0);
Debug.Assert(_charBufferSize - _charBufferCount < count);
var index = 0;
while (count > 0)
{
@ -231,7 +286,7 @@ namespace Microsoft.AspNetCore.WebUtilities
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
return GetObjectDisposedTask();
}
return FlushInternalAsync(flushEncoder: true);
@ -306,6 +361,19 @@ namespace Microsoft.AspNetCore.WebUtilities
}
}
private void CopyToCharBuffer(string value)
{
Debug.Assert(_charBufferSize - _charBufferCount >= value.Length);
value.CopyTo(
sourceIndex: 0,
destination: _charBuffer,
destinationIndex: _charBufferCount,
count: value.Length);
_charBufferCount += value.Length;
}
private void CopyToCharBuffer(string value, ref int index, ref int count)
{
var remaining = Math.Min(_charBufferSize - _charBufferCount, count);
@ -336,5 +404,11 @@ namespace Microsoft.AspNetCore.WebUtilities
index += remaining;
count -= remaining;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Task GetObjectDisposedTask()
{
return Task.FromException(new ObjectDisposedException(nameof(HttpResponseStreamWriter)));
}
}
}