From ca860429f9e6d9f42a0f2cbcd95c58e769fe723d Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 5 Oct 2017 00:36:18 -0400 Subject: [PATCH] Add TextReader/Writer Memory-based virtuals (dotnet/corefx#24434) * Add Memory-based Read/WriteAsync virtuals to TextReader/Writer * Override Span/Memory-based APIs on StringReader/Writer * Address PR feedback Commit migrated from https://github.com/dotnet/corefx/commit/03d705664c6c14b3ac3b1c73caae69e91542f530 --- .../tests/StringReader/StringReader.CtorTests.cs | 2 +- .../StringReader/StringReaderTests.netcoreapp.cs | 122 +++++++++++++++++++++ .../tests/StringWriter/StringWriterTests.cs | 2 +- .../StringWriter/StringWriterTests.netcoreapp.cs | 40 +++++++ .../System.IO/tests/System.IO.Tests.csproj | 6 +- .../tests/TextWriter/TextWriterTests.netcoreapp.cs | 24 +++- .../ref/System.Runtime.Extensions.cs | 64 ++++++----- .../src/System/IO/StringReader.cs | 40 +++++++ .../src/System/IO/StringWriter.cs | 64 ++++++++++- .../src/System/IO/TextReader.cs | 18 +++ .../src/System/IO/TextWriter.cs | 18 +++ 11 files changed, 366 insertions(+), 34 deletions(-) create mode 100644 src/libraries/System.IO/tests/StringReader/StringReaderTests.netcoreapp.cs create mode 100644 src/libraries/System.IO/tests/StringWriter/StringWriterTests.netcoreapp.cs diff --git a/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs b/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs index 2144054..cec91ad 100644 --- a/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs +++ b/src/libraries/System.IO/tests/StringReader/StringReader.CtorTests.cs @@ -8,7 +8,7 @@ using Xunit; namespace System.IO.Tests { - public class ReaderTests + public partial class StringReaderTests { [Fact] public static void StringReaderWithNullString() diff --git a/src/libraries/System.IO/tests/StringReader/StringReaderTests.netcoreapp.cs b/src/libraries/System.IO/tests/StringReader/StringReaderTests.netcoreapp.cs new file mode 100644 index 0000000..cf9ea48 --- /dev/null +++ b/src/libraries/System.IO/tests/StringReader/StringReaderTests.netcoreapp.cs @@ -0,0 +1,122 @@ +// 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.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class StringReaderTests + { + [Fact] + public void ReadSpan_Success() + { + string input = "abcdef"; + var reader = new StringReader(input); + Span s = new char[2]; + + Assert.Equal(2, reader.Read(s)); + Assert.Equal("ab", new string(s.ToArray())); + + Assert.Equal(1, reader.Read(s.Slice(0, 1))); + Assert.Equal("cb", new string(s.ToArray())); + + Assert.Equal(2, reader.Read(s)); + Assert.Equal("de", new string(s.ToArray())); + + Assert.Equal(1, reader.Read(s)); + Assert.Equal("f", new string(s.Slice(0, 1).ToArray())); + + Assert.Equal(0, reader.Read(s)); + } + + [Fact] + public void ReadBlockSpan_Success() + { + string input = "abcdef"; + var reader = new StringReader(input); + Span s = new char[2]; + + Assert.Equal(2, reader.ReadBlock(s)); + Assert.Equal("ab", new string(s.ToArray())); + + Assert.Equal(1, reader.ReadBlock(s.Slice(0, 1))); + Assert.Equal("cb", new string(s.ToArray())); + + Assert.Equal(2, reader.ReadBlock(s)); + Assert.Equal("de", new string(s.ToArray())); + + Assert.Equal(1, reader.ReadBlock(s)); + Assert.Equal("f", new string(s.Slice(0, 1).ToArray())); + + Assert.Equal(0, reader.ReadBlock(s)); + } + + [Fact] + public async Task ReadMemoryAsync_Success() + { + string input = "abcdef"; + var reader = new StringReader(input); + Memory m = new char[2]; + + Assert.Equal(2, await reader.ReadAsync(m)); + Assert.Equal("ab", new string(m.ToArray())); + + Assert.Equal(1, await reader.ReadAsync(m.Slice(0, 1))); + Assert.Equal("cb", new string(m.ToArray())); + + Assert.Equal(2, await reader.ReadAsync(m)); + Assert.Equal("de", new string(m.ToArray())); + + Assert.Equal(1, await reader.ReadAsync(m)); + Assert.Equal("f", new string(m.Slice(0, 1).ToArray())); + + Assert.Equal(0, await reader.ReadAsync(m)); + } + + [Fact] + public async Task ReadBlockMemoryAsync_Success() + { + string input = "abcdef"; + var reader = new StringReader(input); + Memory m = new char[2]; + + Assert.Equal(2, await reader.ReadBlockAsync(m)); + Assert.Equal("ab", new string(m.ToArray())); + + Assert.Equal(1, await reader.ReadBlockAsync(m.Slice(0, 1))); + Assert.Equal("cb", new string(m.ToArray())); + + Assert.Equal(2, await reader.ReadBlockAsync(m)); + Assert.Equal("de", new string(m.ToArray())); + + Assert.Equal(1, await reader.ReadBlockAsync(m)); + Assert.Equal("f", new string(m.Slice(0, 1).ToArray())); + + Assert.Equal(0, await reader.ReadBlockAsync(m)); + } + + [Fact] + public void Disposed_ThrowsException() + { + var reader = new StringReader("abc"); + reader.Dispose(); + + Assert.Throws(() => reader.Read(Span.Empty)); + Assert.Throws(() => reader.ReadBlock(Span.Empty)); + Assert.Throws(() => { reader.ReadAsync(Memory.Empty); }); + Assert.Throws(() => { reader.ReadBlockAsync(Memory.Empty); }); + } + + [Fact] + public async Task Precanceled_ThrowsException() + { + var reader = new StringReader("abc"); + + await Assert.ThrowsAnyAsync(() => reader.ReadAsync(Memory.Empty, new CancellationToken(true)).AsTask()); + await Assert.ThrowsAnyAsync(() => reader.ReadBlockAsync(Memory.Empty, new CancellationToken(true)).AsTask()); + } + } +} diff --git a/src/libraries/System.IO/tests/StringWriter/StringWriterTests.cs b/src/libraries/System.IO/tests/StringWriter/StringWriterTests.cs index 3e14961..4155c9d 100644 --- a/src/libraries/System.IO/tests/StringWriter/StringWriterTests.cs +++ b/src/libraries/System.IO/tests/StringWriter/StringWriterTests.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace System.IO.Tests { - public class StringWriterTests + public partial class StringWriterTests { static int[] iArrInvalidValues = new int[] { -1, -2, -100, -1000, -10000, -100000, -1000000, -10000000, -100000000, -1000000000, int.MinValue, short.MinValue }; static int[] iArrLargeValues = new int[] { int.MaxValue, int.MaxValue - 1, int.MaxValue / 2, int.MaxValue / 10, int.MaxValue / 100 }; diff --git a/src/libraries/System.IO/tests/StringWriter/StringWriterTests.netcoreapp.cs b/src/libraries/System.IO/tests/StringWriter/StringWriterTests.netcoreapp.cs new file mode 100644 index 0000000..afc1702 --- /dev/null +++ b/src/libraries/System.IO/tests/StringWriter/StringWriterTests.netcoreapp.cs @@ -0,0 +1,40 @@ +// 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.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class StringWriterTests + { + [Fact] + public async Task WriteSpanMemory_Success() + { + var sw = new StringWriter(); + + sw.Write((Span)new char[0]); + sw.Write((Span)new char[] { 'a' }); + sw.Write((Span)new char[] { 'b', 'c', 'd' }); + sw.WriteLine((Span)new char[] { 'e' }); + + await sw.WriteAsync((ReadOnlyMemory)new char[0]); + await sw.WriteAsync((ReadOnlyMemory)new char[] { 'f' }); + await sw.WriteAsync((ReadOnlyMemory)new char[] { 'g', 'h', 'i' }); + await sw.WriteLineAsync((ReadOnlyMemory)new char[] { 'j' }); + + Assert.Equal("abcde" + Environment.NewLine + "fghij" + Environment.NewLine, sw.ToString()); + } + + [Fact] + public async Task Precanceled_ThrowsException() + { + var writer = new StringWriter(); + + await Assert.ThrowsAnyAsync(() => writer.WriteAsync(Memory.Empty, new CancellationToken(true))); + await Assert.ThrowsAnyAsync(() => writer.WriteLineAsync(Memory.Empty, new CancellationToken(true))); + } + } +} diff --git a/src/libraries/System.IO/tests/System.IO.Tests.csproj b/src/libraries/System.IO/tests/System.IO.Tests.csproj index d54417b..7273e96 100644 --- a/src/libraries/System.IO/tests/System.IO.Tests.csproj +++ b/src/libraries/System.IO/tests/System.IO.Tests.csproj @@ -51,9 +51,11 @@ + + - + Common\System\Buffers\NativeOwnedMemory.cs @@ -77,4 +79,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs b/src/libraries/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs index 9c53162..fb23dfc 100644 --- a/src/libraries/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs +++ b/src/libraries/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs @@ -2,8 +2,6 @@ // 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.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Xunit; @@ -32,5 +30,27 @@ namespace System.IO.Tests Assert.Equal(new string(rs) + tw.NewLine, tw.Text); } } + + [Fact] + public async Task WriteCharMemoryTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + var rs = new Memory(TestDataProvider.CharData, 4, 6); + await tw.WriteAsync(rs); + Assert.Equal(new string(rs.Span), tw.Text); + } + } + + [Fact] + public async Task WriteLineCharMemoryTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + var rs = new Memory(TestDataProvider.CharData, 4, 6); + await tw.WriteLineAsync(rs); + Assert.Equal(new string(rs.Span) + tw.NewLine, tw.Text); + } + } } } diff --git a/src/libraries/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs b/src/libraries/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs index 9b708d7..e0ec10a 100644 --- a/src/libraries/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs +++ b/src/libraries/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs @@ -141,47 +141,47 @@ namespace System public static double Int64BitsToDouble(long value) { throw null; } public static int SingleToInt32Bits(float value) { throw null; } public static bool ToBoolean(byte[] value, int startIndex) { throw null; } - public static bool ToBoolean(ReadOnlySpan value) { throw null; } + public static bool ToBoolean(System.ReadOnlySpan value) { throw null; } public static char ToChar(byte[] value, int startIndex) { throw null; } - public static char ToChar(ReadOnlySpan value) { throw null; } + public static char ToChar(System.ReadOnlySpan value) { throw null; } public static double ToDouble(byte[] value, int startIndex) { throw null; } - public static double ToDouble(ReadOnlySpan value) { throw null; } + public static double ToDouble(System.ReadOnlySpan value) { throw null; } public static short ToInt16(byte[] value, int startIndex) { throw null; } - public static short ToInt16(ReadOnlySpan value) { throw null; } + public static short ToInt16(System.ReadOnlySpan value) { throw null; } public static int ToInt32(byte[] value, int startIndex) { throw null; } - public static int ToInt32(ReadOnlySpan value) { throw null; } + public static int ToInt32(System.ReadOnlySpan value) { throw null; } public static long ToInt64(byte[] value, int startIndex) { throw null; } - public static long ToInt64(ReadOnlySpan value) { throw null; } + public static long ToInt64(System.ReadOnlySpan value) { throw null; } public static float ToSingle(byte[] value, int startIndex) { throw null; } - public static float ToSingle(ReadOnlySpan value) { throw null; } + public static float ToSingle(System.ReadOnlySpan value) { throw null; } public static string ToString(byte[] value) { throw null; } public static string ToString(byte[] value, int startIndex) { throw null; } public static string ToString(byte[] value, int startIndex, int length) { throw null; } [System.CLSCompliantAttribute(false)] public static ushort ToUInt16(byte[] value, int startIndex) { throw null; } [System.CLSCompliantAttribute(false)] - public static ushort ToUInt16(ReadOnlySpan value) { throw null; } + public static ushort ToUInt16(System.ReadOnlySpan value) { throw null; } [System.CLSCompliantAttribute(false)] public static uint ToUInt32(byte[] value, int startIndex) { throw null; } [System.CLSCompliantAttribute(false)] - public static uint ToUInt32(ReadOnlySpan value) { throw null; } + public static uint ToUInt32(System.ReadOnlySpan value) { throw null; } [System.CLSCompliantAttribute(false)] public static ulong ToUInt64(byte[] value, int startIndex) { throw null; } [System.CLSCompliantAttribute(false)] - public static ulong ToUInt64(ReadOnlySpan value) { throw null; } - public static bool TryWriteBytes(Span destination, bool value) { throw null; } - public static bool TryWriteBytes(Span destination, char value) { throw null; } - public static bool TryWriteBytes(Span destination, double value) { throw null; } - public static bool TryWriteBytes(Span destination, short value) { throw null; } - public static bool TryWriteBytes(Span destination, int value) { throw null; } - public static bool TryWriteBytes(Span destination, long value) { throw null; } - public static bool TryWriteBytes(Span destination, float value) { throw null; } + public static ulong ToUInt64(System.ReadOnlySpan value) { throw null; } + public static bool TryWriteBytes(System.Span destination, bool value) { throw null; } + public static bool TryWriteBytes(System.Span destination, char value) { throw null; } + public static bool TryWriteBytes(System.Span destination, double value) { throw null; } + public static bool TryWriteBytes(System.Span destination, short value) { throw null; } + public static bool TryWriteBytes(System.Span destination, int value) { throw null; } + public static bool TryWriteBytes(System.Span destination, long value) { throw null; } + public static bool TryWriteBytes(System.Span destination, float value) { throw null; } [System.CLSCompliantAttribute(false)] - public static bool TryWriteBytes(Span destination, ushort value) { throw null; } + public static bool TryWriteBytes(System.Span destination, ushort value) { throw null; } [System.CLSCompliantAttribute(false)] - public static bool TryWriteBytes(Span destination, uint value) { throw null; } + public static bool TryWriteBytes(System.Span destination, uint value) { throw null; } [System.CLSCompliantAttribute(false)] - public static bool TryWriteBytes(Span destination, ulong value) { throw null; } + public static bool TryWriteBytes(System.Span destination, ulong value) { throw null; } } public static partial class Convert { @@ -1283,8 +1283,8 @@ namespace System.IO public virtual void Write(uint value) { } [System.CLSCompliantAttribute(false)] public virtual void Write(ulong value) { } - public virtual void Write(ReadOnlySpan span) { } - public virtual void Write(ReadOnlySpan span) { } + public virtual void Write(System.ReadOnlySpan span) { } + public virtual void Write(System.ReadOnlySpan span) { } protected void Write7BitEncodedInt(int value) { } } public sealed partial class BufferedStream : System.IO.Stream @@ -1437,8 +1437,12 @@ namespace System.IO 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 destination) { throw null; } + public override int ReadBlock(System.Span destination) { throw null; } public override System.Threading.Tasks.Task ReadAsync(char[] buffer, int index, int count) { throw null; } + public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override System.Threading.Tasks.Task ReadBlockAsync(char[] buffer, int index, int count) { throw null; } + public override System.Threading.Tasks.ValueTask ReadBlockAsync(System.Memory destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override string ReadLine() { throw null; } public override System.Threading.Tasks.Task ReadLineAsync() { throw null; } public override string ReadToEnd() { throw null; } @@ -1458,12 +1462,16 @@ namespace System.IO 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 source) { throw null; } public override void Write(string value) { } + public override void WriteLine(System.ReadOnlySpan source) { 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 source, 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 source, 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 @@ -1476,11 +1484,13 @@ namespace System.IO 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(Span destination) { throw null; } + public virtual int Read(System.Span destination) { throw null; } public virtual System.Threading.Tasks.Task ReadAsync(char[] buffer, int index, int count) { throw null; } + public virtual System.Threading.Tasks.ValueTask ReadAsync(System.Memory destination, 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(Span destination) { throw null; } + public virtual int ReadBlock(System.Span destination) { throw null; } public virtual System.Threading.Tasks.Task ReadBlockAsync(char[] buffer, int index, int count) { throw null; } + public virtual System.Threading.Tasks.ValueTask ReadBlockAsync(System.Memory destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual string ReadLine() { throw null; } public virtual System.Threading.Tasks.Task ReadLineAsync() { throw null; } public virtual string ReadToEnd() { throw null; } @@ -1521,10 +1531,11 @@ namespace System.IO public virtual void Write(uint value) { } [System.CLSCompliantAttribute(false)] public virtual void Write(ulong value) { } - public virtual void Write(ReadOnlySpan source) { } + public virtual void Write(System.ReadOnlySpan source) { } 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 source, 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) { } @@ -1546,11 +1557,12 @@ namespace System.IO public virtual void WriteLine(uint value) { } [System.CLSCompliantAttribute(false)] public virtual void WriteLine(ulong value) { } - public virtual void WriteLine(ReadOnlySpan source) { } + public virtual void WriteLine(System.ReadOnlySpan source) { } 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 source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task WriteLineAsync(string value) { throw null; } } } diff --git a/src/libraries/System.Runtime.Extensions/src/System/IO/StringReader.cs b/src/libraries/System.Runtime.Extensions/src/System/IO/StringReader.cs index 99bed6c..f2d5a1c 100644 --- a/src/libraries/System.Runtime.Extensions/src/System/IO/StringReader.cs +++ b/src/libraries/System.Runtime.Extensions/src/System/IO/StringReader.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Contracts; +using System.Threading; using System.Threading.Tasks; namespace System.IO @@ -117,6 +118,37 @@ namespace System.IO return n; } + public override int Read(Span destination) + { + 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); + } + + if (_s == null) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed); + } + + int n = _length - _pos; + if (n > 0) + { + if (n > destination.Length) + { + n = destination.Length; + } + + _s.AsReadOnlySpan().Slice(_pos, n).CopyTo(destination); + _pos += n; + } + + return n; + } + + public override int ReadBlock(Span destination) => Read(destination); + public override string ReadToEnd() { if (_s == null) @@ -209,6 +241,10 @@ namespace System.IO return Task.FromResult(ReadBlock(buffer, index, count)); } + public override ValueTask ReadBlockAsync(Memory destination, CancellationToken cancellationToken = default) => + cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : + new ValueTask(ReadBlock(destination.Span)); + public override Task ReadAsync(char[] buffer, int index, int count) { if (buffer == null) @@ -226,6 +262,10 @@ namespace System.IO return Task.FromResult(Read(buffer, index, count)); } + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) => + cancellationToken.IsCancellationRequested ? new ValueTask(Task.FromCanceled(cancellationToken)) : + new ValueTask(Read(destination.Span)); #endregion } } diff --git a/src/libraries/System.Runtime.Extensions/src/System/IO/StringWriter.cs b/src/libraries/System.Runtime.Extensions/src/System/IO/StringWriter.cs index 94dff2d..8d7cec6 100644 --- a/src/libraries/System.Runtime.Extensions/src/System/IO/StringWriter.cs +++ b/src/libraries/System.Runtime.Extensions/src/System/IO/StringWriter.cs @@ -2,8 +2,9 @@ // 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.Text; using System.Globalization; +using System.Text; +using System.Threading; using System.Threading.Tasks; namespace System.IO @@ -124,6 +125,24 @@ namespace System.IO _sb.Append(buffer, index, count); } + public override void Write(ReadOnlySpan source) + { + 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); + return; + } + + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); + } + + _sb.Append(source); + } + // Writes a string to the underlying string buffer. If the given string is // null, nothing is written. // @@ -140,8 +159,27 @@ namespace System.IO } } + public override void WriteLine(ReadOnlySpan source) + { + 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); + return; + } + + if (!_isOpen) + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed); + } + + _sb.Append(source); + WriteLine(); + } + #region Task based Async APIs - + public override Task WriteAsync(char value) { Write(value); @@ -160,6 +198,17 @@ namespace System.IO return Task.CompletedTask; } + public override Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + Write(source.Span); + return Task.CompletedTask; + } + public override Task WriteLineAsync(char value) { WriteLine(value); @@ -178,6 +227,17 @@ namespace System.IO return Task.CompletedTask; } + public override Task WriteLineAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + WriteLine(source.Span); + return Task.CompletedTask; + } + public override Task FlushAsync() { return Task.CompletedTask; diff --git a/src/libraries/System.Runtime.Extensions/src/System/IO/TextReader.cs b/src/libraries/System.Runtime.Extensions/src/System/IO/TextReader.cs index ff7596e..cf0257a 100644 --- a/src/libraries/System.Runtime.Extensions/src/System/IO/TextReader.cs +++ b/src/libraries/System.Runtime.Extensions/src/System/IO/TextReader.cs @@ -247,6 +247,15 @@ namespace System.IO return ReadAsyncInternal(buffer, index, count); } + public virtual ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) => + new ValueTask(destination.TryGetArray(out ArraySegment array) ? + ReadAsync(array.Array, array.Offset, array.Count) : + Task.Factory.StartNew(state => + { + var t = (Tuple>)state; + return t.Item1.Read(t.Item2.Span); + }, Tuple.Create(this, destination), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + internal virtual Task ReadAsyncInternal(char[] buffer, int index, int count) { Debug.Assert(buffer != null); @@ -281,6 +290,15 @@ namespace System.IO return ReadBlockAsyncInternal(buffer, index, count); } + public virtual ValueTask ReadBlockAsync(Memory destination, CancellationToken cancellationToken = default(CancellationToken)) => + new ValueTask(destination.TryGetArray(out ArraySegment array) ? + ReadBlockAsync(array.Array, array.Offset, array.Count) : + Task.Factory.StartNew(state => + { + var t = (Tuple>)state; + return t.Item1.ReadBlock(t.Item2.Span); + }, Tuple.Create(this, destination), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); + private async Task ReadBlockAsyncInternal(char[] buffer, int index, int count) { Debug.Assert(buffer != null); diff --git a/src/libraries/System.Runtime.Extensions/src/System/IO/TextWriter.cs b/src/libraries/System.Runtime.Extensions/src/System/IO/TextWriter.cs index 5faee4f..490c14d 100644 --- a/src/libraries/System.Runtime.Extensions/src/System/IO/TextWriter.cs +++ b/src/libraries/System.Runtime.Extensions/src/System/IO/TextWriter.cs @@ -544,6 +544,15 @@ namespace System.IO tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } + public virtual Task WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) => + source.DangerousTryGetArray(out ArraySegment array) ? + WriteAsync(array.Array, array.Offset, array.Count) : + Task.Factory.StartNew(state => + { + var t = (Tuple>)state; + t.Item1.Write(t.Item2.Span); + }, Tuple.Create(this, source), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + public virtual Task WriteLineAsync(char value) { var tuple = new Tuple(this, value); @@ -587,6 +596,15 @@ namespace System.IO tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } + public virtual Task WriteLineAsync(ReadOnlyMemory source, CancellationToken cancellationToken = default(CancellationToken)) => + source.DangerousTryGetArray(out ArraySegment array) ? + WriteLineAsync(array.Array, array.Offset, array.Count) : + Task.Factory.StartNew(state => + { + var t = (Tuple>)state; + t.Item1.WriteLine(t.Item2.Span); + }, Tuple.Create(this, source), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + public virtual Task WriteLineAsync() { return WriteAsync(CoreNewLine); -- 2.7.4