Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

Commit

Permalink
Fast-path async in HttpResponseStreamWriter
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams authored and John Luo committed Oct 11, 2018
1 parent f6e20a3 commit 800c79c
Showing 1 changed file with 86 additions and 12 deletions.
98 changes: 86 additions & 12 deletions src/Microsoft.AspNetCore.WebUtilities/HttpResponseStreamWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -150,33 +152,65 @@ public override void Write(string value)
}
}

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)
{
Expand All @@ -186,22 +220,43 @@ public override async Task WriteAsync(char[] values, int index, int count)
}

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)
{
Expand Down Expand Up @@ -231,7 +286,7 @@ public override Task FlushAsync()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
return GetObjectDisposedTask();
}

return FlushInternalAsync(flushEncoder: true);
Expand Down Expand Up @@ -306,6 +361,19 @@ private async Task FlushInternalAsync(bool flushEncoder)
}
}

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);
Expand Down Expand Up @@ -336,5 +404,11 @@ private void CopyToCharBuffer(char[] values, ref int index, ref int count)
index += remaining;
count -= remaining;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static Task GetObjectDisposedTask()
{
return Task.FromException(new ObjectDisposedException(nameof(HttpResponseStreamWriter)));
}
}
}

0 comments on commit 800c79c

Please sign in to comment.