Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add TextReader/Writer Memory-based virtuals #24434

Merged
merged 3 commits into from
Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion src/System.IO/tests/StringReader/StringReader.CtorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace System.IO.Tests
{
public class ReaderTests
public partial class StringReaderTests
{
[Fact]
public static void StringReaderWithNullString()
Expand Down
122 changes: 122 additions & 0 deletions src/System.IO/tests/StringReader/StringReaderTests.netcoreapp.cs
Original file line number Diff line number Diff line change
@@ -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<char> 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<char> 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<char> 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<char> 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<ObjectDisposedException>(() => reader.Read(Span<char>.Empty));
Assert.Throws<ObjectDisposedException>(() => reader.ReadBlock(Span<char>.Empty));
Assert.Throws<ObjectDisposedException>(() => { reader.ReadAsync(Memory<char>.Empty); });
Assert.Throws<ObjectDisposedException>(() => { reader.ReadBlockAsync(Memory<char>.Empty); });
}

[Fact]
public async Task Precanceled_ThrowsException()
{
var reader = new StringReader("abc");

await Assert.ThrowsAnyAsync<OperationCanceledException>(() => reader.ReadAsync(Memory<char>.Empty, new CancellationToken(true)).AsTask());
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => reader.ReadBlockAsync(Memory<char>.Empty, new CancellationToken(true)).AsTask());
}
}
}
2 changes: 1 addition & 1 deletion src/System.IO/tests/StringWriter/StringWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

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 };
Expand Down
40 changes: 40 additions & 0 deletions src/System.IO/tests/StringWriter/StringWriterTests.netcoreapp.cs
Original file line number Diff line number Diff line change
@@ -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<char>)new char[0]);
sw.Write((Span<char>)new char[] { 'a' });
sw.Write((Span<char>)new char[] { 'b', 'c', 'd' });
sw.WriteLine((Span<char>)new char[] { 'e' });

await sw.WriteAsync((ReadOnlyMemory<char>)new char[0]);
await sw.WriteAsync((ReadOnlyMemory<char>)new char[] { 'f' });
await sw.WriteAsync((ReadOnlyMemory<char>)new char[] { 'g', 'h', 'i' });
await sw.WriteLineAsync((ReadOnlyMemory<char>)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<OperationCanceledException>(() => writer.WriteAsync(Memory<char>.Empty, new CancellationToken(true)));
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => writer.WriteLineAsync(Memory<char>.Empty, new CancellationToken(true)));
}
}
}
6 changes: 4 additions & 2 deletions src/System.IO/tests/System.IO.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@
<Compile Include="Stream\Stream.Methods.cs" />
<Compile Include="Stream\Stream.TestLeaveOpen.cs" />
<Compile Include="Stream\Stream.TimeoutTests.cs" />
<Compile Include="StringReader\StringReaderTests.netcoreapp.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" />
<Compile Include="StringReader\StringReader.CtorTests.cs" />
<Compile Include="StringWriter\StringWriterTests.netcoreapp.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" />
<Compile Include="StringWriter\StringWriterTests.cs" />
<Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs" Condition="'$(TargetGroup)' == 'netcoreapp'" >
<Compile Include="$(CommonTestPath)\System\Buffers\NativeOwnedMemory.cs" Condition="'$(TargetGroup)' == 'netcoreapp'">
<Link>Common\System\Buffers\NativeOwnedMemory.cs</Link>
</Compile>
<Compile Include="$(CommonTestPath)\System\IO\CallTrackingStream.cs">
Expand All @@ -77,4 +79,4 @@
<EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>
24 changes: 22 additions & 2 deletions src/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -32,5 +30,27 @@ public void WriteLineCharSpanTest()
Assert.Equal(new string(rs) + tw.NewLine, tw.Text);
}
}

[Fact]
public async Task WriteCharMemoryTest()
{
using (CharArrayTextWriter tw = NewTextWriter)
{
var rs = new Memory<char>(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<char>(TestDataProvider.CharData, 4, 6);
await tw.WriteLineAsync(rs);
Assert.Equal(new string(rs.Span) + tw.NewLine, tw.Text);
}
}
}
}
12 changes: 12 additions & 0 deletions src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,8 +1435,12 @@ protected override void Dispose(bool disposing) { }
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; }
Copy link
Member

@ahsonkhan ahsonkhan Oct 4, 2017

Choose a reason for hiding this comment

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

nit: Is specifying System required in the type name? If not, remove (here and elsewhere) to be consistent.

Copy link
Member Author

Choose a reason for hiding this comment

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

Using the full type name is consistent with how the ref generator tool generates the code.

public override int ReadBlock(System.Span<char> destination) { 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.Task<int> ReadBlockAsync(char[] buffer, int index, int count) { throw null; }
public override System.Threading.Tasks.ValueTask<int> ReadBlockAsync(Memory<char> destination, 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; }
Expand All @@ -1456,12 +1460,16 @@ protected override void Dispose(bool disposing) { }
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(string value) { }
public override void WriteLine(System.ReadOnlySpan<char> 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(ReadOnlyMemory<char> 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(ReadOnlyMemory<char> 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
Expand All @@ -1476,9 +1484,11 @@ protected virtual void Dispose(bool disposing) { }
public virtual int Read(char[] buffer, int index, int count) { throw null; }
public virtual int Read(Span<char> destination) { 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 int ReadBlock(char[] buffer, int index, int count) { throw null; }
public virtual int ReadBlock(Span<char> destination) { 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(Memory<char> destination, 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; }
Expand Down Expand Up @@ -1523,6 +1533,7 @@ public virtual void Write(ReadOnlySpan<char> 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(ReadOnlyMemory<char> source, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
Copy link
Member

Choose a reason for hiding this comment

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

Using the full type name is consistent with how the ref generator tool generates the code.

There are some places where the full type name is not being used.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll fix those.

public virtual System.Threading.Tasks.Task WriteAsync(string value) { throw null; }
public virtual void WriteLine() { }
public virtual void WriteLine(bool value) { }
Expand All @@ -1549,6 +1560,7 @@ public virtual void WriteLine(ReadOnlySpan<char> source) { }
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(string value) { throw null; }
}
}
Expand Down
38 changes: 38 additions & 0 deletions src/System.Runtime.Extensions/src/System/IO/StringReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -117,6 +118,35 @@ public override int Read(char[] buffer, int index, int count)
return n;
}

public override int Read(Span<char> destination)
{
if (GetType() != typeof(StringReader))
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to add the comment on why we do the check for derived classes?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, I'll add comments.

{
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<char> destination) => Read(destination);

public override string ReadToEnd()
{
if (_s == null)
Expand Down Expand Up @@ -209,6 +239,10 @@ public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
return Task.FromResult(ReadBlock(buffer, index, count));
}

public override ValueTask<int> ReadBlockAsync(Memory<char> destination, CancellationToken cancellationToken = default) =>
cancellationToken.IsCancellationRequested ? new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)) :
new ValueTask<int>(ReadBlock(destination.Span));

public override Task<int> ReadAsync(char[] buffer, int index, int count)
{
if (buffer == null)
Expand All @@ -226,6 +260,10 @@ 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) =>
cancellationToken.IsCancellationRequested ? new ValueTask<int>(Task.FromCanceled<int>(cancellationToken)) :
new ValueTask<int>(Read(destination.Span));
#endregion
}
}
Loading