Skip to content

Added Write/WriteLine ReadOnlySpan<char> override to HttpResponseStreamWriter #18445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,12 @@ public override void Flush() { }
public override System.Threading.Tasks.Task FlushAsync() { throw null; }
public override void Write(char value) { }
public override void Write(char[] values, int index, int count) { }
public override void Write(System.ReadOnlySpan<char> values) { }
public override void Write(string value) { }
public override System.Threading.Tasks.Task WriteAsync(char value) { throw null; }
public override System.Threading.Tasks.Task WriteAsync(char[] values, int index, int count) { throw null; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a parallel method for WriteAsync that accepts a ReadOnlyMemory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have #18451 for those, but it is based on this one, so I will mark it ready for review once this is merged. Unless you prefer to combine it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now!

public override System.Threading.Tasks.Task WriteAsync(string value) { throw null; }
public override void WriteLine(System.ReadOnlySpan<char> values) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct KeyValueAccumulator
Expand Down
48 changes: 48 additions & 0 deletions src/Http/WebUtilities/src/HttpResponseStreamWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,30 @@ public override void Write(char[] values, int index, int count)
}
}

public override void Write(ReadOnlySpan<char> values)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
}

int written = 0;
while (written < values.Length)
Copy link
Contributor

@jkotalik jkotalik Jan 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: save values.Length in a local rather than checking every iteration of the loop. nvrm you are slicing it

{
if (_charBufferCount == _charBufferSize)
{
FlushInternal(flushEncoder: false);
}

written = CopyToCharBuffer(values);

if (written < values.Length)
{
values = values.Slice(written);
}
};
}

public override void Write(string value)
{
if (_disposed)
Expand All @@ -152,6 +176,17 @@ public override void Write(string value)
}
}

public override void WriteLine(ReadOnlySpan<char> values)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(HttpResponseStreamWriter));
}

Write(values);
Write(NewLine);
}

public override Task WriteAsync(char value)
{
if (_disposed)
Expand Down Expand Up @@ -423,6 +458,19 @@ private void CopyToCharBuffer(char[] values, ref int index, ref int count)
count -= remaining;
}

private int CopyToCharBuffer(ReadOnlySpan<char> values)
{
var remaining = Math.Min(_charBufferSize - _charBufferCount, values.Length);

var source = values.Slice(0, remaining);
var destination = new Span<char>(_charBuffer, _charBufferCount, remaining);
source.CopyTo(destination);

_charBufferCount += remaining;

return remaining;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static Task GetObjectDisposedTask()
{
Expand Down
62 changes: 61 additions & 1 deletion src/Http/WebUtilities/test/HttpResponseStreamWriterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,62 @@ public void WriteCharArray_WritesToStream(int byteLength)
Assert.Equal(byteLength, stream.Length);
}

[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public void WriteReadOnlySpanChar_WritesToStream(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);

// Act
using (writer)
{
var array = new string('a', byteLength).ToCharArray();
var span = new ReadOnlySpan<char>(array);
writer.Write(span);
}

// Assert
Assert.Equal(byteLength, stream.Length);
}

[Theory]
[InlineData(1022, "\n")]
[InlineData(1023, "\n")]
[InlineData(1024, "\n")]
[InlineData(1050, "\n")]
[InlineData(2047, "\n")]
[InlineData(2048, "\n")]
[InlineData(1021, "\r\n")]
[InlineData(1022, "\r\n")]
[InlineData(1023, "\r\n")]
[InlineData(1024, "\r\n")]
[InlineData(1050, "\r\n")]
[InlineData(2046, "\r\n")]
[InlineData(2048, "\r\n")]
public void WriteLineReadOnlySpanChar_WritesToStream(int byteLength, string newLine)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);

writer.NewLine = newLine;
// Act
using (writer)
{
var array = new string('a', byteLength).ToCharArray();
var span = new ReadOnlySpan<char>(array);
writer.WriteLine(span);
}

// Assert
Assert.Equal(byteLength + newLine.Length, stream.Length);
}

[Theory]
[InlineData(1023)]
[InlineData(1024)]
Expand Down Expand Up @@ -539,11 +595,15 @@ public static IEnumerable<object[]> HttpResponseDisposeData()
{
httpResponseStreamWriter.Write(new char[] { 'a', 'b' }, 0, 1);
})};

yield return new object[] { new Action<HttpResponseStreamWriter>((httpResponseStreamWriter) =>
{
httpResponseStreamWriter.Write("hello");
})};
yield return new object[] { new Action<HttpResponseStreamWriter>((httpResponseStreamWriter) =>
{
httpResponseStreamWriter.Write(new ReadOnlySpan<char>(new char[] { 'a', 'b' }));
})};

yield return new object[] { new Action<HttpResponseStreamWriter>((httpResponseStreamWriter) =>
{
httpResponseStreamWriter.Flush();
Expand Down