--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.IO.Tests
+{
+ public partial class StreamWriterTests
+ {
+ [Fact]
+ public void Write_EmptySpan_WritesNothing()
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s))
+ {
+ writer.Write(ReadOnlySpan<char>.Empty);
+ writer.Flush();
+ Assert.Equal(0, s.Position);
+ }
+ }
+
+ [Fact]
+ public void WriteLine_EmptySpan_WritesNewLine()
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s))
+ {
+ writer.WriteLine(ReadOnlySpan<char>.Empty);
+ writer.Flush();
+ Assert.Equal(Environment.NewLine.Length, s.Position);
+ }
+ }
+
+ [Fact]
+ public async Task WriteAsync_EmptyMemory_WritesNothing()
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s))
+ {
+ await writer.WriteAsync(ReadOnlyMemory<char>.Empty);
+ await writer.FlushAsync();
+ Assert.Equal(0, s.Position);
+ }
+ }
+
+ [Fact]
+ public async Task WriteLineAsync_EmptyMemory_WritesNothing()
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s))
+ {
+ await writer.WriteLineAsync(ReadOnlyMemory<char>.Empty);
+ await writer.FlushAsync();
+ Assert.Equal(Environment.NewLine.Length, s.Position);
+ }
+ }
+
+ [Theory]
+ [InlineData(1, 1, 1, false)]
+ [InlineData(100, 1, 100, false)]
+ [InlineData(100, 10, 3, false)]
+ [InlineData(1, 1, 1, true)]
+ [InlineData(100, 1, 100, true)]
+ [InlineData(100, 10, 3, true)]
+ public void Write_Span_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush)
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush })
+ {
+ var data = new char[length];
+ var rand = new Random(42);
+ for (int i = 0; i < data.Length; i++)
+ {
+ data[i] = (char)(rand.Next(0, 26) + 'a');
+ }
+
+ Span<char> source = data;
+ while (source.Length > 0)
+ {
+ int n = Math.Min(source.Length, writeSize);
+ writer.Write(source.Slice(0, n));
+ source = source.Slice(n);
+ }
+
+ writer.Flush();
+
+ Assert.Equal(data, s.ToArray().Select(b => (char)b));
+ }
+ }
+
+ [Theory]
+ [InlineData(1, 1, 1, false)]
+ [InlineData(100, 1, 100, false)]
+ [InlineData(100, 10, 3, false)]
+ [InlineData(1, 1, 1, true)]
+ [InlineData(100, 1, 100, true)]
+ [InlineData(100, 10, 3, true)]
+ public async Task Write_Memory_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush)
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush })
+ {
+ var data = new char[length];
+ var rand = new Random(42);
+ for (int i = 0; i < data.Length; i++)
+ {
+ data[i] = (char)(rand.Next(0, 26) + 'a');
+ }
+
+ ReadOnlyMemory<char> source = data;
+ while (source.Length > 0)
+ {
+ int n = Math.Min(source.Length, writeSize);
+ await writer.WriteAsync(source.Slice(0, n));
+ source = source.Slice(n);
+ }
+
+ await writer.FlushAsync();
+
+ Assert.Equal(data, s.ToArray().Select(b => (char)b));
+ }
+ }
+
+ [Theory]
+ [InlineData(1, 1, 1, false)]
+ [InlineData(100, 1, 100, false)]
+ [InlineData(100, 10, 3, false)]
+ [InlineData(1, 1, 1, true)]
+ [InlineData(100, 1, 100, true)]
+ [InlineData(100, 10, 3, true)]
+ public void WriteLine_Span_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush)
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush })
+ {
+ var data = new char[length];
+ var rand = new Random(42);
+ for (int i = 0; i < data.Length; i++)
+ {
+ data[i] = (char)(rand.Next(0, 26) + 'a');
+ }
+
+ Span<char> source = data;
+ while (source.Length > 0)
+ {
+ int n = Math.Min(source.Length, writeSize);
+ writer.WriteLine(source.Slice(0, n));
+ source = source.Slice(n);
+ }
+
+ writer.Flush();
+
+ Assert.Equal(length + (Environment.NewLine.Length * (length / writeSize)), s.Length);
+ }
+ }
+
+ [Theory]
+ [InlineData(1, 1, 1, false)]
+ [InlineData(100, 1, 100, false)]
+ [InlineData(100, 10, 3, false)]
+ [InlineData(1, 1, 1, true)]
+ [InlineData(100, 1, 100, true)]
+ [InlineData(100, 10, 3, true)]
+ public async Task WriteLineAsync_Memory_WritesExpectedData(int length, int writeSize, int writerBufferSize, bool autoFlush)
+ {
+ using (var s = new MemoryStream())
+ using (var writer = new StreamWriter(s, Encoding.ASCII, writerBufferSize) { AutoFlush = autoFlush })
+ {
+ var data = new char[length];
+ var rand = new Random(42);
+ for (int i = 0; i < data.Length; i++)
+ {
+ data[i] = (char)(rand.Next(0, 26) + 'a');
+ }
+
+ ReadOnlyMemory<char> source = data;
+ while (source.Length > 0)
+ {
+ int n = Math.Min(source.Length, writeSize);
+ await writer.WriteLineAsync(source.Slice(0, n));
+ source = source.Slice(n);
+ }
+
+ await writer.FlushAsync();
+
+ Assert.Equal(length + (Environment.NewLine.Length * (length / writeSize)), s.Length);
+ }
+ }
+
+ [Fact]
+ public async Task WriteAsync_Precanceled_ThrowsCancellationException()
+ {
+ using (var writer = new StreamWriter(Stream.Null))
+ {
+ await Assert.ThrowsAnyAsync<OperationCanceledException>(() => writer.WriteAsync(ReadOnlyMemory<char>.Empty, new CancellationToken(true)));
+ await Assert.ThrowsAnyAsync<OperationCanceledException>(() => writer.WriteLineAsync(ReadOnlyMemory<char>.Empty, new CancellationToken(true)));
+ }
+ }
+ }
+}
<Compile Include="StreamWriter\StreamWriter.CtorTests.cs" />
<Compile Include="StreamWriter\StreamWriter.FlushTests.cs" />
<Compile Include="StreamWriter\StreamWriter.WriteTests.cs" />
+ <Compile Include="StreamWriter\StreamWriter.WriteTests.netcoreapp.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" />
<Compile Include="Stream\Stream.ReadWriteSpan.netcoreapp.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" />
<Compile Include="Stream\Stream.NullTests.netcoreapp.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" />
<Compile Include="Stream\Stream.NullTests.cs" />
public virtual int Read() { throw null; }
public virtual int Read(byte[] buffer, int index, int count) { throw null; }
public virtual int Read(char[] buffer, int index, int count) { throw null; }
- public virtual int Read(System.Span<byte> destination) { throw null; }
- public virtual int Read(System.Span<char> destination) { throw null; }
+ public virtual int Read(System.Span<byte> buffer) { throw null; }
+ public virtual int Read(System.Span<char> buffer) { throw null; }
protected internal int Read7BitEncodedInt() { throw null; }
public virtual bool ReadBoolean() { throw null; }
public virtual byte ReadByte() { throw null; }
public virtual void Write(uint value) { }
[System.CLSCompliantAttribute(false)]
public virtual void Write(ulong value) { }
- public virtual void Write(System.ReadOnlySpan<byte> span) { }
- public virtual void Write(System.ReadOnlySpan<char> span) { }
+ public virtual void Write(System.ReadOnlySpan<byte> buffer) { }
+ public virtual void Write(System.ReadOnlySpan<char> chars) { }
protected void Write7BitEncodedInt(int value) { }
}
public sealed partial class BufferedStream : System.IO.Stream
public override void Write(char value) { }
public override void Write(char[] buffer) { }
public override void Write(char[] buffer, int index, int count) { }
+ public override void Write(System.ReadOnlySpan<char> buffer) { }
public override void Write(string value) { }
+ public override void WriteLine(string value) { }
+ public override void WriteLine(System.ReadOnlySpan<char> buffer) { }
public override System.Threading.Tasks.Task WriteAsync(char value) { throw null; }
public override System.Threading.Tasks.Task WriteAsync(char[] buffer, int index, int count) { throw null; }
+ public override System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory<char> buffer, System.Threading.CancellationToken cancellationToken = default) { throw null; }
public override System.Threading.Tasks.Task WriteAsync(string value) { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync() { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync(char value) { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync(char[] buffer, int index, int count) { throw null; }
+ public override System.Threading.Tasks.Task WriteLineAsync(System.ReadOnlyMemory<char> buffer, System.Threading.CancellationToken cancellationToken = default) { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync(string value) { throw null; }
}
public partial class StringReader : System.IO.TextReader
public override int Peek() { throw null; }
public override int Read() { throw null; }
public override int Read(char[] buffer, int index, int count) { throw null; }
- public override int Read(System.Span<char> destination) { throw null; }
- public override int ReadBlock(System.Span<char> destination) { throw null; }
+ public override int Read(System.Span<char> buffer) { throw null; }
+ public override int ReadBlock(System.Span<char> buffer) { throw null; }
public override System.Threading.Tasks.Task<int> ReadAsync(char[] buffer, int index, int count) { throw null; }
- public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<char> destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override System.Threading.Tasks.Task<int> ReadBlockAsync(char[] buffer, int index, int count) { throw null; }
- public override System.Threading.Tasks.ValueTask<int> ReadBlockAsync(System.Memory<char> destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public override System.Threading.Tasks.ValueTask<int> ReadBlockAsync(System.Memory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override string ReadLine() { throw null; }
public override System.Threading.Tasks.Task<string> ReadLineAsync() { throw null; }
public override string ReadToEnd() { throw null; }
public override string ToString() { throw null; }
public override void Write(char value) { }
public override void Write(char[] buffer, int index, int count) { }
- public override void Write(System.ReadOnlySpan<char> source) { throw null; }
+ public override void Write(System.ReadOnlySpan<char> buffer) { throw null; }
public override void Write(string value) { }
- public override void WriteLine(System.ReadOnlySpan<char> source) { throw null; }
+ public override void WriteLine(System.ReadOnlySpan<char> buffer) { throw null; }
public override System.Threading.Tasks.Task WriteAsync(char value) { throw null; }
public override System.Threading.Tasks.Task WriteAsync(char[] buffer, int index, int count) { throw null; }
- public override System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory<char> source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public override System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override System.Threading.Tasks.Task WriteAsync(string value) { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync(char value) { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync(char[] buffer, int index, int count) { throw null; }
- public override System.Threading.Tasks.Task WriteLineAsync(System.ReadOnlyMemory<char> source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public override System.Threading.Tasks.Task WriteLineAsync(System.ReadOnlyMemory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override System.Threading.Tasks.Task WriteLineAsync(string value) { throw null; }
}
public abstract partial class TextReader : System.MarshalByRefObject, System.IDisposable
public virtual int Peek() { throw null; }
public virtual int Read() { throw null; }
public virtual int Read(char[] buffer, int index, int count) { throw null; }
- public virtual int Read(System.Span<char> destination) { throw null; }
+ public virtual int Read(System.Span<char> buffer) { throw null; }
public virtual System.Threading.Tasks.Task<int> ReadAsync(char[] buffer, int index, int count) { throw null; }
- public virtual System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<char> destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual int ReadBlock(char[] buffer, int index, int count) { throw null; }
- public virtual int ReadBlock(System.Span<char> destination) { throw null; }
+ public virtual int ReadBlock(System.Span<char> buffer) { throw null; }
public virtual System.Threading.Tasks.Task<int> ReadBlockAsync(char[] buffer, int index, int count) { throw null; }
- public virtual System.Threading.Tasks.ValueTask<int> ReadBlockAsync(System.Memory<char> destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.ValueTask<int> ReadBlockAsync(System.Memory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual string ReadLine() { throw null; }
public virtual System.Threading.Tasks.Task<string> ReadLineAsync() { throw null; }
public virtual string ReadToEnd() { throw null; }
public virtual void Write(uint value) { }
[System.CLSCompliantAttribute(false)]
public virtual void Write(ulong value) { }
- public virtual void Write(System.ReadOnlySpan<char> source) { }
+ public virtual void Write(System.ReadOnlySpan<char> buffer) { }
public virtual System.Threading.Tasks.Task WriteAsync(char value) { throw null; }
public System.Threading.Tasks.Task WriteAsync(char[] buffer) { throw null; }
public virtual System.Threading.Tasks.Task WriteAsync(char[] buffer, int index, int count) { throw null; }
- public virtual System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory<char> source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task WriteAsync(string value) { throw null; }
public virtual void WriteLine() { }
public virtual void WriteLine(bool value) { }
public virtual void WriteLine(uint value) { }
[System.CLSCompliantAttribute(false)]
public virtual void WriteLine(ulong value) { }
- public virtual void WriteLine(System.ReadOnlySpan<char> source) { }
+ public virtual void WriteLine(System.ReadOnlySpan<char> buffer) { }
public virtual System.Threading.Tasks.Task WriteLineAsync() { throw null; }
public virtual System.Threading.Tasks.Task WriteLineAsync(char value) { throw null; }
public System.Threading.Tasks.Task WriteLineAsync(char[] buffer) { throw null; }
public virtual System.Threading.Tasks.Task WriteLineAsync(char[] buffer, int index, int count) { throw null; }
- public virtual System.Threading.Tasks.Task WriteLineAsync(System.ReadOnlyMemory<char> source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.Task WriteLineAsync(System.ReadOnlyMemory<char> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task WriteLineAsync(string value) { throw null; }
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
-using System.Diagnostics;
namespace System.IO
{
public override void Write(char[] buffer)
{
- // This may be faster than the one with the index & count since it
- // has to do less argument checking.
- if (buffer == null)
+ if (buffer != null)
{
- return;
- }
-
- CheckAsyncTaskInProgress();
-
- // Threshold of 4 was chosen after running perf tests
- if (buffer.Length <= 4)
- {
- for (int i = 0; i < buffer.Length; i++)
- {
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- Debug.Assert(_charLen - _charPos > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race in user code.");
- _charBuffer[_charPos] = buffer[i];
- _charPos++;
- }
- }
- else
- {
- int count = buffer.Length;
-
- int index = 0;
- while (count > 0)
- {
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- int n = _charLen - _charPos;
- if (n > count)
- {
- n = count;
- }
-
- Debug.Assert(n > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race in user code.");
- Buffer.BlockCopy(buffer, index * sizeof(char), _charBuffer, _charPos * sizeof(char), n * sizeof(char));
- _charPos += n;
- index += n;
- count -= n;
- }
- }
-
- if (_autoFlush)
- {
- Flush(true, false);
+ WriteCore(buffer, _autoFlush);
}
}
throw new ArgumentException(SR.Argument_InvalidOffLen);
}
- CheckAsyncTaskInProgress();
+ WriteCore(new ReadOnlySpan<char>(buffer, index, count), _autoFlush);
+ }
- // Threshold of 4 was chosen after running perf tests
- if (count <= 4)
+ public override void Write(ReadOnlySpan<char> buffer)
+ {
+ if (GetType() == typeof(StreamWriter))
{
- while (count > 0)
- {
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- Debug.Assert(_charLen - _charPos > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race in user code.");
- _charBuffer[_charPos] = buffer[index];
- _charPos++;
- index++;
- count--;
- }
+ WriteCore(buffer, _autoFlush);
}
else
{
- while (count > 0)
- {
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- int n = _charLen - _charPos;
- if (n > count)
- {
- n = count;
- }
-
- Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
- Buffer.BlockCopy(buffer, index * sizeof(char), _charBuffer, _charPos * sizeof(char), n * sizeof(char));
- _charPos += n;
- index += n;
- count -= n;
- }
- }
-
- if (_autoFlush)
- {
- Flush(true, false);
+ // If a derived class may have overridden existing Write behavior,
+ // we need to make sure we use it.
+ base.Write(buffer);
}
}
- public override void Write(string value)
+ private unsafe void WriteCore(ReadOnlySpan<char> buffer, bool autoFlush)
{
- if (value == null)
- {
- return;
- }
-
CheckAsyncTaskInProgress();
- int count = value.Length;
-
- // Threshold of 4 was chosen after running perf tests
- if (count <= 4)
+ if (buffer.Length <= 4 && // Threshold of 4 chosen based on perf experimentation
+ buffer.Length <= _charLen - _charPos)
{
- for (int i = 0; i < count; i++)
+ // For very short buffers and when we don't need to worry about running out of space
+ // in the char buffer, just copy the chars individually.
+ for (int i = 0; i < buffer.Length; i++)
{
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- Debug.Assert(_charLen - _charPos > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
- _charBuffer[_charPos] = value[i];
- _charPos++;
+ _charBuffer[_charPos++] = buffer[i];
}
}
else
{
- int index = 0;
- while (count > 0)
+ // For larger buffers or when we may run out of room in the internal char buffer, copy in chunks.
+ // Use unsafe code until https://github.com/dotnet/coreclr/issues/13827 is addressed, as spans are
+ // resulting in significant overhead (even when the if branch above is taken rather than this
+ // else) due to temporaries that need to be cleared. Given the use of unsafe code, we also
+ // make local copies of instance state to protect against potential concurrent misuse.
+
+ char[] charBuffer = _charBuffer;
+ if (charBuffer == null)
{
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
- int n = _charLen - _charPos;
- if (n > count)
+ fixed (char* bufferPtr = &buffer.DangerousGetPinnableReference())
+ fixed (char* dstPtr = &charBuffer[0])
+ {
+ char* srcPtr = bufferPtr;
+ int count = buffer.Length;
+ int dstPos = _charPos; // use a local copy of _charPos for safety
+ while (count > 0)
{
- n = count;
- }
+ if (dstPos == charBuffer.Length)
+ {
+ Flush(false, false);
+ dstPos = 0;
+ }
+
+ int n = Math.Min(charBuffer.Length - dstPos, count);
+ int bytesToCopy = n * sizeof(char);
+
+ Buffer.MemoryCopy(srcPtr, dstPtr + dstPos, bytesToCopy, bytesToCopy);
- Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
- value.CopyTo(index, _charBuffer, _charPos, n);
- _charPos += n;
- index += n;
- count -= n;
+ _charPos += n;
+ dstPos += n;
+ srcPtr += n;
+ count -= n;
+ }
}
}
- if (_autoFlush)
+ if (autoFlush)
{
Flush(true, false);
}
}
+ public override void Write(string value)
+ {
+ if (value != null)
+ {
+ WriteCore(value.AsReadOnlySpan(), _autoFlush);
+ }
+ }
+
//
// Optimize the most commonly used WriteLine overload. This optimization is important for System.Console in particular
// because of it will make one WriteLine equal to one call to the OS instead of two in the common case.
//
public override void WriteLine(string value)
{
- if (value == null)
- {
- value = String.Empty;
- }
-
CheckAsyncTaskInProgress();
-
- int count = value.Length;
- int index = 0;
- while (count > 0)
+ if (value != null)
{
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- int n = _charLen - _charPos;
- if (n > count)
- {
- n = count;
- }
-
- Debug.Assert(n > 0, "StreamWriter::WriteLine(String) isn't making progress! This is most likely a race condition in user code.");
- value.CopyTo(index, _charBuffer, _charPos, n);
- _charPos += n;
- index += n;
- count -= n;
+ WriteCore(value.AsReadOnlySpan(), autoFlush: false);
}
+ WriteCore(new ReadOnlySpan<char>(CoreNewLine), autoFlush: true);
+ }
- char[] coreNewLine = CoreNewLine;
- for (int i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
+ public override void WriteLine(ReadOnlySpan<char> value)
+ {
+ if (GetType() == typeof(StreamWriter))
{
- if (_charPos == _charLen)
- {
- Flush(false, false);
- }
-
- _charBuffer[_charPos] = coreNewLine[i];
- _charPos++;
+ CheckAsyncTaskInProgress();
+ WriteCore(value, autoFlush: false);
+ WriteCore(new ReadOnlySpan<char>(CoreNewLine), autoFlush: true);
}
-
- if (_autoFlush)
+ else
{
- Flush(true, false);
+ // If a derived class may have overridden existing WriteLine behavior,
+ // we need to make sure we use it.
+ base.WriteLine(value);
}
}
CheckAsyncTaskInProgress();
- Task task = WriteAsyncInternal(this, buffer, index, count, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
+ Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: default);
_asyncWriteTask = task;
return task;
}
+ public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
+ {
+ if (GetType() != typeof(StreamWriter))
+ {
+ // If a derived type may have overridden existing WriteASync(char[], ...) behavior, make sure we use it.
+ return base.WriteAsync(buffer, cancellationToken);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: cancellationToken);
+ _asyncWriteTask = task;
+ return task;
+ }
+
// We pass in private instance fields of this MarshalByRefObject-derived type as local params
// to ensure performant access inside the state machine that corresponds this async method.
// Fields that are written to must be assigned at the end of the method *and* before instance invocations.
- private static async Task WriteAsyncInternal(StreamWriter _this, char[] buffer, int index, int count,
+ private static async Task WriteAsyncInternal(StreamWriter _this, ReadOnlyMemory<char> source,
char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
- bool autoFlush, bool appendNewLine)
+ bool autoFlush, bool appendNewLine, CancellationToken cancellationToken)
{
- Debug.Assert(count == 0 || (count > 0 && buffer != null));
- Debug.Assert(index >= 0);
- Debug.Assert(count >= 0);
- Debug.Assert(buffer == null || (buffer != null && buffer.Length - index >= count));
-
- while (count > 0)
+ while (source.Length > 0)
{
if (charPos == charLen)
{
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
Debug.Assert(_this._charPos == 0);
charPos = 0;
}
- int n = charLen - charPos;
- if (n > count)
- {
- n = count;
- }
-
+ int n = Math.Min(charLen - charPos, source.Length);
Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
- Buffer.BlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char));
+ source.Span.Slice(0, n).CopyTo(new Span<char>(charBuffer, charPos, n));
+ source = source.Slice(n);
charPos += n;
- index += n;
- count -= n;
}
if (appendNewLine)
{
if (charPos == charLen)
{
- await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+ await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
Debug.Assert(_this._charPos == 0);
charPos = 0;
}
if (autoFlush)
{
- await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
+ await _this.FlushAsyncInternal(true, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
Debug.Assert(_this._charPos == 0);
charPos = 0;
}
CheckAsyncTaskInProgress();
- Task task = WriteAsyncInternal(this, null, 0, 0, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
+ Task task = WriteAsyncInternal(this, ReadOnlyMemory<char>.Empty, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
_asyncWriteTask = task;
return task;
CheckAsyncTaskInProgress();
- Task task = WriteAsyncInternal(this, buffer, index, count, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
+ Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
+ _asyncWriteTask = task;
+
+ return task;
+ }
+
+ public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
+ {
+ if (GetType() != typeof(StreamWriter))
+ {
+ return base.WriteLineAsync(buffer, cancellationToken);
+ }
+
+ if (_stream == null)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+ }
+
+ CheckAsyncTaskInProgress();
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: cancellationToken);
_asyncWriteTask = task;
return task;
}
private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
- char[] sCharBuffer, int sCharPos)
+ char[] sCharBuffer, int sCharPos, CancellationToken cancellationToken = default)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
// Perf boost for Flush on non-dirty writers.
if (sCharPos == 0 && !flushStream && !flushEncoder)
{
}
Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, _haveWrittenPreamble,
- _encoding, _encoder, _byteBuffer, _stream);
+ _encoding, _encoder, _byteBuffer, _stream, cancellationToken);
_charPos = 0;
return flushTask;
// to ensure performant access inside the state machine that corresponds this async method.
private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder,
char[] charBuffer, int charPos, bool haveWrittenPreamble,
- Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream)
+ Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream, CancellationToken cancellationToken)
{
if (!haveWrittenPreamble)
{
byte[] preamble = encoding.GetPreamble();
if (preamble.Length > 0)
{
- await stream.WriteAsync(preamble, 0, preamble.Length).ConfigureAwait(false);
+ await stream.WriteAsync(preamble, 0, preamble.Length, cancellationToken).ConfigureAwait(false);
}
}
int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
if (count > 0)
{
- await stream.WriteAsync(byteBuffer, 0, count).ConfigureAwait(false);
+ await stream.WriteAsync(byteBuffer, 0, count, cancellationToken).ConfigureAwait(false);
}
// By definition, calling Flush should flush the stream, but this is
// Services guys have some perf tests where flushing needlessly hurts.
if (flushStream)
{
- await stream.FlushAsync().ConfigureAwait(false);
+ await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
#endregion
return n;
}
- public override int Read(Span<char> destination)
+ public override int Read(Span<char> buffer)
{
if (GetType() != typeof(StringReader))
{
// This overload was added affter the Read(char[], ...) overload, and so in case
// a derived type may have overridden it, we need to delegate to it, which the base does.
- return base.Read(destination);
+ return base.Read(buffer);
}
if (_s == null)
int n = _length - _pos;
if (n > 0)
{
- if (n > destination.Length)
+ if (n > buffer.Length)
{
- n = destination.Length;
+ n = buffer.Length;
}
- _s.AsReadOnlySpan().Slice(_pos, n).CopyTo(destination);
+ _s.AsReadOnlySpan().Slice(_pos, n).CopyTo(buffer);
_pos += n;
}
return n;
}
- public override int ReadBlock(Span<char> destination) => Read(destination);
+ public override int ReadBlock(Span<char> buffer) => Read(buffer);
public override string ReadToEnd()
{
return Task.FromResult(ReadBlock(buffer, index, count));
}
- public override ValueTask<int> ReadBlockAsync(Memory<char> destination, CancellationToken cancellationToken = default) =>
+ public override ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
cancellationToken.IsCancellationRequested ? new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)) :
- new ValueTask<int>(ReadBlock(destination.Span));
+ new ValueTask<int>(ReadBlock(buffer.Span));
public override Task<int> ReadAsync(char[] buffer, int index, int count)
{
return Task.FromResult(Read(buffer, index, count));
}
- public override ValueTask<int> ReadAsync(Memory<char> destination, CancellationToken cancellationToken = default) =>
+ public override ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default) =>
cancellationToken.IsCancellationRequested ? new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)) :
- new ValueTask<int>(Read(destination.Span));
+ new ValueTask<int>(Read(buffer.Span));
#endregion
}
}
_sb.Append(buffer, index, count);
}
- public override void Write(ReadOnlySpan<char> source)
+ public override void Write(ReadOnlySpan<char> buffer)
{
if (GetType() != typeof(StringWriter))
{
// This overload was added affter the Write(char[], ...) overload, and so in case
// a derived type may have overridden it, we need to delegate to it, which the base does.
- base.Write(source);
+ base.Write(buffer);
return;
}
throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
}
- _sb.Append(source);
+ _sb.Append(buffer);
}
// Writes a string to the underlying string buffer. If the given string is
}
}
- public override void WriteLine(ReadOnlySpan<char> source)
+ public override void WriteLine(ReadOnlySpan<char> buffer)
{
if (GetType() != typeof(StringWriter))
{
// This overload was added affter the WriteLine(char[], ...) overload, and so in case
// a derived type may have overridden it, we need to delegate to it, which the base does.
- base.WriteLine(source);
+ base.WriteLine(buffer);
return;
}
throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
}
- _sb.Append(source);
+ _sb.Append(buffer);
WriteLine();
}
return Task.CompletedTask;
}
- public override Task WriteAsync(ReadOnlyMemory<char> source, CancellationToken cancellationToken = default)
+ public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
- Write(source.Span);
+ Write(buffer.Span);
return Task.CompletedTask;
}
return Task.CompletedTask;
}
- public override Task WriteLineAsync(ReadOnlyMemory<char> source, CancellationToken cancellationToken = default)
+ public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}
- WriteLine(source.Span);
+ WriteLine(buffer.Span);
return Task.CompletedTask;
}
// count characters from this TextReader into the
// span of characters Returns the actual number of characters read.
//
- public virtual int Read(Span<char> destination)
+ public virtual int Read(Span<char> buffer)
{
- char[] buffer = ArrayPool<char>.Shared.Rent(destination.Length);
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
try
{
- int numRead = Read(buffer, 0, destination.Length);
- if ((uint)numRead > destination.Length)
+ int numRead = Read(array, 0, buffer.Length);
+ if ((uint)numRead > buffer.Length)
{
throw new IOException(SR.IO_InvalidReadLength);
}
- new Span<char>(buffer, 0, numRead).CopyTo(destination);
+ new Span<char>(array, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
- ArrayPool<char>.Shared.Return(buffer);
+ ArrayPool<char>.Shared.Return(array);
}
}
// Blocking version of read for span of characters. Returns only when count
// characters have been read or the end of the file was reached.
//
- public virtual int ReadBlock(Span<char> destination)
+ public virtual int ReadBlock(Span<char> buffer)
{
- char[] buffer = ArrayPool<char>.Shared.Rent(destination.Length);
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
try
{
- int numRead = ReadBlock(buffer, 0, destination.Length);
- if ((uint)numRead > destination.Length)
+ int numRead = ReadBlock(array, 0, buffer.Length);
+ if ((uint)numRead > buffer.Length)
{
throw new IOException(SR.IO_InvalidReadLength);
}
- new Span<char>(buffer, 0, numRead).CopyTo(destination);
+ new Span<char>(array, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
- ArrayPool<char>.Shared.Return(buffer);
+ ArrayPool<char>.Shared.Return(array);
}
}
return ReadAsyncInternal(buffer, index, count);
}
- public virtual ValueTask<int> ReadAsync(Memory<char> destination, CancellationToken cancellationToken = default(CancellationToken)) =>
- new ValueTask<int>(destination.TryGetArray(out ArraySegment<char> array) ?
+ public virtual ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ new ValueTask<int>(buffer.TryGetArray(out ArraySegment<char> array) ?
ReadAsync(array.Array, array.Offset, array.Count) :
Task<int>.Factory.StartNew(state =>
{
var t = (Tuple<TextReader, Memory<char>>)state;
return t.Item1.Read(t.Item2.Span);
- }, Tuple.Create(this, destination), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
internal virtual Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
{
return ReadBlockAsyncInternal(buffer, index, count);
}
- public virtual ValueTask<int> ReadBlockAsync(Memory<char> destination, CancellationToken cancellationToken = default(CancellationToken)) =>
- new ValueTask<int>(destination.TryGetArray(out ArraySegment<char> array) ?
+ public virtual ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ new ValueTask<int>(buffer.TryGetArray(out ArraySegment<char> array) ?
ReadBlockAsync(array.Array, array.Offset, array.Count) :
Task<int>.Factory.StartNew(state =>
{
var t = (Tuple<TextReader, Memory<char>>)state;
return t.Item1.ReadBlock(t.Item2.Span);
- }, Tuple.Create(this, destination), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
private async Task<int> ReadBlockAsyncInternal(char[] buffer, int index, int count)
{
// Writes a span of characters to the text stream.
//
- public virtual void Write(ReadOnlySpan<char> source)
+ public virtual void Write(ReadOnlySpan<char> buffer)
{
- char[] buffer = ArrayPool<char>.Shared.Rent(source.Length);
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
try
{
- source.CopyTo(new Span<char>(buffer));
- Write(buffer, 0, source.Length);
+ buffer.CopyTo(new Span<char>(array));
+ Write(array, 0, buffer.Length);
}
finally
{
- ArrayPool<char>.Shared.Return(buffer);
+ ArrayPool<char>.Shared.Return(array);
}
}
WriteLine();
}
- public virtual void WriteLine(ReadOnlySpan<char> source)
+ public virtual void WriteLine(ReadOnlySpan<char> buffer)
{
- char[] buffer = ArrayPool<char>.Shared.Rent(source.Length);
+ char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
try
{
- source.CopyTo(new Span<char>(buffer));
- WriteLine(buffer, 0, source.Length);
+ buffer.CopyTo(new Span<char>(array));
+ WriteLine(array, 0, buffer.Length);
}
finally
{
- ArrayPool<char>.Shared.Return(buffer);
+ ArrayPool<char>.Shared.Return(array);
}
}
tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
- public virtual Task WriteAsync(ReadOnlyMemory<char> source, CancellationToken cancellationToken = default(CancellationToken)) =>
- source.DangerousTryGetArray(out ArraySegment<char> array) ?
+ public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ buffer.DangerousTryGetArray(out ArraySegment<char> array) ?
WriteAsync(array.Array, array.Offset, array.Count) :
Task.Factory.StartNew(state =>
{
var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state;
t.Item1.Write(t.Item2.Span);
- }, Tuple.Create(this, source), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
public virtual Task WriteLineAsync(char value)
{
tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
- public virtual Task WriteLineAsync(ReadOnlyMemory<char> source, CancellationToken cancellationToken = default(CancellationToken)) =>
- source.DangerousTryGetArray(out ArraySegment<char> array) ?
+ public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+ buffer.DangerousTryGetArray(out ArraySegment<char> array) ?
WriteLineAsync(array.Array, array.Offset, array.Count) :
Task.Factory.StartNew(state =>
{
var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state;
t.Item1.WriteLine(t.Item2.Span);
- }, Tuple.Create(this, source), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
public virtual Task WriteLineAsync()
{