diff --git a/src/libraries/System.Console/src/Resources/Strings.resx b/src/libraries/System.Console/src/Resources/Strings.resx
index 6f16c49d115b60..9e0151c9df0feb 100644
--- a/src/libraries/System.Console/src/Resources/Strings.resx
+++ b/src/libraries/System.Console/src/Resources/Strings.resx
@@ -182,9 +182,6 @@
The process cannot access the file because it is being used by another process.
-
- Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
-
The ConsoleColor enum value was not defined on that enum. Please use a defined color from the enum.
diff --git a/src/libraries/System.Console/src/System/ConsolePal.Android.cs b/src/libraries/System.Console/src/System/ConsolePal.Android.cs
index 3586f64aff0765..910a33ad824949 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.Android.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.Android.cs
@@ -11,11 +11,11 @@ internal sealed unsafe class LogcatStream : ConsoleStream
{
public LogcatStream() : base(FileAccess.Write) {}
- public override int Read(byte[] buffer, int offset, int count) => throw Error.GetReadNotSupported();
+ public override int Read(Span buffer) => throw Error.GetReadNotSupported();
- public override unsafe void Write(byte[] buffer, int offset, int count)
+ public override unsafe void Write(ReadOnlySpan buffer)
{
- string log = ConsolePal.OutputEncoding.GetString(buffer, offset, count);
+ string log = ConsolePal.OutputEncoding.GetString(buffer);
Interop.Logcat.AndroidLogPrint(Interop.Logcat.LogLevel.Info, "DOTNET", log);
}
}
diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
index dd20b7040e9b85..9dec2e221a86bc 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
@@ -1212,15 +1212,13 @@ private void AddKey(TermInfo.Database db, string extendedName, ConsoleKey key, b
/// Reads data from the file descriptor into the buffer.
/// The file descriptor.
/// The buffer to read into.
- /// The offset at which to start writing into the buffer.
- /// The maximum number of bytes to read.
/// The number of bytes read, or a negative value if there's an error.
- internal static unsafe int Read(SafeFileHandle fd, byte[] buffer, int offset, int count)
+ internal static unsafe int Read(SafeFileHandle fd, Span buffer)
{
fixed (byte* bufPtr = buffer)
{
- int result = Interop.CheckIo(Interop.Sys.Read(fd, (byte*)bufPtr + offset, count));
- Debug.Assert(result <= count);
+ int result = Interop.CheckIo(Interop.Sys.Read(fd, bufPtr, buffer.Length));
+ Debug.Assert(result <= buffer.Length);
return result;
}
}
@@ -1424,26 +1422,13 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- public override int Read(byte[] buffer, int offset, int count)
- {
- ValidateRead(buffer, offset, count);
-
- if (_useReadLine)
- {
- return ConsolePal.StdInReader.ReadLine(buffer, offset, count);
- }
- else
- {
- return ConsolePal.Read(_handle, buffer, offset, count);
- }
- }
+ public override int Read(Span buffer) =>
+ _useReadLine ?
+ ConsolePal.StdInReader.ReadLine(buffer) :
+ ConsolePal.Read(_handle, buffer);
- public override void Write(byte[] buffer, int offset, int count)
- {
- ValidateWrite(buffer, offset, count);
-
- ConsolePal.Write(_handle, buffer.AsSpan(offset, count));
- }
+ public override void Write(ReadOnlySpan buffer) =>
+ ConsolePal.Write(_handle, buffer);
public override void Flush()
{
diff --git a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs
index cbce7776d5397d..405326185ebb7f 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.WebAssembly.cs
@@ -29,15 +29,13 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- public override int Read(byte[] buffer, int offset, int count) => throw Error.GetReadNotSupported();
+ public override int Read(Span buffer) => throw Error.GetReadNotSupported();
- public override unsafe void Write(byte[] buffer, int offset, int count)
+ public override unsafe void Write(ReadOnlySpan buffer)
{
- ValidateWrite(buffer, offset, count);
-
fixed (byte* bufPtr = buffer)
{
- Write(_handle, bufPtr + offset, count);
+ Write(_handle, bufPtr, buffer.Length);
}
}
diff --git a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs
index 76edfe8512e858..78758905ea6a54 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.Windows.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.Windows.cs
@@ -1118,24 +1118,24 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- public override int Read(byte[] buffer, int offset, int count)
+ public override int Read(Span buffer)
{
- ValidateRead(buffer, offset, count);
-
- int bytesRead;
- int errCode = ReadFileNative(_handle, buffer, offset, count, _isPipe, out bytesRead, _useFileAPIs);
+ int errCode = ReadFileNative(_handle, buffer, _isPipe, out int bytesRead, _useFileAPIs);
if (Interop.Errors.ERROR_SUCCESS != errCode)
+ {
throw Win32Marshal.GetExceptionForWin32Error(errCode);
+ }
+
return bytesRead;
}
- public override void Write(byte[] buffer, int offset, int count)
+ public override void Write(ReadOnlySpan buffer)
{
- ValidateWrite(buffer, offset, count);
-
- int errCode = WriteFileNative(_handle, buffer, offset, count, _useFileAPIs);
+ int errCode = WriteFileNative(_handle, buffer, _useFileAPIs);
if (Interop.Errors.ERROR_SUCCESS != errCode)
+ {
throw Win32Marshal.GetExceptionForWin32Error(errCode);
+ }
}
public override void Flush()
@@ -1149,35 +1149,26 @@ public override void Flush()
// world working set and to avoid requiring a reference to the
// System.IO.FileSystem contract.
- private static unsafe int ReadFileNative(IntPtr hFile, byte[] bytes, int offset, int count, bool isPipe, out int bytesRead, bool useFileAPIs)
+ private static unsafe int ReadFileNative(IntPtr hFile, Span buffer, bool isPipe, out int bytesRead, bool useFileAPIs)
{
- Debug.Assert(offset >= 0, "offset >= 0");
- Debug.Assert(count >= 0, "count >= 0");
- Debug.Assert(bytes != null, "bytes != null");
- // Don't corrupt memory when multiple threads are erroneously writing
- // to this stream simultaneously.
- if (bytes.Length - offset < count)
- throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition);
-
- // You can't use the fixed statement on an array of length 0.
- if (bytes.Length == 0)
+ if (buffer.IsEmpty)
{
bytesRead = 0;
return Interop.Errors.ERROR_SUCCESS;
}
bool readSuccess;
- fixed (byte* p = &bytes[0])
+ fixed (byte* p = buffer)
{
if (useFileAPIs)
{
- readSuccess = (0 != Interop.Kernel32.ReadFile(hFile, p + offset, count, out bytesRead, IntPtr.Zero));
+ readSuccess = (0 != Interop.Kernel32.ReadFile(hFile, p, buffer.Length, out bytesRead, IntPtr.Zero));
}
else
{
// If the code page could be Unicode, we should use ReadConsole instead, e.g.
int charsRead;
- readSuccess = Interop.Kernel32.ReadConsole(hFile, p + offset, count / BytesPerWChar, out charsRead, IntPtr.Zero);
+ readSuccess = Interop.Kernel32.ReadConsole(hFile, p, buffer.Length / BytesPerWChar, out charsRead, IntPtr.Zero);
bytesRead = charsRead * BytesPerWChar;
}
}
@@ -1193,24 +1184,19 @@ private static unsafe int ReadFileNative(IntPtr hFile, byte[] bytes, int offset,
return errorCode;
}
- private static unsafe int WriteFileNative(IntPtr hFile, byte[] bytes, int offset, int count, bool useFileAPIs)
+ private static unsafe int WriteFileNative(IntPtr hFile, ReadOnlySpan bytes, bool useFileAPIs)
{
- Debug.Assert(offset >= 0, "offset >= 0");
- Debug.Assert(count >= 0, "count >= 0");
- Debug.Assert(bytes != null, "bytes != null");
- Debug.Assert(bytes.Length >= offset + count, "bytes.Length >= offset + count");
-
// You can't use the fixed statement on an array of length 0.
- if (bytes.Length == 0)
+ if (bytes.IsEmpty)
return Interop.Errors.ERROR_SUCCESS;
bool writeSuccess;
- fixed (byte* p = &bytes[0])
+ fixed (byte* p = bytes)
{
if (useFileAPIs)
{
int numBytesWritten;
- writeSuccess = (0 != Interop.Kernel32.WriteFile(hFile, p + offset, count, out numBytesWritten, IntPtr.Zero));
+ writeSuccess = (0 != Interop.Kernel32.WriteFile(hFile, p, bytes.Length, out numBytesWritten, IntPtr.Zero));
// In some cases we have seen numBytesWritten returned that is twice count;
// so we aren't asserting the value of it. See https://github.com/dotnet/runtime/issues/23776
}
@@ -1223,8 +1209,8 @@ private static unsafe int WriteFileNative(IntPtr hFile, byte[] bytes, int offset
// However, we do not need to worry about that because the StreamWriter in Console has
// a much shorter buffer size anyway.
int charsWritten;
- writeSuccess = Interop.Kernel32.WriteConsole(hFile, p + offset, count / BytesPerWChar, out charsWritten, IntPtr.Zero);
- Debug.Assert(!writeSuccess || count / BytesPerWChar == charsWritten);
+ writeSuccess = Interop.Kernel32.WriteConsole(hFile, p, bytes.Length / BytesPerWChar, out charsWritten, IntPtr.Zero);
+ Debug.Assert(!writeSuccess || bytes.Length / BytesPerWChar == charsWritten);
}
}
if (writeSuccess)
diff --git a/src/libraries/System.Console/src/System/ConsolePal.iOS.cs b/src/libraries/System.Console/src/System/ConsolePal.iOS.cs
index 089ebf24fcf9d5..fa1f2d120a8c88 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.iOS.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.iOS.cs
@@ -10,15 +10,13 @@ internal sealed class NSLogStream : ConsoleStream
{
public NSLogStream() : base(FileAccess.Write) {}
- public override int Read(byte[] buffer, int offset, int count) => throw Error.GetReadNotSupported();
+ public override int Read(Span buffer) => throw Error.GetReadNotSupported();
- public override unsafe void Write(byte[] buffer, int offset, int count)
+ public override unsafe void Write(ReadOnlySpan buffer)
{
- ValidateWrite(buffer, offset, count);
-
fixed (byte* ptr = buffer)
{
- Interop.Sys.Log(ptr + offset, count);
+ Interop.Sys.Log(ptr, buffer.Length);
}
}
}
diff --git a/src/libraries/System.Console/src/System/IO/ConsoleStream.cs b/src/libraries/System.Console/src/System/IO/ConsoleStream.cs
index 9fdc87da2f1d4a..935f565c7ed1d8 100644
--- a/src/libraries/System.Console/src/System/IO/ConsoleStream.cs
+++ b/src/libraries/System.Console/src/System/IO/ConsoleStream.cs
@@ -3,7 +3,6 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
-using System.Text;
namespace System.IO
{
@@ -21,37 +20,46 @@ internal ConsoleStream(FileAccess access)
_canWrite = ((access & FileAccess.Write) == FileAccess.Write);
}
- protected override void Dispose(bool disposing)
+ public override void Write(byte[] buffer, int offset, int count)
{
- _canRead = false;
- _canWrite = false;
- base.Dispose(disposing);
+ ValidateWrite(buffer, offset, count);
+ Write(new ReadOnlySpan(buffer, offset, count));
}
- public sealed override bool CanRead
- {
- get { return _canRead; }
- }
+ public override void WriteByte(byte value) => Write(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
- public sealed override bool CanWrite
+ public override int Read(byte[] buffer, int offset, int count)
{
- get { return _canWrite; }
+ ValidateRead(buffer, offset, count);
+ return Read(new Span(buffer, offset, count));
}
- public sealed override bool CanSeek
+ public override int ReadByte()
{
- get { return false; }
+ byte b = 0;
+ int result = Read(MemoryMarshal.CreateSpan(ref b, 1));
+ return result != 0 ? b : -1;
}
- public sealed override long Length
+ protected override void Dispose(bool disposing)
{
- get { throw Error.GetSeekNotSupported(); }
+ _canRead = false;
+ _canWrite = false;
+ base.Dispose(disposing);
}
+ public sealed override bool CanRead => _canRead;
+
+ public sealed override bool CanWrite => _canWrite;
+
+ public sealed override bool CanSeek => false;
+
+ public sealed override long Length => throw Error.GetSeekNotSupported();
+
public sealed override long Position
{
- get { throw Error.GetSeekNotSupported(); }
- set { throw Error.GetSeekNotSupported(); }
+ get => throw Error.GetSeekNotSupported();
+ set => throw Error.GetSeekNotSupported();
}
public override void Flush()
@@ -59,28 +67,28 @@ public override void Flush()
if (!CanWrite) throw Error.GetWriteNotSupported();
}
- public sealed override void SetLength(long value)
- {
- throw Error.GetSeekNotSupported();
- }
+ public sealed override void SetLength(long value) => throw Error.GetSeekNotSupported();
- public sealed override long Seek(long offset, SeekOrigin origin)
- {
- throw Error.GetSeekNotSupported();
- }
+ public sealed override long Seek(long offset, SeekOrigin origin) => throw Error.GetSeekNotSupported();
protected void ValidateRead(byte[] buffer, int offset, int count)
{
ValidateBufferArguments(buffer, offset, count);
- if (!_canRead) throw Error.GetReadNotSupported();
+ if (!_canRead)
+ {
+ throw Error.GetReadNotSupported();
+ }
}
protected void ValidateWrite(byte[] buffer, int offset, int count)
{
ValidateBufferArguments(buffer, offset, count);
- if (!_canWrite) throw Error.GetWriteNotSupported();
+ if (!_canWrite)
+ {
+ throw Error.GetWriteNotSupported();
+ }
}
}
}
diff --git a/src/libraries/System.Console/src/System/IO/StdInReader.cs b/src/libraries/System.Console/src/System/IO/StdInReader.cs
index 8b0cd2021ecfe9..7af76c629f0800 100644
--- a/src/libraries/System.Console/src/System/IO/StdInReader.cs
+++ b/src/libraries/System.Console/src/System/IO/StdInReader.cs
@@ -93,9 +93,9 @@ internal unsafe int ReadStdin(byte* buffer, int bufferSize)
return line;
}
- public int ReadLine(byte[] buffer, int offset, int count)
+ public int ReadLine(Span buffer)
{
- if (count == 0)
+ if (buffer.IsEmpty)
{
return 0;
}
@@ -114,11 +114,10 @@ public int ReadLine(byte[] buffer, int offset, int count)
Encoder encoder = _bufferReadEncoder ??= _encoding.GetEncoder();
int bytesUsedTotal = 0;
int charsUsedTotal = 0;
- Span destination = buffer.AsSpan(offset, count);
foreach (ReadOnlyMemory chunk in _readLineSB.GetChunks())
{
- encoder.Convert(chunk.Span, destination, flush: false, out int charsUsed, out int bytesUsed, out bool completed);
- destination = destination.Slice(bytesUsed);
+ encoder.Convert(chunk.Span, buffer, flush: false, out int charsUsed, out int bytesUsed, out bool completed);
+ buffer = buffer.Slice(bytesUsed);
bytesUsedTotal += bytesUsed;
charsUsedTotal += charsUsed;
diff --git a/src/libraries/System.Console/src/System/IO/SyncTextReader.Unix.cs b/src/libraries/System.Console/src/System/IO/SyncTextReader.Unix.cs
index 9e68d74c1b0023..b2e5d36d7a23a4 100644
--- a/src/libraries/System.Console/src/System/IO/SyncTextReader.Unix.cs
+++ b/src/libraries/System.Console/src/System/IO/SyncTextReader.Unix.cs
@@ -40,7 +40,6 @@ public bool KeyAvailable
}
}
- public int ReadLine(byte[] buffer, int offset, int count)
- => Inner.ReadLine(buffer, offset, count);
+ public int ReadLine(Span buffer) => Inner.ReadLine(buffer);
}
}
diff --git a/src/libraries/System.Console/src/System/TermInfo.cs b/src/libraries/System.Console/src/System/TermInfo.cs
index d8e954d8c5e465..be84dbaf836619 100644
--- a/src/libraries/System.Console/src/System/TermInfo.cs
+++ b/src/libraries/System.Console/src/System/TermInfo.cs
@@ -262,10 +262,9 @@ private static bool TryOpen(string filePath, [NotNullWhen(true)] out SafeFileHan
{
throw new InvalidOperationException(SR.IO_TermInfoInvalid);
}
- int fileLen = (int)termInfoLength;
- byte[] data = new byte[fileLen];
- if (ConsolePal.Read(fd, data, 0, fileLen) != fileLen)
+ byte[] data = new byte[(int)termInfoLength];
+ if (ConsolePal.Read(fd, data) != data.Length)
{
throw new InvalidOperationException(SR.IO_TermInfoInvalid);
}