Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 80 additions & 84 deletions src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -780,53 +780,23 @@ private int ReadBuffer(Span<char> userBuffer, out bool readToUserBuffer)
ThrowIfDisposed();
CheckAsyncTaskInProgress();

if (_charPos == _charLen)
{
if (ReadBuffer() == 0)
{
return null;
}
}

StringBuilder? sb = null;
do
bool spilledLineFeed = false;
while (true)
{
int i = _charPos;
do
if (_charPos == _charLen)
{
char ch = _charBuffer[i];
// Note the following common line feed chars:
// \n - UNIX \r\n - DOS \r - Mac
if (ch == '\r' || ch == '\n')
if (ReadBuffer() == 0)
{
string s;
if (sb != null)
{
sb.Append(_charBuffer, _charPos, i - _charPos);
s = sb.ToString();
}
else
{
s = new string(_charBuffer, _charPos, i - _charPos);
}
_charPos = i + 1;
if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0))
{
if (_charBuffer[_charPos] == '\n')
{
_charPos++;
}
}
return s;
return sb?.ToString();
}
i++;
} while (i < _charLen);
}

i = _charLen - _charPos;
sb ??= new StringBuilder(i + 80);
sb.Append(_charBuffer, _charPos, i);
} while (ReadBuffer() > 0);
return sb.ToString();
if (TryReadLine(ref sb, ref spilledLineFeed, out string? result))
{
return result;
}
}
}

public override Task<string?> ReadLineAsync() =>
Expand Down Expand Up @@ -881,63 +851,89 @@ private int ReadBuffer(Span<char> userBuffer, out bool readToUserBuffer)

private async Task<string?> ReadLineAsyncInternal(CancellationToken cancellationToken)
{
if (_charPos == _charLen && (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0)
{
return null;
}

StringBuilder? sb = null;

do
bool spilledLineFeed = false;
while (true)
{
char[] tmpCharBuffer = _charBuffer;
int tmpCharLen = _charLen;
int tmpCharPos = _charPos;
int i = tmpCharPos;
if (_charPos == _charLen)
{
if ((await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) == 0)
{
return sb?.ToString();
}
}

do
if (TryReadLine(ref sb, ref spilledLineFeed, out string? result))
{
char ch = tmpCharBuffer[i];
return result;
}
}
}

// Note the following common line feed chars:
// \n - UNIX \r\n - DOS \r - Mac
if (ch == '\r' || ch == '\n')
{
string s;
private bool TryReadLine(ref StringBuilder? sb, ref bool spilledLineFeed, [NotNullWhen(true)] out string? result)
{
// Special case if the buffer runs out of space between \r and (potentially) \n
if (spilledLineFeed)
{
if (_charBuffer[_charPos] == '\n')
{
_charPos++;
}

if (sb != null)
{
sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
s = sb.ToString();
}
else
{
s = new string(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
}
// sb cannot be null here
result = sb!.ToString();
return true;
}

_charPos = tmpCharPos = i + 1;
ReadOnlySpan<char> segment = _charBuffer.AsSpan(_charPos, _charLen - _charPos);

if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync(cancellationToken).ConfigureAwait(false)) > 0))
int newLineIndex = segment.IndexOfAny('\r', '\n');
if ((uint)newLineIndex < (uint)segment.Length)
{
_charPos += newLineIndex + 1;
if (segment[newLineIndex] == '\r')
{
int nextCharIndex = newLineIndex + 1;
if ((uint)nextCharIndex < (uint)segment.Length)
{
if (segment[nextCharIndex] == '\n')
{
tmpCharPos = _charPos;
if (_charBuffer[tmpCharPos] == '\n')
{
_charPos = ++tmpCharPos;
}
_charPos++;
}

return s;
}
else
{
Debug.Assert(_charPos == _charLen);
// At the end of buffer, but there might be another line feed.
// Stash the current string and consume the line feed next iteration
sb ??= new StringBuilder(newLineIndex);
sb.Append(segment.Slice(0, newLineIndex));
spilledLineFeed = true;

result = null;
return false;
}
}

i++;
} while (i < tmpCharLen);
segment = segment.Slice(0, newLineIndex);
if (sb != null)
{
sb.Append(segment);
result = sb.ToString();
}
else
{
result = new string(segment);
}
return true;
}

i = tmpCharLen - tmpCharPos;
sb ??= new StringBuilder(i + 80);
sb.Append(tmpCharBuffer, tmpCharPos, i);
} while (await ReadBufferAsync(cancellationToken).ConfigureAwait(false) > 0);
sb ??= new StringBuilder(segment.Length + 80);
sb.Append(segment);
_charPos = _charLen;

return sb.ToString();
result = null;
return false;
}

public override Task<string> ReadToEndAsync() => ReadToEndAsync(default);
Expand Down