From ccbec0f23341ab01d47b2496868264e63d740d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Thu, 9 Dec 2021 02:51:32 +0500 Subject: [PATCH 01/21] Store string-builder as a field and clear when reuse. --- .../src/System/IO/StreamReader.cs | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 3b3e03a806ff54..928406e7cf9521 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -16,6 +16,7 @@ public class StreamReader : TextReader { // StreamReader.Null is threadsafe. public static new readonly StreamReader Null = new NullStreamReader(); + private StringBuilder? _sb; // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well // perf-wise. On even a 40 MB text file, any perf loss by using a 4K @@ -427,14 +428,15 @@ public override string ReadToEnd() CheckAsyncTaskInProgress(); // Call ReadBuffer, then pull data out of charBuffer. - StringBuilder sb = new StringBuilder(_charLen - _charPos); + _sb?.Clear(); + _sb ??= new StringBuilder(_charLen - _charPos); do { - sb.Append(_charBuffer, _charPos, _charLen - _charPos); + _sb.Append(_charBuffer, _charPos, _charLen - _charPos); _charPos = _charLen; // Note we consumed these characters ReadBuffer(); } while (_charLen > 0); - return sb.ToString(); + return _sb.ToString(); } public override int ReadBlock(char[] buffer, int index, int count) @@ -804,7 +806,8 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } } - StringBuilder? sb = null; + + _sb?.Clear(); do { int i = _charPos; @@ -816,10 +819,10 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) if (ch == '\r' || ch == '\n') { string s; - if (sb != null) + if (_sb != null) { - sb.Append(_charBuffer, _charPos, i - _charPos); - s = sb.ToString(); + _sb.Append(_charBuffer, _charPos, i - _charPos); + s = _sb.ToString(); } else { @@ -839,10 +842,10 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } while (i < _charLen); i = _charLen - _charPos; - sb ??= new StringBuilder(i + 80); - sb.Append(_charBuffer, _charPos, i); + _sb ??= new StringBuilder(i + 80); + _sb.Append(_charBuffer, _charPos, i); } while (ReadBuffer() > 0); - return sb.ToString(); + return _sb.ToString(); } public override Task ReadLineAsync() @@ -872,8 +875,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) return null; } - StringBuilder? sb = null; - + _sb?.Clear(); do { char[] tmpCharBuffer = _charBuffer; @@ -891,10 +893,10 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { string s; - if (sb != null) + if (_sb != null) { - sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos); - s = sb.ToString(); + _sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos); + s = _sb.ToString(); } else { @@ -919,11 +921,11 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } while (i < tmpCharLen); i = tmpCharLen - tmpCharPos; - sb ??= new StringBuilder(i + 80); - sb.Append(tmpCharBuffer, tmpCharPos, i); + _sb ??= new StringBuilder(i + 80); + _sb.Append(tmpCharBuffer, tmpCharPos, i); } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); - return sb.ToString(); + return _sb.ToString(); } public override Task ReadToEndAsync() @@ -949,16 +951,17 @@ public override Task ReadToEndAsync() private async Task ReadToEndAsyncInternal() { // Call ReadBuffer, then pull data out of charBuffer. - StringBuilder sb = new StringBuilder(_charLen - _charPos); + _sb?.Clear(); + _sb ??= new StringBuilder(_charLen - _charPos); do { int tmpCharPos = _charPos; - sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos); + _sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos); _charPos = _charLen; // We consumed these characters await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false); } while (_charLen > 0); - return sb.ToString(); + return _sb.ToString(); } public override Task ReadAsync(char[] buffer, int index, int count) From d3407605f29d9ebaf8e6b36c55b9bf6d139c74f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Thu, 9 Dec 2021 03:17:24 +0500 Subject: [PATCH 02/21] IndexOfAny --- .../src/System/IO/StreamReader.cs | 61 ++++++++----------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 928406e7cf9521..68e58af64bfcde 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -819,10 +819,9 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) if (ch == '\r' || ch == '\n') { string s; - if (_sb != null) + if (_sb is { Length: > 0 }) { - _sb.Append(_charBuffer, _charPos, i - _charPos); - s = _sb.ToString(); + s = _sb!.Append(_charBuffer, _charPos, i - _charPos).ToString(); } else { @@ -878,51 +877,39 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) _sb?.Clear(); do { - char[] tmpCharBuffer = _charBuffer; - int tmpCharLen = _charLen; - int tmpCharPos = _charPos; - int i = tmpCharPos; - - do + // Note the following common line feed chars: + // \n - UNIX \r\n - DOS \r - Mac + int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); + if (i >= 0) { - char ch = tmpCharBuffer[i]; + char ch = _charBuffer[_charPos + i]; + string s; - // Note the following common line feed chars: - // \n - UNIX \r\n - DOS \r - Mac - if (ch == '\r' || ch == '\n') + if (_sb is { Length: > 0 }) { - string s; - - if (_sb != null) - { - _sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos); - s = _sb.ToString(); - } - else - { - s = new string(tmpCharBuffer, tmpCharPos, i - tmpCharPos); - } + s = _sb!.Append(_charBuffer, _charPos, i - _charPos).ToString(); + } + else + { + s = new string(_charBuffer, _charPos, i); + } - _charPos = tmpCharPos = i + 1; + _charPos += i + 1; - if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) + if (ch == '\r' && (_charPos < _charLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) + { + if (_charBuffer[_charPos] == '\n') { - tmpCharPos = _charPos; - if (_charBuffer[tmpCharPos] == '\n') - { - _charPos = ++tmpCharPos; - } + _charPos++; } - - return s; } - i++; - } while (i < tmpCharLen); + return s; + } - i = tmpCharLen - tmpCharPos; + i = _charLen - _charPos; _sb ??= new StringBuilder(i + 80); - _sb.Append(tmpCharBuffer, tmpCharPos, i); + _sb.Append(_charBuffer, _charPos, i); } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); return _sb.ToString(); From e2e318fb025f9d1f3c310c6b0462649dde8e8f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Thu, 9 Dec 2021 03:23:38 +0500 Subject: [PATCH 03/21] Fixes by review. --- .../src/System/IO/StreamReader.cs | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 68e58af64bfcde..9435f92ed28f03 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -428,8 +428,14 @@ public override string ReadToEnd() CheckAsyncTaskInProgress(); // Call ReadBuffer, then pull data out of charBuffer. - _sb?.Clear(); - _sb ??= new StringBuilder(_charLen - _charPos); + if (_sb == null) + { + _sb = new StringBuilder(_charLen - _charPos); + } + else + { + _sb.Clear(); + } do { _sb.Append(_charBuffer, _charPos, _charLen - _charPos); @@ -883,16 +889,10 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) if (i >= 0) { char ch = _charBuffer[_charPos + i]; - string s; - if (_sb is { Length: > 0 }) - { - s = _sb!.Append(_charBuffer, _charPos, i - _charPos).ToString(); - } - else - { - s = new string(_charBuffer, _charPos, i); - } + string s = _sb is { Length: > 0 } + ? _sb!.Append(_charBuffer, _charPos, i - _charPos).ToString() + : new string(_charBuffer, _charPos, i); _charPos += i + 1; @@ -938,8 +938,14 @@ public override Task ReadToEndAsync() private async Task ReadToEndAsyncInternal() { // Call ReadBuffer, then pull data out of charBuffer. - _sb?.Clear(); - _sb ??= new StringBuilder(_charLen - _charPos); + if (_sb == null) + { + _sb = new StringBuilder(_charLen - _charPos); + } + else + { + _sb.Clear(); + } do { int tmpCharPos = _charPos; From 7ac2d06c82f47235d69478d1f756feadd5b5f5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Thu, 9 Dec 2021 21:09:08 +0500 Subject: [PATCH 04/21] Added array pools as a draft. --- .../src/System/IO/StreamReader.cs | 96 ++++++++++++++----- 1 file changed, 74 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 9435f92ed28f03..42fd3db20683ed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -875,44 +877,94 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) private async Task ReadLineAsyncInternal() { + static char[] ResizeArray(char[]? array, int atLeastSpace) + { + if (array == null) + { + return ArrayPool.Shared.Rent(atLeastSpace); + } + + char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); + Array.Copy(array, newArr, array.Length); + ArrayPool.Shared.Return(array); + return newArr; + } + static char[] Append(char[]? array1, int destinationOffset, char[] array2, int offset, int length) + { + if (array1 != null && (destinationOffset + length) < array1.Length) + { + Array.Copy(array2, offset, array1, destinationOffset, length); + } + else + { + char[] newArr = ResizeArray(array1, destinationOffset + length + 80); + Array.Copy(array2, offset, newArr, destinationOffset, length); + return newArr; + } + return array1; + } + if (_charPos == _charLen && (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) == 0) { return null; } - _sb?.Clear(); - do + char[]? polledArray = null; + var lastI = 0; + try { - // Note the following common line feed chars: - // \n - UNIX \r\n - DOS \r - Mac - int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); - if (i >= 0) + do { - char ch = _charBuffer[_charPos + i]; + // Note the following common line feed chars: + // \n - UNIX \r\n - DOS \r - Mac + int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); + if (i >= 0) + { + char ch = _charBuffer[_charPos + i]; - string s = _sb is { Length: > 0 } - ? _sb!.Append(_charBuffer, _charPos, i - _charPos).ToString() - : new string(_charBuffer, _charPos, i); + string? s = null; + if (polledArray != null) + { + polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i - _charPos); + // lastI += i - _charPos;; + s = new string(polledArray); + } + else + { + s = new string(_charBuffer, _charPos, i); + } - _charPos += i + 1; + _charPos += i + 1; - if (ch == '\r' && (_charPos < _charLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) - { - if (_charBuffer[_charPos] == '\n') + if (ch == '\r' && (_charPos < _charLen || + (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) { - _charPos++; + if (_charBuffer[_charPos] == '\n') + { + _charPos++; + } } + + return s; } - return s; - } + i = _charLen - _charPos; - i = _charLen - _charPos; - _sb ??= new StringBuilder(i + 80); - _sb.Append(_charBuffer, _charPos, i); - } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); + polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i); + lastI += i; + // _sb ??= new StringBuilder(i + 80); + // _sb.Append(_charBuffer, _charPos, i); + } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); - return _sb.ToString(); + return new string(polledArray); + } + finally + { + if (polledArray != null) + { + ArrayPool.Shared.Return(polledArray); + } + } } public override Task ReadToEndAsync() From ff0ee6d5de9104614c66f355aa2030e3bd32362e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Thu, 9 Dec 2021 22:33:41 +0500 Subject: [PATCH 05/21] Return new string from polled array with specific length. --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 42fd3db20683ed..1c29a7c9c5fbfa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -926,8 +926,8 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o if (polledArray != null) { polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i - _charPos); - // lastI += i - _charPos;; - s = new string(polledArray); + lastI += i - _charPos; + s = new string(polledArray, 0, lastI); } else { @@ -956,7 +956,7 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o // _sb.Append(_charBuffer, _charPos, i); } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); - return new string(polledArray); + return new string(polledArray, 0, lastI); } finally { From 6f2af2d87d9ca6010879916c63d48a2264312831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Fri, 10 Dec 2021 21:04:22 +0500 Subject: [PATCH 06/21] Add ValueStringBuilder and fix length in ReadAsyncInternal. --- .../src/System/IO/StreamReader.cs | 98 ++++++++----------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 1c29a7c9c5fbfa..a3ef212bcde6af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -18,7 +18,7 @@ public class StreamReader : TextReader { // StreamReader.Null is threadsafe. public static new readonly StreamReader Null = new NullStreamReader(); - private StringBuilder? _sb; + // private StringBuilder? _sb; // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well // perf-wise. On even a 40 MB text file, any perf loss by using a 4K @@ -430,21 +430,14 @@ public override string ReadToEnd() CheckAsyncTaskInProgress(); // Call ReadBuffer, then pull data out of charBuffer. - if (_sb == null) - { - _sb = new StringBuilder(_charLen - _charPos); - } - else - { - _sb.Clear(); - } + using ValueStringBuilder sb = new(_charLen - _charPos); do { - _sb.Append(_charBuffer, _charPos, _charLen - _charPos); + sb.Append(_charBuffer.AsSpan(_charPos, _charLen - _charPos)); _charPos = _charLen; // Note we consumed these characters ReadBuffer(); } while (_charLen > 0); - return _sb.ToString(); + return sb.ToString(); } public override int ReadBlock(char[] buffer, int index, int count) @@ -814,45 +807,43 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } } - - _sb?.Clear(); + using ValueStringBuilder sb = new(0); do { - int i = _charPos; - do + // Note the following common line feed chars: + // \n - UNIX \r\n - DOS \r - Mac + int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); + if (i >= 0) { - char ch = _charBuffer[i]; - // Note the following common line feed chars: - // \n - UNIX \r\n - DOS \r - Mac - if (ch == '\r' || ch == '\n') + char ch = _charBuffer[_charPos + i]; + + string? s; + + if (sb is { Length: > 0 }) { - string s; - if (_sb is { Length: > 0 }) - { - s = _sb!.Append(_charBuffer, _charPos, i - _charPos).ToString(); - } - else - { - s = new string(_charBuffer, _charPos, i - _charPos); - } - _charPos = i + 1; - if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0)) + sb.Append(_charBuffer.AsSpan(_charPos, i)); + s = sb.ToString(); + } + else + { + s = new string(_charBuffer, _charPos, i); + } + _charPos = i + 1; + if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0)) + { + if (_charBuffer[_charPos] == '\n') { - if (_charBuffer[_charPos] == '\n') - { - _charPos++; - } + _charPos++; } - return s; } - i++; - } while (i < _charLen); + return s; + } i = _charLen - _charPos; - _sb ??= new StringBuilder(i + 80); - _sb.Append(_charBuffer, _charPos, i); + + sb.Append(_charBuffer.AsSpan(_charPos, i)); } while (ReadBuffer() > 0); - return _sb.ToString(); + return sb.ToString(); } public override Task ReadLineAsync() @@ -910,7 +901,7 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o } char[]? polledArray = null; - var lastI = 0; + int lastI = 0; try { do @@ -922,11 +913,11 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o { char ch = _charBuffer[_charPos + i]; - string? s = null; + string? s; if (polledArray != null) { - polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i - _charPos); - lastI += i - _charPos; + polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i); + lastI += i; s = new string(polledArray, 0, lastI); } else @@ -936,8 +927,7 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o _charPos += i + 1; - if (ch == '\r' && (_charPos < _charLen || - (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) + if (ch == '\r' && (_charPos < _charLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) { if (_charBuffer[_charPos] == '\n') { @@ -952,8 +942,6 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i); lastI += i; - // _sb ??= new StringBuilder(i + 80); - // _sb.Append(_charBuffer, _charPos, i); } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); return new string(polledArray, 0, lastI); @@ -990,23 +978,17 @@ public override Task ReadToEndAsync() private async Task ReadToEndAsyncInternal() { // Call ReadBuffer, then pull data out of charBuffer. - if (_sb == null) - { - _sb = new StringBuilder(_charLen - _charPos); - } - else - { - _sb.Clear(); - } + + StringBuilder sb = new(_charLen - _charPos); do { int tmpCharPos = _charPos; - _sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos); + sb.Append(_charBuffer.AsSpan(tmpCharPos, _charLen - tmpCharPos)); _charPos = _charLen; // We consumed these characters await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false); } while (_charLen > 0); - return _sb.ToString(); + return sb.ToString(); } public override Task ReadAsync(char[] buffer, int index, int count) From 03fae0948d7784afc6cedf58d5f357f733a6f33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Fri, 10 Dec 2021 21:38:10 +0500 Subject: [PATCH 07/21] Summing char-position --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index a3ef212bcde6af..3b1714993f24a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -818,7 +818,6 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) char ch = _charBuffer[_charPos + i]; string? s; - if (sb is { Length: > 0 }) { sb.Append(_charBuffer.AsSpan(_charPos, i)); @@ -828,7 +827,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { s = new string(_charBuffer, _charPos, i); } - _charPos = i + 1; + _charPos += i + 1; if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0)) { if (_charBuffer[_charPos] == '\n') @@ -924,9 +923,7 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o { s = new string(_charBuffer, _charPos, i); } - _charPos += i + 1; - if (ch == '\r' && (_charPos < _charLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) { if (_charBuffer[_charPos] == '\n') @@ -943,7 +940,6 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i); lastI += i; } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); - return new string(polledArray, 0, lastI); } finally From 75cfdc6ac3011a7db906ccd291b8d21cc22eec6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Fri, 10 Dec 2021 21:42:40 +0500 Subject: [PATCH 08/21] Added tests for the async version of ReadLine --- .../tests/StreamReader/StreamReaderTests.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs index 4a2b91f8862b57..8c73c9cc25707a 100644 --- a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs +++ b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs @@ -287,6 +287,29 @@ public void VanillaReadLines() Assert.Equal((valueString.Substring(valueString.LastIndexOf('\n') + 1)), data); } + [Fact] + public async Task VanillaReadLinesAsync() + { + var baseInfo = GetCharArrayStream(); + var sr = baseInfo.Item2; + + string valueString = new string(baseInfo.Item1); + + + var data = await sr.ReadLineAsync(); + Assert.Equal(valueString.Substring(0, valueString.IndexOf('\r')), data); + + data = await sr.ReadLineAsync(); + Assert.Equal(valueString.Substring(valueString.IndexOf('\r') + 1, 3), data); + + data = await sr.ReadLineAsync(); + Assert.Equal(valueString.Substring(valueString.IndexOf('\n') + 1, 2), data); + + data = await sr.ReadLineAsync(); + Assert.Equal((valueString.Substring(valueString.LastIndexOf('\n') + 1)), data); + } + + [Fact] public void VanillaReadLines2() { @@ -301,6 +324,20 @@ public void VanillaReadLines2() Assert.Equal(valueString.Substring(1, valueString.IndexOf('\r') - 1), data); } + [Fact] + public async Task VanillaReadLines2Async() + { + var baseInfo = GetCharArrayStream(); + var sr = baseInfo.Item2; + + string valueString = new string(baseInfo.Item1); + + var temp = new char[10]; + sr.Read(temp, 0, 1); + var data = await sr.ReadLineAsync(); + Assert.Equal(valueString.Substring(1, valueString.IndexOf('\r') - 1), data); + } + [Fact] public async Task ContinuousNewLinesAndTabsAsync() { From 04860a65c43e958d4c0dcfe75f0e63cc0ac625eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Fri, 10 Dec 2021 21:53:06 +0500 Subject: [PATCH 09/21] Renaming --- .../src/System/IO/StreamReader.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 3b1714993f24a3..6b5aae3e6b33db 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -867,7 +867,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) private async Task ReadLineAsyncInternal() { - static char[] ResizeArray(char[]? array, int atLeastSpace) + static char[] ResizeOrPoolNewArray(char[]? array, int atLeastSpace) { if (array == null) { @@ -879,19 +879,19 @@ static char[] ResizeArray(char[]? array, int atLeastSpace) ArrayPool.Shared.Return(array); return newArr; } - static char[] Append(char[]? array1, int destinationOffset, char[] array2, int offset, int length) + static char[] Append(char[]? to, int destinationOffset, char[] @from, int offset, int length) { - if (array1 != null && (destinationOffset + length) < array1.Length) + if (to != null && (destinationOffset + length) < to.Length) { - Array.Copy(array2, offset, array1, destinationOffset, length); + Array.Copy(@from, offset, to, destinationOffset, length); } else { - char[] newArr = ResizeArray(array1, destinationOffset + length + 80); - Array.Copy(array2, offset, newArr, destinationOffset, length); - return newArr; + char[] newArr = ResizeOrPoolNewArray(to, destinationOffset + length + 80); + Array.Copy(@from, offset, newArr, destinationOffset, length); + to = newArr; } - return array1; + return to; } if (_charPos == _charLen && (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) == 0) @@ -899,8 +899,8 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o return null; } - char[]? polledArray = null; - int lastI = 0; + char[]? rentedArray = null; + int lastWrittenIndex = 0; try { do @@ -913,11 +913,11 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o char ch = _charBuffer[_charPos + i]; string? s; - if (polledArray != null) + if (rentedArray != null) { - polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i); - lastI += i; - s = new string(polledArray, 0, lastI); + rentedArray = Append(rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); + lastWrittenIndex += i; + s = new string(rentedArray, 0, lastWrittenIndex); } else { @@ -937,16 +937,16 @@ static char[] Append(char[]? array1, int destinationOffset, char[] array2, int o i = _charLen - _charPos; - polledArray = Append(polledArray, lastI, _charBuffer, _charPos, i); - lastI += i; + rentedArray = Append(rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); + lastWrittenIndex += i; } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); - return new string(polledArray, 0, lastI); + return new string(rentedArray, 0, lastWrittenIndex); } finally { - if (polledArray != null) + if (rentedArray != null) { - ArrayPool.Shared.Return(polledArray); + ArrayPool.Shared.Return(rentedArray); } } } From 7d338a1915de6d8639f673eb34077ca810cf98c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Fri, 10 Dec 2021 23:52:03 +0500 Subject: [PATCH 10/21] ReadToEndAsyncInternal to ArrayPools. --- .../src/System/IO/StreamReader.cs | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 6b5aae3e6b33db..36d5cd8ed43c68 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -817,7 +817,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { char ch = _charBuffer[_charPos + i]; - string? s; + string s; if (sb is { Length: > 0 }) { sb.Append(_charBuffer.AsSpan(_charPos, i)); @@ -879,7 +879,7 @@ static char[] ResizeOrPoolNewArray(char[]? array, int atLeastSpace) ArrayPool.Shared.Return(array); return newArr; } - static char[] Append(char[]? to, int destinationOffset, char[] @from, int offset, int length) + static void Append(ref char[]? to, int destinationOffset, char[] @from, int offset, int length) { if (to != null && (destinationOffset + length) < to.Length) { @@ -891,7 +891,6 @@ static char[] Append(char[]? to, int destinationOffset, char[] @from, int offset Array.Copy(@from, offset, newArr, destinationOffset, length); to = newArr; } - return to; } if (_charPos == _charLen && (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) == 0) @@ -915,9 +914,9 @@ static char[] Append(char[]? to, int destinationOffset, char[] @from, int offset string? s; if (rentedArray != null) { - rentedArray = Append(rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); + Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); lastWrittenIndex += i; - s = new string(rentedArray, 0, lastWrittenIndex); + s = new string(rentedArray!, 0, lastWrittenIndex); } else { @@ -937,10 +936,10 @@ static char[] Append(char[]? to, int destinationOffset, char[] @from, int offset i = _charLen - _charPos; - rentedArray = Append(rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); + Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); lastWrittenIndex += i; } while (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false) > 0); - return new string(rentedArray, 0, lastWrittenIndex); + return new string(rentedArray!, 0, lastWrittenIndex); } finally { @@ -973,18 +972,46 @@ public override Task ReadToEndAsync() private async Task ReadToEndAsyncInternal() { - // Call ReadBuffer, then pull data out of charBuffer. + static char[] Resize(char[] array, int atLeastSpace) + { + char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); + Array.Copy(array, newArr, array.Length); + ArrayPool.Shared.Return(array); + return newArr; + } + static void Append(ref char[] to, int destinationOffset, char[] @from, int offset, int length) + { + if ((destinationOffset + length) < to.Length) + { + Array.Copy(@from, offset, to, destinationOffset, length); + } + else + { + char[] newArr = Resize(to, destinationOffset + length + 80); + Array.Copy(@from, offset, newArr, destinationOffset, length); + to = newArr; + } + } - StringBuilder sb = new(_charLen - _charPos); - do + char[] rentedArray = ArrayPool.Shared.Rent(_charLen - _charPos); + int lastWrittenIndex = 0; + try { - int tmpCharPos = _charPos; - sb.Append(_charBuffer.AsSpan(tmpCharPos, _charLen - tmpCharPos)); - _charPos = _charLen; // We consumed these characters - await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false); - } while (_charLen > 0); + do + { + // int tmpCharPos = _charPos; + Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, _charLen - _charPos); + lastWrittenIndex += _charLen - _charPos; + _charPos = _charLen; // We consumed these characters + await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false); + } while (_charLen > 0); - return sb.ToString(); + return new string(rentedArray, 0, lastWrittenIndex); + } + finally + { + ArrayPool.Shared.Return(rentedArray); + } } public override Task ReadAsync(char[] buffer, int index, int count) From b8a91ff9e9d873c5ab1e6b825a6d3eaad0c9731d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Sat, 11 Dec 2021 06:10:11 +0500 Subject: [PATCH 11/21] PR comments fixes and a hack for CI --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 36d5cd8ed43c68..fe559c57f99353 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -807,7 +807,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } } - using ValueStringBuilder sb = new(0); + using ValueStringBuilder sb = new(stackalloc char[256]); do { // Note the following common line feed chars: @@ -815,10 +815,8 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); if (i >= 0) { - char ch = _charBuffer[_charPos + i]; - string s; - if (sb is { Length: > 0 }) + if (sb.Length > 0) { sb.Append(_charBuffer.AsSpan(_charPos, i)); s = sb.ToString(); @@ -827,6 +825,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { s = new string(_charBuffer, _charPos, i); } + char ch = _charBuffer[_charPos + i]; _charPos += i + 1; if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0)) { @@ -839,6 +838,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } i = _charLen - _charPos; + if (i < 0) break; // a hack for System.Globalization.Tests (to check on CI if it fixes the problem) sb.Append(_charBuffer.AsSpan(_charPos, i)); } while (ReadBuffer() > 0); @@ -935,6 +935,7 @@ static void Append(ref char[]? to, int destinationOffset, char[] @from, int offs } i = _charLen - _charPos; + if (i < 0) break; // a hack for System.Globalization.Tests (to check on CI if it fixes the problem) Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); lastWrittenIndex += i; From d490b8570162f3c7b88fb6966bd22983e88d4b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Sat, 11 Dec 2021 13:31:28 +0500 Subject: [PATCH 12/21] Handle exceptions. --- .../src/System/IO/StreamReader.cs | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index fe559c57f99353..8543358441bf3f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -869,28 +869,30 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { static char[] ResizeOrPoolNewArray(char[]? array, int atLeastSpace) { - if (array == null) + if (array == null) return ArrayPool.Shared.Rent(atLeastSpace); + + char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); + try + { + Array.Copy(array, newArr, array.Length); + } + catch { - return ArrayPool.Shared.Rent(atLeastSpace); + ArrayPool.Shared.Return(newArr); + throw; } - char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); - Array.Copy(array, newArr, array.Length); ArrayPool.Shared.Return(array); return newArr; } static void Append(ref char[]? to, int destinationOffset, char[] @from, int offset, int length) { - if (to != null && (destinationOffset + length) < to.Length) + if (to == null || (destinationOffset + length) > to.Length) { - Array.Copy(@from, offset, to, destinationOffset, length); - } - else - { - char[] newArr = ResizeOrPoolNewArray(to, destinationOffset + length + 80); - Array.Copy(@from, offset, newArr, destinationOffset, length); - to = newArr; + to = ResizeOrPoolNewArray(to, destinationOffset + length + 80); } + + Array.Copy(@from, offset, to, destinationOffset, length); } if (_charPos == _charLen && (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) == 0) @@ -976,22 +978,26 @@ private async Task ReadToEndAsyncInternal() static char[] Resize(char[] array, int atLeastSpace) { char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); - Array.Copy(array, newArr, array.Length); + try + { + Array.Copy(array, newArr, array.Length); + } + catch + { + ArrayPool.Shared.Return(newArr); + throw; + } ArrayPool.Shared.Return(array); return newArr; } static void Append(ref char[] to, int destinationOffset, char[] @from, int offset, int length) { - if ((destinationOffset + length) < to.Length) + if ((destinationOffset + length) >= to.Length) { - Array.Copy(@from, offset, to, destinationOffset, length); - } - else - { - char[] newArr = Resize(to, destinationOffset + length + 80); - Array.Copy(@from, offset, newArr, destinationOffset, length); - to = newArr; + to = Resize(to, destinationOffset + length + 80); } + + Array.Copy(@from, offset, to, destinationOffset, length); } char[] rentedArray = ArrayPool.Shared.Rent(_charLen - _charPos); From 576e9536338728cbc5c5280528dc20a4f1d06607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Sat, 11 Dec 2021 21:03:55 +0500 Subject: [PATCH 13/21] AsSpan with specified length --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 8543358441bf3f..b94eb3b09c8085 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -812,7 +812,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) { // Note the following common line feed chars: // \n - UNIX \r\n - DOS \r - Mac - int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); + int i = _charBuffer.AsSpan(_charPos, _charLen - _charPos).IndexOfAny('\r', '\n'); if (i >= 0) { string s; @@ -838,7 +838,6 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) } i = _charLen - _charPos; - if (i < 0) break; // a hack for System.Globalization.Tests (to check on CI if it fixes the problem) sb.Append(_charBuffer.AsSpan(_charPos, i)); } while (ReadBuffer() > 0); @@ -908,11 +907,9 @@ static void Append(ref char[]? to, int destinationOffset, char[] @from, int offs { // Note the following common line feed chars: // \n - UNIX \r\n - DOS \r - Mac - int i = _charBuffer.AsSpan(_charPos).IndexOfAny('\r', '\n'); + int i = _charBuffer.AsSpan(_charPos, _charLen - _charPos).IndexOfAny('\r', '\n'); if (i >= 0) { - char ch = _charBuffer[_charPos + i]; - string? s; if (rentedArray != null) { @@ -924,6 +921,7 @@ static void Append(ref char[]? to, int destinationOffset, char[] @from, int offs { s = new string(_charBuffer, _charPos, i); } + char ch = _charBuffer[_charPos + i]; _charPos += i + 1; if (ch == '\r' && (_charPos < _charLen || (await ReadBufferAsync(CancellationToken.None).ConfigureAwait(false)) > 0)) { From ba913a97f24608f60a3fbef07340a8f941415395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Sat, 11 Dec 2021 21:05:59 +0500 Subject: [PATCH 14/21] Remove commented out StringBuilder --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index b94eb3b09c8085..c4df13e703a88e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -18,7 +18,6 @@ public class StreamReader : TextReader { // StreamReader.Null is threadsafe. public static new readonly StreamReader Null = new NullStreamReader(); - // private StringBuilder? _sb; // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well // perf-wise. On even a 40 MB text file, any perf loss by using a 4K From 7526cad16726764f452185ca5cb5ad49e6410d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Wed, 11 May 2022 15:29:46 +0500 Subject: [PATCH 15/21] Remove try catch block --- .../src/System/IO/StreamReader.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index fdfd990443a615..08165cd4e23910 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -1016,15 +1016,7 @@ private async Task ReadToEndAsyncInternal(CancellationToken cancellation static char[] Resize(char[] array, int atLeastSpace) { char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); - try - { - Array.Copy(array, newArr, array.Length); - } - catch - { - ArrayPool.Shared.Return(newArr); - throw; - } + Array.Copy(array, newArr, array.Length); ArrayPool.Shared.Return(array); return newArr; } From ec33696364a1916d4af810730da366e0e50f275c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Wed, 11 May 2022 17:51:48 +0500 Subject: [PATCH 16/21] Fixed issues --- .../tests/Normalization/NormalizationAll.cs | 26 ------------------- ...NumberFormatInfoCurrencyNegativePattern.cs | 8 ------ .../src/System/IO/StreamReader.cs | 6 ++--- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs index 4b3c3a5dc5b163..c32d7e36f1df61 100644 --- a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs +++ b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs @@ -12,13 +12,6 @@ namespace System.Globalization.Tests { public class StringNormalizationAllTests { - private readonly ITestOutputHelper _testOutputHelper; - - public StringNormalizationAllTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - private string ConvertToString(string codepoints) { StringBuilder sb = new StringBuilder(); @@ -30,21 +23,6 @@ private string ConvertToString(string codepoints) return sb.ToString(); } - // d750-334-11b5, - // d750-334-11b5, - // 1112-1173-334-11b5, - // d750-334-11b5, - // 1112-1173-334-11b51a0-334-11b5, - // 1110-1169-334-11b5, - // d1a0-334-11b5, - // 1110-1169-334-11b5 - - // d750-334-11b5, - // d750-334-11b5, - // 1112-1173-334-11b5, - // d750-334-11b5, - // 1112-1173-334-11b5 - [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34577", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void Normalize() @@ -65,15 +43,11 @@ public void Normalize() var str = str2.ReadToEnd(); var ar = str.Split('\n'); - _testOutputHelper.WriteLine($" Length {ar.Length}"); - _testOutputHelper.WriteLine(string.Join("\n", ar.Take(150))); int index = 0; while (!sr.EndOfStream) { string line = sr.ReadLine(); - - string[] parts = line.Split(','); Assert.True(parts.Length == 5, $"{line}\n Wrong data at the line {index}. Parts length {parts.Length}"); diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs index 66b119b365180f..dc56dad09e51f6 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs @@ -10,13 +10,6 @@ namespace System.Globalization.Tests { public class NumberFormatInfoCurrencyNegativePattern { - private readonly ITestOutputHelper _testOutputHelper; - - public NumberFormatInfoCurrencyNegativePattern(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - public static IEnumerable CurrencyNegativePattern_TestData() { yield return new object[] { NumberFormatInfo.InvariantInfo, new int[] { 0 } }; @@ -61,7 +54,6 @@ public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale) } NumberFormatInfo format = culture.NumberFormat; - _testOutputHelper.WriteLine(locale); Assert.Contains(format.CurrencyNegativePattern, NumberFormatInfoData.GetCurrencyNegativePatterns(locale)); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 08165cd4e23910..d07f93f671f69d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -796,7 +796,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) do { // Note the following common line feed chars: - // \n - UNIX \r\n - DOS \r - Mac + // \n - UNIX/Mac \r\n - Windows int i = _charBuffer.AsSpan(_charPos, _charLen - _charPos).IndexOfAny('\r', '\n'); if (i >= 0) { @@ -881,6 +881,7 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) private async Task ReadLineAsyncInternal(CancellationToken cancellationToken) { + static char[] ResizeOrPoolNewArray(char[]? array, int atLeastSpace) { if (array == null) return ArrayPool.Shared.Rent(atLeastSpace); @@ -921,7 +922,7 @@ static void Append(ref char[]? to, int destinationOffset, char[] @from, int offs do { // Note the following common line feed chars: - // \n - UNIX \r\n - DOS \r - Mac + // \n - UNIX/Mac \r\n - Windows int i = _charBuffer.AsSpan(_charPos, _charLen - _charPos).IndexOfAny('\r', '\n'); if (i >= 0) { @@ -1036,7 +1037,6 @@ static void Append(ref char[] to, int destinationOffset, char[] @from, int offse { do { - // int tmpCharPos = _charPos; Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, _charLen - _charPos); lastWrittenIndex += _charLen - _charPos; _charPos = _charLen; // We consumed these characters From 8060dc2535b15a4fdd5f14b6d3120e2862505145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Wed, 11 May 2022 18:12:20 +0500 Subject: [PATCH 17/21] Fix merging issues --- .../tests/Normalization/NormalizationAll.cs | 11 ------- ...NumberFormatInfoCurrencyNegativePattern.cs | 1 - .../tests/StreamReader/StreamReaderTests.cs | 31 ------------------- 3 files changed, 43 deletions(-) diff --git a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs index c32d7e36f1df61..2d3fca8e35b8aa 100644 --- a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs +++ b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using Xunit; -using Xunit.Abstractions; namespace System.Globalization.Tests { @@ -34,16 +33,6 @@ public void Normalize() { using (StreamReader sr = new StreamReader(stream)) { - using var str1 = typeof(StringNormalizationAllTests).GetTypeInfo().Assembly - .GetManifestResourceStream(PlatformDetection.IsWindows7 - ? "NormalizationDataWin7" - : "NormalizationDataWin8"); - - using var str2 = new StreamReader(str1); - - var str = str2.ReadToEnd(); - var ar = str.Split('\n'); - int index = 0; while (!sr.EndOfStream) { diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs index dc56dad09e51f6..c001a7446525df 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyNegativePattern.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using Xunit; -using Xunit.Abstractions; namespace System.Globalization.Tests { diff --git a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs index 7dbcb221768eea..393fe055229c64 100644 --- a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs +++ b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs @@ -132,36 +132,6 @@ public async Task ReadToEndAsync_WithCanceledCancellationToken() Assert.Equal(token, ex.CancellationToken); } - [OuterLoop("It creates 1GB file")] - [Fact] - [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser.")] - public async Task ReadToEndAsync_WithCancellation() - { - string path = GetTestFilePath(); - CreateLargeFile(path); - - using StreamReader reader = File.OpenText(path); - using CancellationTokenSource cts = new(); - var token = cts.Token; - - var ex = await Assert.ThrowsAnyAsync(async () => - { - Task readToEndTask = reader.ReadToEndAsync(token); - - // This is a time-sensitive test where the cancellation needs to happen before the async read completes. - // A sleep may be too long a delay, so spin-wait for a very short duration before canceling. - SpinWait spinner = default; - while (!spinner.NextSpinWillYield) - { - spinner.SpinOnce(sleep1Threshold: -1); - } - - cts.Cancel(); - await readToEndTask; - }); - Assert.Equal(token, ex.CancellationToken); - } - private void CreateLargeFile(string path) { const string sentence = "A very large file used for testing StreamReader cancellation. 0123456789012345678901234567890123456789."; @@ -605,7 +575,6 @@ public async Task ReadBlockAsync_RepeatsReadsUntilReadDesiredAmount() } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34583", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData(0, false)] [InlineData(0, true)] [InlineData(1, false)] From 554ca18d4d7dd3601cee125b2f9decf92c9010ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Wed, 11 May 2022 18:14:36 +0500 Subject: [PATCH 18/21] Remove merging issues x2 --- .../tests/Normalization/NormalizationAll.cs | 3 +-- .../tests/StreamReader/StreamReaderTests.cs | 25 ------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs index 2d3fca8e35b8aa..48f73a4527a389 100644 --- a/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs +++ b/src/libraries/System.Globalization.Extensions/tests/Normalization/NormalizationAll.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Text; using System.IO; -using System.Linq; using Xunit; namespace System.Globalization.Tests @@ -38,7 +37,7 @@ public void Normalize() { string line = sr.ReadLine(); string[] parts = line.Split(','); - Assert.True(parts.Length == 5, $"{line}\n Wrong data at the line {index}. Parts length {parts.Length}"); + Assert.True(parts.Length == 5, $"Wrong data at the line {index}"); string part0 = ConvertToString(parts[0]); string part1 = ConvertToString(parts[1]); diff --git a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs index 393fe055229c64..810cbe5380f6cf 100644 --- a/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs +++ b/src/libraries/System.IO/tests/StreamReader/StreamReaderTests.cs @@ -132,31 +132,6 @@ public async Task ReadToEndAsync_WithCanceledCancellationToken() Assert.Equal(token, ex.CancellationToken); } - private void CreateLargeFile(string path) - { - const string sentence = "A very large file used for testing StreamReader cancellation. 0123456789012345678901234567890123456789."; - const int repeatCount = 10_000_000; - Encoding encoding = Encoding.UTF8; - - using FileStream fs = File.OpenWrite(path); - long fileSize = encoding.GetByteCount(sentence) * repeatCount; - - try - { - fs.SetLength(fileSize); - } - catch (IOException) - { - throw new SkipTestException($"Unable to run {ReadToEndAsync_WithCancellation} due to lack of available disk space"); - } - - using StreamWriter streamWriter = new StreamWriter(fs, encoding); - for (int i = 0; i < repeatCount; i++) - { - streamWriter.WriteLine(sentence); - } - } - [Fact] public void GetBaseStream() { From c6d3eeac91c3587aee026901f71edff9835091e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=90=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BD?= Date: Wed, 11 May 2022 18:24:37 +0500 Subject: [PATCH 19/21] Changes after review --- .../src/System/IO/StreamReader.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index d07f93f671f69d..8fa119e291176f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -881,22 +881,13 @@ private int ReadBuffer(Span userBuffer, out bool readToUserBuffer) private async Task ReadLineAsyncInternal(CancellationToken cancellationToken) { - static char[] ResizeOrPoolNewArray(char[]? array, int atLeastSpace) { - if (array == null) return ArrayPool.Shared.Rent(atLeastSpace); + if (array == null) + return ArrayPool.Shared.Rent(atLeastSpace); char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); - try - { - Array.Copy(array, newArr, array.Length); - } - catch - { - ArrayPool.Shared.Return(newArr); - throw; - } - + Array.Copy(array, newArr, array.Length); ArrayPool.Shared.Return(array); return newArr; } From 6325e8e623ffc2a5b7606482facce1710ffd531a Mon Sep 17 00:00:00 2001 From: "d.afonin" Date: Wed, 11 May 2022 23:15:53 +0500 Subject: [PATCH 20/21] ReadToEnd revert --- .../src/System/IO/StreamReader.cs | 46 +++++-------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 8fa119e291176f..f3dc6d044894fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -417,10 +417,10 @@ public override string ReadToEnd() CheckAsyncTaskInProgress(); // Call ReadBuffer, then pull data out of charBuffer. - using ValueStringBuilder sb = new(_charLen - _charPos); + StringBuilder sb = new StringBuilder(_charLen - _charPos); do { - sb.Append(_charBuffer.AsSpan(_charPos, _charLen - _charPos)); + sb.Append(_charBuffer, _charPos, _charLen - _charPos); _charPos = _charLen; // Note we consumed these characters ReadBuffer(); } while (_charLen > 0); @@ -1005,41 +1005,17 @@ public override Task ReadToEndAsync(CancellationToken cancellationToken) private async Task ReadToEndAsyncInternal(CancellationToken cancellationToken) { - static char[] Resize(char[] array, int atLeastSpace) - { - char[] newArr = ArrayPool.Shared.Rent(array.Length + atLeastSpace); - Array.Copy(array, newArr, array.Length); - ArrayPool.Shared.Return(array); - return newArr; - } - static void Append(ref char[] to, int destinationOffset, char[] @from, int offset, int length) - { - if ((destinationOffset + length) >= to.Length) - { - to = Resize(to, destinationOffset + length + 80); - } - - Array.Copy(@from, offset, to, destinationOffset, length); - } - - char[] rentedArray = ArrayPool.Shared.Rent(_charLen - _charPos); - int lastWrittenIndex = 0; - try + // Call ReadBuffer, then pull data out of charBuffer. + StringBuilder sb = new StringBuilder(_charLen - _charPos); + do { - do - { - Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, _charLen - _charPos); - lastWrittenIndex += _charLen - _charPos; - _charPos = _charLen; // We consumed these characters - await ReadBufferAsync(cancellationToken).ConfigureAwait(false); - } while (_charLen > 0); + int tmpCharPos = _charPos; + sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos); + _charPos = _charLen; // We consumed these characters + await ReadBufferAsync(cancellationToken).ConfigureAwait(false); + } while (_charLen > 0); - return new string(rentedArray, 0, lastWrittenIndex); - } - finally - { - ArrayPool.Shared.Return(rentedArray); - } + return sb.ToString(); } public override Task ReadAsync(char[] buffer, int index, int count) From 9922cf86d6018ae87b259952e5d69168fac075b4 Mon Sep 17 00:00:00 2001 From: "d.afonin" Date: Thu, 12 May 2022 00:42:53 +0500 Subject: [PATCH 21/21] Fix-up --- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index f3dc6d044894fa..86bf08e5263c50 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -942,7 +942,6 @@ static void Append(ref char[]? to, int destinationOffset, char[] @from, int offs } i = _charLen - _charPos; - if (i < 0) break; // a hack for System.Globalization.Tests (to check on CI if it fixes the problem) Append(ref rentedArray, lastWrittenIndex, _charBuffer, _charPos, i); lastWrittenIndex += i;