From c09ce65e6de10572abe4058f514338db2ef47ed9 Mon Sep 17 00:00:00 2001 From: Mandar Sahasrabuddhe Date: Thu, 21 Sep 2017 23:09:23 +0530 Subject: [PATCH] CoreFx #22406 Span based APIs - Text Reader Writer (#23786) --- .../tests/StreamReader/StreamReaderTests.cs | 28 +- .../StreamWriter/StreamWriter.WriteTests.cs | 38 +- .../tests/StringWriter/StringWriterTests.cs | 34 +- src/System.IO/tests/System.IO.Tests.csproj | 7 + .../TestDataProvider/TestDataProvider.cs | 72 +++ .../tests/TextReader/CharArrayTextReader.cs | 37 ++ .../tests/TextReader/TextReaderTests.cs | 248 ++++++++ .../TextReader/TextReaderTests.netcoreapp.cs | 51 ++ .../tests/TextWriter/CharArrayTextWriter.cs | 29 + .../tests/TextWriter/TextWriterTests.cs | 543 ++++++++++++++++++ .../TextWriter/TextWriterTests.netcoreapp.cs | 36 ++ .../ref/System.Runtime.Extensions.cs | 4 + .../src/Resources/Strings.resx | 64 ++- .../src/System/IO/TextReader.cs | 48 ++ .../src/System/IO/TextWriter.cs | 33 ++ 15 files changed, 1184 insertions(+), 88 deletions(-) create mode 100644 src/System.IO/tests/TestDataProvider/TestDataProvider.cs create mode 100644 src/System.IO/tests/TextReader/CharArrayTextReader.cs create mode 100644 src/System.IO/tests/TextReader/TextReaderTests.cs create mode 100644 src/System.IO/tests/TextReader/TextReaderTests.netcoreapp.cs create mode 100644 src/System.IO/tests/TextWriter/CharArrayTextWriter.cs create mode 100644 src/System.IO/tests/TextWriter/TextWriterTests.cs create mode 100644 src/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs diff --git a/src/System.IO/tests/StreamReader/StreamReaderTests.cs b/src/System.IO/tests/StreamReader/StreamReaderTests.cs index 0a70a82028ec..a127c809bed2 100644 --- a/src/System.IO/tests/StreamReader/StreamReaderTests.cs +++ b/src/System.IO/tests/StreamReader/StreamReaderTests.cs @@ -40,33 +40,7 @@ protected virtual Stream GetLargeStream() protected Tuple GetCharArrayStream() { - var chArr = new char[]{ - char.MinValue - ,char.MaxValue - ,'\t' - ,' ' - ,'$' - ,'@' - ,'#' - ,'\0' - ,'\v' - ,'\'' - ,'\u3190' - ,'\uC3A0' - ,'A' - ,'5' - ,'\r' - ,'\uFE70' - ,'-' - ,';' - ,'\r' - ,'\n' - ,'T' - ,'3' - ,'\n' - ,'K' - ,'\u00E6' - }; + var chArr = TestDataProvider.CharData; var ms = CreateStream(); var sw = new StreamWriter(ms); diff --git a/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.cs b/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.cs index 5c913da954f8..b36033d11833 100644 --- a/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.cs +++ b/src/System.IO/tests/StreamWriter/StreamWriter.WriteTests.cs @@ -20,7 +20,7 @@ protected virtual Stream CreateStream() [Fact] public void WriteChars() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; // [] Write a wide variety of characters and read them back @@ -40,30 +40,6 @@ public void WriteChars() } } - private static char[] setupArray() - { - return new char[]{ - char.MinValue - ,char.MaxValue - ,'\t' - ,' ' - ,'$' - ,'@' - ,'#' - ,'\0' - ,'\v' - ,'\'' - ,'\u3190' - ,'\uC3A0' - ,'A' - ,'5' - ,'\uFE70' - ,'-' - ,';' - ,'\u00E6' - }; - } - [Fact] public void NullArray() { @@ -78,7 +54,7 @@ public void NullArray() [Fact] public void NegativeOffset() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; // [] Exception if offset is negative Stream ms = CreateStream(); @@ -91,7 +67,7 @@ public void NegativeOffset() [Fact] public void NegativeCount() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; // [] Exception if count is negative Stream ms = CreateStream(); @@ -104,7 +80,7 @@ public void NegativeCount() [Fact] public void WriteCustomLenghtStrings() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; // [] Write some custom length strings Stream ms = CreateStream(); @@ -127,7 +103,7 @@ public void WriteCustomLenghtStrings() [Fact] public void WriteToStreamWriter() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; // [] Just construct a streamwriter and write to it //------------------------------------------------- Stream ms = CreateStream(); @@ -149,7 +125,7 @@ public void WriteToStreamWriter() [Fact] public void TestWritingPastEndOfArray() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; Stream ms = CreateStream(); StreamWriter sw = new StreamWriter(ms); @@ -160,7 +136,7 @@ public void TestWritingPastEndOfArray() [Fact] public void VerifyWrittenString() { - char[] chArr = setupArray(); + char[] chArr = TestDataProvider.CharData; // [] Write string with wide selection of characters and read it back StringBuilder sb = new StringBuilder(40); diff --git a/src/System.IO/tests/StringWriter/StringWriterTests.cs b/src/System.IO/tests/StringWriter/StringWriterTests.cs index 6655a2d54862..3e149612adb6 100644 --- a/src/System.IO/tests/StringWriter/StringWriterTests.cs +++ b/src/System.IO/tests/StringWriter/StringWriterTests.cs @@ -17,33 +17,9 @@ public class StringWriterTests static int[] iArrLargeValues = new int[] { int.MaxValue, int.MaxValue - 1, int.MaxValue / 2, int.MaxValue / 10, int.MaxValue / 100 }; static int[] iArrValidValues = new int[] { 10000, 100000, int.MaxValue / 2000, int.MaxValue / 5000, short.MaxValue }; - private static char[] getCharArray() - { - return new char[]{ - char.MinValue - ,char.MaxValue - ,'\t' - ,' ' - ,'$' - ,'@' - ,'#' - ,'\0' - ,'\v' - ,'\'' - ,'\u3190' - ,'\uC3A0' - ,'A' - ,'5' - ,'\uFE70' - ,'-' - ,';' - ,'\u00E6' - }; - } - private static StringBuilder getSb() { - var chArr = getCharArray(); + var chArr = TestDataProvider.CharData; var sb = new StringBuilder(40); for (int i = 0; i < chArr.Length; i++) sb.Append(chArr[i]); @@ -88,7 +64,7 @@ public static void SimpleWriter() [Fact] public static void WriteArray() { - var chArr = getCharArray(); + var chArr = TestDataProvider.CharData; StringBuilder sb = getSb(); StringWriter sw = new StringWriter(sb); @@ -125,7 +101,7 @@ public static void CantWriteNegativeCount() [Fact] public static void CantWriteIndexLargeValues() { - var chArr = getCharArray(); + var chArr = TestDataProvider.CharData; for (int i = 0; i < iArrLargeValues.Length; i++) { StringWriter sw = new StringWriter(); @@ -136,7 +112,7 @@ public static void CantWriteIndexLargeValues() [Fact] public static void CantWriteCountLargeValues() { - var chArr = getCharArray(); + var chArr = TestDataProvider.CharData; for (int i = 0; i < iArrLargeValues.Length; i++) { StringWriter sw = new StringWriter(); @@ -150,7 +126,7 @@ public static void WriteWithOffset() StringWriter sw = new StringWriter(); StringReader sr; - var chArr = getCharArray(); + var chArr = TestDataProvider.CharData; sw.Write(chArr, 2, 5); diff --git a/src/System.IO/tests/System.IO.Tests.csproj b/src/System.IO/tests/System.IO.Tests.csproj index fb3c81ecbe27..d54417b8cd02 100644 --- a/src/System.IO/tests/System.IO.Tests.csproj +++ b/src/System.IO/tests/System.IO.Tests.csproj @@ -65,6 +65,13 @@ Common\System\IO\WrappedMemoryStream.cs + + + + + + + diff --git a/src/System.IO/tests/TestDataProvider/TestDataProvider.cs b/src/System.IO/tests/TestDataProvider/TestDataProvider.cs new file mode 100644 index 000000000000..b7e37ddff144 --- /dev/null +++ b/src/System.IO/tests/TestDataProvider/TestDataProvider.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.IO.Tests +{ + public static class TestDataProvider + { + private static readonly char[] s_charData; + private static readonly char[] s_smallData; + private static readonly char[] s_largeData; + + public static object FirstObject { get; } = (object)1; + public static object SecondObject { get; } = (object)"[second object]"; + public static object ThirdObject { get; } = (object)""; + public static object[] MultipleObjects { get; } = new object[] { FirstObject, SecondObject, ThirdObject }; + + public static string FormatStringOneObject { get; } = "Object is {0}"; + public static string FormatStringTwoObjects { get; } = $"Object are '{0}', {SecondObject}"; + public static string FormatStringThreeObjects { get; } = $"Objects are {0}, {SecondObject}, {ThirdObject}"; + public static string FormatStringMultipleObjects { get; } = "Multiple Objects are: {0}, {1}, {2}"; + + static TestDataProvider() + { + s_charData = new char[] + { + char.MinValue, + char.MaxValue, + '\t', + ' ', + '$', + '@', + '#', + '\0', + '\v', + '\'', + '\u3190', + '\uC3A0', + 'A', + '5', + '\r', + '\uFE70', + '-', + ';', + '\r', + '\n', + 'T', + '3', + '\n', + 'K', + '\u00E6' + }; + + s_smallData = "HELLO".ToCharArray(); + + var data = new List(); + for (int count = 0; count < 1000; ++count) + { + data.AddRange(s_smallData); + } + s_largeData = data.ToArray(); + } + + public static char[] CharData => s_charData; + + public static char[] SmallData => s_smallData; + + public static char[] LargeData => s_largeData; + } +} diff --git a/src/System.IO/tests/TextReader/CharArrayTextReader.cs b/src/System.IO/tests/TextReader/CharArrayTextReader.cs new file mode 100644 index 000000000000..c82f2bb32188 --- /dev/null +++ b/src/System.IO/tests/TextReader/CharArrayTextReader.cs @@ -0,0 +1,37 @@ +// 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. + +namespace System.IO.Tests +{ + public class CharArrayTextReader : TextReader + { + private readonly char[] _charBuffer; + private int _charPos = 0; + + public bool EndOfStream => _charPos >= _charBuffer.Length; + + public CharArrayTextReader(char[] data) + { + _charBuffer = data; + } + + public override int Peek() + { + if (_charPos == _charBuffer.Length) + { + return -1; + } + return _charBuffer[_charPos]; + } + + public override int Read() + { + if (_charPos == _charBuffer.Length) + { + return -1; + } + return _charBuffer[_charPos++]; + } + } +} diff --git a/src/System.IO/tests/TextReader/TextReaderTests.cs b/src/System.IO/tests/TextReader/TextReaderTests.cs new file mode 100644 index 000000000000..befc068cb6b1 --- /dev/null +++ b/src/System.IO/tests/TextReader/TextReaderTests.cs @@ -0,0 +1,248 @@ +// 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.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class TextReaderTests + { + protected (char[] chArr, CharArrayTextReader textReader) GetCharArray() + { + CharArrayTextReader tr = new CharArrayTextReader(TestDataProvider.CharData); + return (TestDataProvider.CharData, tr); + } + + [Fact] + public void EndOfStream() + { + using (CharArrayTextReader tr = new CharArrayTextReader(TestDataProvider.SmallData)) + { + var result = tr.ReadToEnd(); + + Assert.Equal("HELLO", result); + + Assert.True(tr.EndOfStream, "End of TextReader was not true after ReadToEnd"); + } + } + + [Fact] + public void NotEndOfStream() + { + using (CharArrayTextReader tr = new CharArrayTextReader(TestDataProvider.SmallData)) + { + char[] charBuff = new char[3]; + var result = tr.Read(charBuff, 0, 3); + + Assert.Equal(3, result); + + Assert.Equal("HEL", new string(charBuff)); + + Assert.False(tr.EndOfStream, "End of TextReader was true after ReadToEnd"); + } + } + + [Fact] + public async Task ReadToEndAsync() + { + using (CharArrayTextReader tr = new CharArrayTextReader(TestDataProvider.LargeData)) + { + var result = await tr.ReadToEndAsync(); + + Assert.Equal(5000, result.Length); + } + } + + [Fact] + public void TestRead() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + for (int count = 0; count < baseInfo.chArr.Length; ++count) + { + int tmp = tr.Read(); + Assert.Equal((int)baseInfo.chArr[count], tmp); + } + } + } + + [Fact] + public void ArgumentNullOnNullArray() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + Assert.Throws(() => tr.Read(null, 0, 0)); + } + } + + [Fact] + public void ArgumentOutOfRangeOnInvalidOffset() + { + using (CharArrayTextReader tr = GetCharArray().textReader) + { + Assert.Throws(() => tr.Read(new char[0], -1, 0)); + } + } + + [Fact] + public void ArgumentOutOfRangeOnNegativCount() + { + using (CharArrayTextReader tr = GetCharArray().textReader) + { + AssertExtensions.Throws(null, () => tr.Read(new char[0], 0, 1)); + } + } + + [Fact] + public void ArgumentExceptionOffsetAndCount() + { + using (CharArrayTextReader tr = GetCharArray().textReader) + { + AssertExtensions.Throws(null, () => tr.Read(new char[0], 2, 0)); + } + } + + [Fact] + public void EmptyInput() + { + using (CharArrayTextReader tr = new CharArrayTextReader(new char[] { })) + { + char[] buffer = new char[10]; + int read = tr.Read(buffer, 0, 1); + Assert.Equal(0, read); + } + } + + [Fact] + public void ReadCharArr() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + char[] chArr = new char[baseInfo.chArr.Length]; + + var read = tr.Read(chArr, 0, chArr.Length); + Assert.Equal(chArr.Length, read); + + for (int count = 0; count < baseInfo.chArr.Length; ++count) + { + Assert.Equal(baseInfo.chArr[count], chArr[count]); + } + } + } + + [Fact] + public void ReadBlockCharArr() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + char[] chArr = new char[baseInfo.chArr.Length]; + + var read = tr.ReadBlock(chArr, 0, chArr.Length); + Assert.Equal(chArr.Length, read); + + for (int count = 0; count < baseInfo.chArr.Length; ++count) + { + Assert.Equal(baseInfo.chArr[count], chArr[count]); + } + } + } + + [Fact] + public async void ReadBlockAsyncCharArr() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + char[] chArr = new char[baseInfo.chArr.Length]; + + var read = await tr.ReadBlockAsync(chArr, 0, chArr.Length); + Assert.Equal(chArr.Length, read); + + for (int count = 0; count < baseInfo.chArr.Length; ++count) + { + Assert.Equal(baseInfo.chArr[count], chArr[count]); + } + } + } + + [Fact] + public async Task ReadAsync() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + char[] chArr = new char[baseInfo.chArr.Length]; + + var read = await tr.ReadAsync(chArr, 4, 3); + Assert.Equal(read, 3); + + for (int count = 0; count < 3; ++count) + { + Assert.Equal(baseInfo.chArr[count], chArr[count + 4]); + } + } + } + + [Fact] + public void ReadLines() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + string valueString = new string(baseInfo.chArr); + + var data = tr.ReadLine(); + Assert.Equal(valueString.Substring(0, valueString.IndexOf('\r')), data); + + data = tr.ReadLine(); + Assert.Equal(valueString.Substring(valueString.IndexOf('\r') + 1, 3), data); + + data = tr.ReadLine(); + Assert.Equal(valueString.Substring(valueString.IndexOf('\n') + 1, 2), data); + + data = tr.ReadLine(); + Assert.Equal((valueString.Substring(valueString.LastIndexOf('\n') + 1)), data); + } + } + + [Fact] + public void ReadLines2() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + string valueString = new string(baseInfo.chArr); + + char[] temp = new char[10]; + tr.Read(temp, 0, 1); + var data = tr.ReadLine(); + + Assert.Equal(valueString.Substring(1, valueString.IndexOf('\r') - 1), data); + } + } + + [Fact] + public async Task ReadLineAsyncContinuousNewLinesAndTabs() + { + char[] newLineTabData = new char[] { '\n', '\n', '\r', '\r', '\n' }; + using (CharArrayTextReader tr = new CharArrayTextReader(newLineTabData)) + { + for (int count = 0; count < 4; ++count) + { + var data = await tr.ReadLineAsync(); + Assert.Equal(string.Empty, data); + } + + var eol = await tr.ReadLineAsync(); + Assert.Null(eol); + } + } + } +} diff --git a/src/System.IO/tests/TextReader/TextReaderTests.netcoreapp.cs b/src/System.IO/tests/TextReader/TextReaderTests.netcoreapp.cs new file mode 100644 index 000000000000..6ed515a57ee1 --- /dev/null +++ b/src/System.IO/tests/TextReader/TextReaderTests.netcoreapp.cs @@ -0,0 +1,51 @@ +// 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.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class TextReaderTests + { + [Fact] + public void ReadSpan() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + char[] chArr = new char[baseInfo.chArr.Length]; + var chSpan = new Span(chArr, 0, baseInfo.chArr.Length); + + var read = tr.Read(chSpan); + Assert.Equal(chArr.Length, read); + + for (int i = 0; i < baseInfo.chArr.Length; i++) + { + Assert.Equal(baseInfo.chArr[i], chArr[i]); + } + } + } + + [Fact] + public void ReadBlockSpan() + { + (char[] chArr, CharArrayTextReader textReader) baseInfo = GetCharArray(); + using (CharArrayTextReader tr = baseInfo.textReader) + { + char[] chArr = new char[baseInfo.chArr.Length]; + var chSpan = new Span(chArr, 0, baseInfo.chArr.Length); + + var read = tr.ReadBlock(chSpan); + Assert.Equal(chArr.Length, read); + + for (int i = 0; i < baseInfo.chArr.Length; i++) + { + Assert.Equal(baseInfo.chArr[i], chArr[i]); + } + } + } + } +} diff --git a/src/System.IO/tests/TextWriter/CharArrayTextWriter.cs b/src/System.IO/tests/TextWriter/CharArrayTextWriter.cs new file mode 100644 index 000000000000..0eb9b6194217 --- /dev/null +++ b/src/System.IO/tests/TextWriter/CharArrayTextWriter.cs @@ -0,0 +1,29 @@ +// 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.Text; + +namespace System.IO.Tests +{ + public class CharArrayTextWriter : TextWriter + { + private StringBuilder _sb; + + public override Encoding Encoding => Encoding.Unicode; + + public CharArrayTextWriter() + { + _sb = new StringBuilder(); + } + + public override void Write(char value) + { + _sb.Append(value); + } + + public string Text => _sb.ToString(); + + public void Clear() => _sb.Clear(); + } +} diff --git a/src/System.IO/tests/TextWriter/TextWriterTests.cs b/src/System.IO/tests/TextWriter/TextWriterTests.cs new file mode 100644 index 000000000000..2a7aaf765877 --- /dev/null +++ b/src/System.IO/tests/TextWriter/TextWriterTests.cs @@ -0,0 +1,543 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class TextWriterTests + { + protected static CharArrayTextWriter NewTextWriter => new CharArrayTextWriter() { NewLine = "---" }; + + #region Write Overloads + + [Fact] + public void WriteCharTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + for (int count = 0; count < TestDataProvider.CharData.Length; ++count) + { + tw.Write(TestDataProvider.CharData[count]); + } + Assert.Equal(new string(TestDataProvider.CharData), tw.Text); + } + } + + [Fact] + public void WriteCharArrayTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.CharData); + Assert.Equal(new string(TestDataProvider.CharData), tw.Text); + } + } + + [Fact] + public void WriteCharArrayIndexCountTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.CharData, 3, 5); + Assert.Equal(new string(TestDataProvider.CharData, 3, 5), tw.Text); + } + } + + [Fact] + public void WriteBoolTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(true); + Assert.Equal("True", tw.Text); + + tw.Clear(); + tw.Write(false); + Assert.Equal("False", tw.Text); + } + } + + [Fact] + public void WriteIntTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(int.MinValue); + Assert.Equal(int.MinValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(int.MaxValue); + Assert.Equal(int.MaxValue.ToString(), tw.Text); + } + } + + [Fact] + public void WriteUIntTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(uint.MinValue); + Assert.Equal(uint.MinValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(uint.MaxValue); + Assert.Equal(uint.MaxValue.ToString(), tw.Text); + } + } + + [Fact] + public void WriteLongTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(long.MinValue); + Assert.Equal(long.MinValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(long.MaxValue); + Assert.Equal(long.MaxValue.ToString(), tw.Text); + } + } + + [Fact] + public void WriteULongTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(ulong.MinValue); + Assert.Equal(ulong.MinValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(ulong.MaxValue); + Assert.Equal(ulong.MaxValue.ToString(), tw.Text); + + } + } + + [Fact] + public void WriteFloatTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(float.MinValue); + Assert.Equal(float.MinValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(float.MaxValue); + Assert.Equal(float.MaxValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(float.NaN); + Assert.Equal(float.NaN.ToString(), tw.Text); + } + } + + [Fact] + public void WriteDoubleTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(double.MinValue); + Assert.Equal(double.MinValue.ToString(), tw.Text); + tw.Clear(); + + tw.Write(double.MaxValue); + Assert.Equal(double.MaxValue.ToString(), tw.Text); + tw.Clear(); + + tw.Write(double.NaN); + Assert.Equal(double.NaN.ToString(), tw.Text); + tw.Clear(); + } + } + + [Fact] + public void WriteDecimalTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(decimal.MinValue); + Assert.Equal(decimal.MinValue.ToString(), tw.Text); + + tw.Clear(); + tw.Write(decimal.MaxValue); + Assert.Equal(decimal.MaxValue.ToString(), tw.Text); + } + } + + [Fact] + public void WriteStringTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(new string(TestDataProvider.CharData)); + Assert.Equal(new string(TestDataProvider.CharData), tw.Text); + } + } + + [Fact] + public void WriteObjectTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.FirstObject); + Assert.Equal(TestDataProvider.FirstObject.ToString(), tw.Text); + } + } + + [Fact] + public void WriteStringObjectTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.FormatStringOneObject, TestDataProvider.FirstObject); + Assert.Equal(string.Format(TestDataProvider.FormatStringOneObject, TestDataProvider.FirstObject), tw.Text); + } + } + + [Fact] + public void WriteStringTwoObjectsTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.FormatStringTwoObjects, TestDataProvider.FirstObject, TestDataProvider.SecondObject); + Assert.Equal(string.Format(TestDataProvider.FormatStringTwoObjects, TestDataProvider.FirstObject, TestDataProvider.SecondObject), tw.Text); + } + } + + [Fact] + public void WriteStringThreeObjectsTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.FormatStringThreeObjects, TestDataProvider.FirstObject, TestDataProvider.SecondObject, TestDataProvider.ThirdObject); + Assert.Equal(string.Format(TestDataProvider.FormatStringThreeObjects, TestDataProvider.FirstObject, TestDataProvider.SecondObject, TestDataProvider.ThirdObject), tw.Text); + } + } + + [Fact] + public void WriteStringMultipleObjectsTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects); + Assert.Equal(string.Format(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects), tw.Text); + } + } + + #endregion + + #region WriteLine Overloads + + [Fact] + public void WriteLineTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(); + Assert.Equal(tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineCharTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + for (int count = 0; count < TestDataProvider.CharData.Length; ++count) + { + tw.WriteLine(TestDataProvider.CharData[count]); + } + Assert.Equal(string.Join(tw.NewLine, TestDataProvider.CharData.Select(ch => ch.ToString()).ToArray()) + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineCharArrayTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.CharData); + Assert.Equal(new string(TestDataProvider.CharData) + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineCharArrayIndexCountTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.CharData, 3, 5); + Assert.Equal(new string(TestDataProvider.CharData, 3, 5) + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineBoolTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(true); + Assert.Equal("True" + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(false); + Assert.Equal("False" + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineIntTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(int.MinValue); + Assert.Equal(int.MinValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(int.MaxValue); + Assert.Equal(int.MaxValue.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineUIntTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(uint.MinValue); + Assert.Equal(uint.MinValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(uint.MaxValue); + Assert.Equal(uint.MaxValue.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineLongTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(long.MinValue); + Assert.Equal(long.MinValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(long.MaxValue); + Assert.Equal(long.MaxValue.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineULongTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(ulong.MinValue); + Assert.Equal(ulong.MinValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(ulong.MaxValue); + Assert.Equal(ulong.MaxValue.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineFloatTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(float.MinValue); + Assert.Equal(float.MinValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(float.MaxValue); + Assert.Equal(float.MaxValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(float.NaN); + Assert.Equal(float.NaN.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineDoubleTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(double.MinValue); + Assert.Equal(double.MinValue.ToString() + tw.NewLine, tw.Text); + tw.Clear(); + + tw.WriteLine(double.MaxValue); + Assert.Equal(double.MaxValue.ToString() + tw.NewLine, tw.Text); + tw.Clear(); + + tw.WriteLine(double.NaN); + Assert.Equal(double.NaN.ToString() + tw.NewLine, tw.Text); + tw.Clear(); + } + } + + [Fact] + public void WriteLineDecimalTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(decimal.MinValue); + Assert.Equal(decimal.MinValue.ToString() + tw.NewLine, tw.Text); + + tw.Clear(); + tw.WriteLine(decimal.MaxValue); + Assert.Equal(decimal.MaxValue.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineStringTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(new string(TestDataProvider.CharData)); + Assert.Equal(new string(TestDataProvider.CharData) + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineObjectTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.FirstObject); + Assert.Equal(TestDataProvider.FirstObject.ToString() + tw.NewLine, tw.Text); + } + } + + [Fact] + public void WriteLineStringObjectTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.FormatStringOneObject, TestDataProvider.FirstObject); + Assert.Equal(string.Format(TestDataProvider.FormatStringOneObject + tw.NewLine, TestDataProvider.FirstObject), tw.Text); + } + } + + [Fact] + public void WriteLineStringTwoObjectsTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.FormatStringTwoObjects, TestDataProvider.FirstObject, TestDataProvider.SecondObject); + Assert.Equal(string.Format(TestDataProvider.FormatStringTwoObjects + tw.NewLine, TestDataProvider.FirstObject, TestDataProvider.SecondObject), tw.Text); + } + } + + [Fact] + public void WriteLineStringThreeObjectsTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.FormatStringThreeObjects, TestDataProvider.FirstObject, TestDataProvider.SecondObject, TestDataProvider.ThirdObject); + Assert.Equal(string.Format(TestDataProvider.FormatStringThreeObjects + tw.NewLine, TestDataProvider.FirstObject, TestDataProvider.SecondObject, TestDataProvider.ThirdObject), tw.Text); + } + } + + [Fact] + public void WriteLineStringMultipleObjectsTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects); + Assert.Equal(string.Format(TestDataProvider.FormatStringMultipleObjects + tw.NewLine, TestDataProvider.MultipleObjects), tw.Text); + } + } + + #endregion + + #region Write Async Overloads + + [Fact] + public async void WriteAsyncCharTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + await tw.WriteAsync('a'); + Assert.Equal("a", tw.Text); + } + } + + [Fact] + public async void WriteAsyncStringTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + var toWrite = new string(TestDataProvider.CharData); + await tw.WriteAsync(toWrite); + Assert.Equal(toWrite, tw.Text); + } + } + + [Fact] + public async void WriteAsyncCharArrayIndexCountTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + await tw.WriteAsync(TestDataProvider.CharData, 3, 5); + Assert.Equal(new string(TestDataProvider.CharData, 3, 5), tw.Text); + } + } + + #endregion + + #region WriteLineAsync Overloads + + [Fact] + public async void WriteLineAsyncTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + await tw.WriteLineAsync(); + Assert.Equal(tw.NewLine, tw.Text); + } + } + + [Fact] + public async void WriteLineAsyncCharTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + await tw.WriteLineAsync('a'); + Assert.Equal("a" + tw.NewLine, tw.Text); + } + } + + [Fact] + public async void WriteLineAsyncStringTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + var toWrite = new string(TestDataProvider.CharData); + await tw.WriteLineAsync(toWrite); + Assert.Equal(toWrite + tw.NewLine, tw.Text); + } + } + + [Fact] + public async void WriteLineAsyncCharArrayIndexCount() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + await tw.WriteLineAsync(TestDataProvider.CharData, 3, 5); + Assert.Equal(new string(TestDataProvider.CharData, 3, 5) + tw.NewLine, tw.Text); + } + } + + #endregion + } +} diff --git a/src/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs b/src/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs new file mode 100644 index 000000000000..9c53162ceb9e --- /dev/null +++ b/src/System.IO/tests/TextWriter/TextWriterTests.netcoreapp.cs @@ -0,0 +1,36 @@ +// 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.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Tests +{ + public partial class TextWriterTests + { + [Fact] + public void WriteCharSpanTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + var rs = new ReadOnlySpan(TestDataProvider.CharData, 4, 6); + tw.Write(rs); + Assert.Equal(new string(rs), tw.Text); + } + } + + [Fact] + public void WriteLineCharSpanTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + var rs = new ReadOnlySpan(TestDataProvider.CharData, 4, 6); + tw.WriteLine(rs); + Assert.Equal(new string(rs) + tw.NewLine, tw.Text); + } + } + } +} diff --git a/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs b/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs index a45eca4daede..bfb9374a89a5 100644 --- a/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs +++ b/src/System.Runtime.Extensions/ref/System.Runtime.Extensions.cs @@ -1474,8 +1474,10 @@ protected virtual void Dispose(bool disposing) { } 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 System.Threading.Tasks.Task ReadAsync(char[] buffer, int index, int count) { throw null; } public virtual int ReadBlock(char[] buffer, int index, int count) { throw null; } + public virtual int ReadBlock(Span destination) { throw null; } public virtual System.Threading.Tasks.Task ReadBlockAsync(char[] buffer, int index, int count) { throw null; } public virtual string ReadLine() { throw null; } public virtual System.Threading.Tasks.Task ReadLineAsync() { throw null; } @@ -1517,6 +1519,7 @@ public virtual void Write(string format, params object[] arg) { } public virtual void Write(uint value) { } [System.CLSCompliantAttribute(false)] public virtual void Write(ulong value) { } + public virtual void Write(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; } @@ -1541,6 +1544,7 @@ public virtual void WriteLine(string format, params object[] arg) { } public virtual void WriteLine(uint value) { } [System.CLSCompliantAttribute(false)] public virtual void WriteLine(ulong value) { } + public virtual void WriteLine(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; } diff --git a/src/System.Runtime.Extensions/src/Resources/Strings.resx b/src/System.Runtime.Extensions/src/Resources/Strings.resx index f399311e3815..1b7afc375751 100644 --- a/src/System.Runtime.Extensions/src/Resources/Strings.resx +++ b/src/System.Runtime.Extensions/src/Resources/Strings.resx @@ -1,5 +1,64 @@  + @@ -352,4 +411,7 @@ AppDomain resource monitoring is not supported on this platform. - + + The read operation returned an invalid length. + + \ No newline at end of file diff --git a/src/System.Runtime.Extensions/src/System/IO/TextReader.cs b/src/System.Runtime.Extensions/src/System/IO/TextReader.cs index a24346120337..ff7596ebba34 100644 --- a/src/System.Runtime.Extensions/src/System/IO/TextReader.cs +++ b/src/System.Runtime.Extensions/src/System/IO/TextReader.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using System.Buffers; namespace System.IO { @@ -100,6 +101,30 @@ public virtual int Read(char[] buffer, int index, int count) return n; } + // Reads a span of characters. This method will read up to + // count characters from this TextReader into the + // span of characters Returns the actual number of characters read. + // + public virtual int Read(Span destination) + { + char[] buffer = ArrayPool.Shared.Rent(destination.Length); + + try + { + int numRead = Read(buffer, 0, destination.Length); + if ((uint)numRead > destination.Length) + { + throw new IOException(SR.IO_InvalidReadLength); + } + new Span(buffer, 0, numRead).CopyTo(destination); + return numRead; + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + // Reads all characters from the current position to the end of the // TextReader, and returns them as one string. public virtual string ReadToEnd() @@ -127,6 +152,29 @@ public virtual int ReadBlock(char[] buffer, int index, int count) return n; } + // 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 destination) + { + char[] buffer = ArrayPool.Shared.Rent(destination.Length); + + try + { + int numRead = ReadBlock(buffer, 0, destination.Length); + if ((uint)numRead > destination.Length) + { + throw new IOException(SR.IO_InvalidReadLength); + } + new Span(buffer, 0, numRead).CopyTo(destination); + return numRead; + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + // Reads a line. A line is defined as a sequence of characters followed by // a carriage return ('\r'), a line feed ('\n'), or a carriage return // immediately followed by a line feed. The resulting string does not diff --git a/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs b/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs index ee0142115e98..5faee4f3220c 100644 --- a/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs +++ b/src/System.Runtime.Extensions/src/System/IO/TextWriter.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Threading.Tasks; using System.Runtime.CompilerServices; +using System.Buffers; namespace System.IO { @@ -161,6 +162,23 @@ public virtual void Write(char[] buffer, int index, int count) for (int i = 0; i < count; i++) Write(buffer[index + i]); } + // Writes a span of characters to the text stream. + // + public virtual void Write(ReadOnlySpan source) + { + char[] buffer = ArrayPool.Shared.Rent(source.Length); + + try + { + source.CopyTo(new Span(buffer)); + Write(buffer, 0, source.Length); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + // Writes the text representation of a boolean to the text stream. This // method outputs either Boolean.TrueString or Boolean.FalseString. // @@ -328,6 +346,21 @@ public virtual void WriteLine(char[] buffer, int index, int count) WriteLine(); } + public virtual void WriteLine(ReadOnlySpan source) + { + char[] buffer = ArrayPool.Shared.Rent(source.Length); + + try + { + source.CopyTo(new Span(buffer)); + WriteLine(buffer, 0, source.Length); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + // Writes the text representation of a boolean followed by a line // terminator to the text stream. //