Move public StreamReader/Writer to CoreLib (#15884)
authorJan Kotas <jkotas@microsoft.com>
Wed, 17 Jan 2018 16:54:19 +0000 (08:54 -0800)
committerGitHub <noreply@github.com>
Wed, 17 Jan 2018 16:54:19 +0000 (08:54 -0800)
src/mscorlib/Resources/Strings.resx
src/mscorlib/System.Private.CoreLib.csproj
src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
src/mscorlib/shared/System/IO/StreamReader.cs [moved from src/mscorlib/src/System/IO/StreamReader.cs with 53% similarity]
src/mscorlib/shared/System/IO/StreamWriter.cs [new file with mode: 0644]
src/mscorlib/shared/System/IO/TextReader.cs [moved from src/mscorlib/src/System/IO/TextReader.cs with 51% similarity]
src/mscorlib/shared/System/IO/TextWriter.cs [new file with mode: 0644]

index d6b57f8..19d81b1 100644 (file)
   <data name="ObjectDisposed_ObjectName_Name" xml:space="preserve">
     <value>Object name: '{0}'.</value>
   </data>
+  <data name="ObjectDisposed_WriterClosed" xml:space="preserve">
+    <value>Cannot write to a closed TextWriter.</value>
+  </data>
   <data name="ObjectDisposed_ReaderClosed" xml:space="preserve">
     <value>Cannot read from a closed TextReader.</value>
   </data>
   <data name="Arg_TypeNotSupported" xml:space="preserve">
     <value>Specified type is not supported</value>
   </data>
+  <data name="IO_InvalidReadLength" xml:space="preserve">
+    <value>The read operation returned an invalid length.</value>
+  </data>
 </root>
\ No newline at end of file
index 0ff3659..765f027 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Globalization\EncodingDataItem.Unix.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Unix.cs" />
     <Compile Include="$(BclSourcesRoot)\System\IO\FileSystemEnumerable.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\IO\TextReader.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\IO\StreamReader.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\Versioning\CompatibilitySwitch.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandle.Unix.cs" />
     <Compile Include="$(BclSourcesRoot)\System\TimeZoneInfo.Unix.cs" />
index 92da5d6..58e6580 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\PinnedBufferMemoryStream.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\SeekOrigin.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamHelpers.CopyValidation.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamReader.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\StreamWriter.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\TextReader.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\IO\TextWriter.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryAccessor.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryStream.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\UnmanagedMemoryStreamWrapper.cs" />
similarity index 53%
rename from src/mscorlib/src/System/IO/StreamReader.cs
rename to src/mscorlib/shared/System/IO/StreamReader.cs
index 605a5f9..4e724dd 100644 (file)
@@ -2,11 +2,11 @@
 // 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;
-using System.Runtime.InteropServices;
-using System.Runtime.Versioning;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace System.IO
@@ -14,50 +14,31 @@ namespace System.IO
     // This class implements a TextReader for reading characters to a Stream.
     // This is designed for character input in a particular Encoding, 
     // whereas the Stream class is designed for byte input and output.  
-    // 
-    internal class StreamReader : TextReader
+    public class StreamReader : TextReader
     {
         // StreamReader.Null is threadsafe.
         public new static readonly StreamReader Null = new NullStreamReader();
 
-        // Encoding.GetPreamble() always allocates and returns a new byte[] array for
-        // encodings that have a preamble.
-        // We can avoid repeated allocations for the default and commonly used Encoding.UTF8
-        // encoding by using our own private cached instance of the UTF8 preamble.
-        // This is lazily allocated the first time it is used.
-        private static byte[] s_utf8Preamble;
-
         // 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
         // buffer is negated by the win of allocating a smaller byte[], which 
         // saves construction time.  This does break adaptive buffering,
         // but this is slightly faster.
-        internal static int DefaultBufferSize
-        {
-            get
-            {
-                return 1024;
-            }
-        }
-
+        private const int DefaultBufferSize = 1024;  // Byte buffer size
         private const int DefaultFileStreamBufferSize = 4096;
         private const int MinBufferSize = 128;
-        private const int MaxSharedBuilderCapacity = 360; // also the max capacity used in StringBuilderCache
-               
-        private Stream stream;
-        private Encoding encoding;
-        private Decoder decoder;
-        private byte[] byteBuffer;
-        private char[] charBuffer;
-        private byte[] _preamble;   // Encoding's preamble, which identifies this encoding.
-        private int charPos;
-        private int charLen;
+
+        private Stream _stream;
+        private Encoding _encoding;
+        private Decoder _decoder;
+        private byte[] _byteBuffer;
+        private char[] _charBuffer;
+        private int _charPos;
+        private int _charLen;
         // Record the number of valid bytes in the byteBuffer, for a few checks.
-        private int byteLen;
+        private int _byteLen;
         // This is used only for preamble detection
-        private int bytePos;
-
-        private StringBuilder _builder;
+        private int _bytePos;
 
         // This is the maximum number of chars we can get from one call to 
         // ReadBuffer.  Used so ReadBuffer can tell when to copy data into
@@ -82,8 +63,9 @@ namespace System.IO
         private bool _isBlocked;
 
         // The intent of this field is to leave open the underlying stream when 
-        // disposing of this StreamReader.
-        private bool _leaveOpen;  // Whether to keep the underlying stream open.
+        // disposing of this StreamReader.  A name like _leaveOpen is better, 
+        // but this type is serializable, and this field's name was _closable.
+        private bool _closable;  // Whether to close the underlying stream.
 
         // We don't guarantee thread safety on StreamReader, but we should at 
         // least prevent users from trying to read anything while an Async
@@ -91,38 +73,45 @@ namespace System.IO
         private volatile Task _asyncReadTask;
 
         private void CheckAsyncTaskInProgress()
-        {           
+        {
             // We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety. 
             // We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress.
-           
+
             Task t = _asyncReadTask;
 
             if (t != null && !t.IsCompleted)
+            {
                 throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
+            }
         }
 
         // StreamReader by default will ignore illegal UTF8 characters. We don't want to 
         // throw here because we want to be able to read ill-formed data without choking. 
         // The high level goal is to be tolerant of encoding errors when we read and very strict 
         // when we write. Hence, default StreamWriter encoding will throw on error.   
-        
-        internal StreamReader() {
+
+        internal StreamReader()
+        {
         }
-        
-        public StreamReader(Stream stream) 
-            : this(stream, true) {
+
+        public StreamReader(Stream stream)
+            : this(stream, true)
+        {
         }
 
-        public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks) 
-            : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
+        public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
+            : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false)
+        {
         }
-        
-        public StreamReader(Stream stream, Encoding encoding) 
-            : this(stream, encoding, true, DefaultBufferSize, false) {
+
+        public StreamReader(Stream stream, Encoding encoding)
+            : this(stream, encoding, true, DefaultBufferSize, false)
+        {
         }
-        
+
         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
-            : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
+            : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false)
+        {
         }
 
         // Creates a new StreamReader for the given stream.  The 
@@ -136,130 +125,139 @@ namespace System.IO
         // of those three match, it will use the Encoding you provided.
         // 
         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
-            : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false) {
+            : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false)
+        {
         }
 
         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
         {
             if (stream == null || encoding == null)
-                throw new ArgumentNullException((stream == null ? nameof(stream) : nameof(encoding)));
+            {
+                throw new ArgumentNullException(stream == null ? nameof(stream) : nameof(encoding));
+            }
             if (!stream.CanRead)
+            {
                 throw new ArgumentException(SR.Argument_StreamNotReadable);
+            }
             if (bufferSize <= 0)
+            {
                 throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+            }
 
             Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen);
         }
 
-        public StreamReader(String path) 
-            : this(path, true) {
+        public StreamReader(string path)
+            : this(path, true)
+        {
         }
 
-        public StreamReader(String path, bool detectEncodingFromByteOrderMarks) 
-            : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
+        public StreamReader(string path, bool detectEncodingFromByteOrderMarks)
+            : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize)
+        {
         }
 
-        public StreamReader(String path, Encoding encoding) 
-            : this(path, encoding, true, DefaultBufferSize) {
+        public StreamReader(string path, Encoding encoding)
+            : this(path, encoding, true, DefaultBufferSize)
+        {
         }
 
-        public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks) 
-            : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
+        public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
+            : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize)
+        {
         }
 
-        public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
+        public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
         {
-            // Don't open a Stream before checking for invalid arguments,
-            // or we'll create a FileStream on disk and we won't close it until
-            // the finalizer runs, causing problems for applications.
-            if (path==null || encoding==null)
-                throw new ArgumentNullException((path==null ? nameof(path) : nameof(encoding)));
-            if (path.Length==0)
+            if (path == null)
+                throw new ArgumentNullException(nameof(path));
+            if (encoding == null)
+                throw new ArgumentNullException(nameof(encoding));
+            if (path.Length == 0)
                 throw new ArgumentException(SR.Argument_EmptyPath);
             if (bufferSize <= 0)
                 throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
 
-            Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan);
-            Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false);
+            Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 
+                DefaultFileStreamBufferSize, FileOptions.SequentialScan);
+            Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen: false);
         }
-        
-        private void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen) {
-            this.stream = stream;
-            this.encoding = encoding;
-            decoder = encoding.GetDecoder();
-            if (bufferSize < MinBufferSize) bufferSize = MinBufferSize;
-            byteBuffer = new byte[bufferSize];
+
+        private void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
+        {
+            _stream = stream;
+            _encoding = encoding;
+            _decoder = encoding.GetDecoder();
+            if (bufferSize < MinBufferSize)
+            {
+                bufferSize = MinBufferSize;
+            }
+
+            _byteBuffer = new byte[bufferSize];
             _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
-            charBuffer = new char[_maxCharsPerBuffer];
-            byteLen = 0;
-            bytePos = 0;
+            _charBuffer = new char[_maxCharsPerBuffer];
+            _byteLen = 0;
+            _bytePos = 0;
             _detectEncoding = detectEncodingFromByteOrderMarks;
-
-            // Encoding.GetPreamble() always allocates and returns a new byte[] array for
-            // encodings that have a preamble.
-            // We can avoid repeated allocations for the default and commonly used Encoding.UTF8
-            // encoding by using our own private cached instance of the UTF8 preamble.
-            // We specifically look for Encoding.UTF8 because we know it has a preamble,
-            // whereas other instances of UTF8Encoding may not have a preamble enabled, and
-            // there's no public way to tell if the preamble is enabled for an instance other
-            // than calling GetPreamble(), which we're trying to avoid.
-            // This means that other instances of UTF8Encoding are excluded from this optimization.
-            _preamble = object.ReferenceEquals(encoding, Encoding.UTF8) ?
-                (s_utf8Preamble ?? (s_utf8Preamble = encoding.GetPreamble())) :
-                encoding.GetPreamble();
-
-            _checkPreamble = (_preamble.Length > 0);
+            _checkPreamble = encoding.Preamble.Length > 0;
             _isBlocked = false;
-            _leaveOpen = leaveOpen;
+            _closable = !leaveOpen;
         }
 
         // Init used by NullStreamReader, to delay load encoding
         internal void Init(Stream stream)
         {
-            this.stream = stream;
-            _leaveOpen = false;
+            _stream = stream;
+            _closable = true;
         }
 
         public override void Close()
         {
             Dispose(true);
         }
-        
+
         protected override void Dispose(bool disposing)
         {
             // Dispose of our resources if this StreamReader is closable.
             // Note that Console.In should be left open.
-            try {
+            try
+            {
                 // Note that Stream.Close() can potentially throw here. So we need to 
                 // ensure cleaning up internal resources, inside the finally block.  
-                if (!LeaveOpen && disposing && (stream != null))
-                    stream.Close();
-            }
-            finally {
-                if (!LeaveOpen && (stream != null)) {
-                    stream = null;
-                    encoding = null;
-                    decoder = null;
-                    byteBuffer = null;
-                    charBuffer = null;
-                    charPos = 0;
-                    charLen = 0;
-                    _builder = null;
+                if (!LeaveOpen && disposing && (_stream != null))
+                {
+                    _stream.Close();
+                }
+            }
+            finally
+            {
+                if (!LeaveOpen && (_stream != null))
+                {
+                    _stream = null;
+                    _encoding = null;
+                    _decoder = null;
+                    _byteBuffer = null;
+                    _charBuffer = null;
+                    _charPos = 0;
+                    _charLen = 0;
                     base.Dispose(disposing);
                 }
             }
         }
-        
-        public virtual Encoding CurrentEncoding {
-            get { return encoding; }
+
+        public virtual Encoding CurrentEncoding
+        {
+            get { return _encoding; }
         }
-        
-        public virtual Stream BaseStream {
-            get { return stream; }
+
+        public virtual Stream BaseStream
+        {
+            get { return _stream; }
         }
 
-        internal bool LeaveOpen {
-            get { return _leaveOpen; }
+        internal bool LeaveOpen
+        {
+            get { return !_closable; }
         }
 
         // DiscardBufferedData tells StreamReader to throw away its internal
@@ -273,26 +271,33 @@ namespace System.IO
         {
             CheckAsyncTaskInProgress();
 
-            byteLen = 0;
-            charLen = 0;
-            charPos = 0;
+            _byteLen = 0;
+            _charLen = 0;
+            _charPos = 0;
             // in general we'd like to have an invariant that encoding isn't null. However,
             // for startup improvements for NullStreamReader, we want to delay load encoding. 
-            if (encoding != null) {
-                decoder = encoding.GetDecoder();
+            if (_encoding != null)
+            {
+                _decoder = _encoding.GetDecoder();
             }
             _isBlocked = false;
         }
 
-        public bool EndOfStream {
-            get {
-                if (stream == null)
+        public bool EndOfStream
+        {
+            get
+            {
+                if (_stream == null)
+                {
                     throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+                }
 
                 CheckAsyncTaskInProgress();
 
-                if (charPos < charLen)
+                if (_charPos < _charLen)
+                {
                     return false;
+                }
 
                 // This may block on pipes!
                 int numRead = ReadBuffer();
@@ -300,44 +305,74 @@ namespace System.IO
             }
         }
 
-        public override int Peek() {
-            if (stream == null)
+        public override int Peek()
+        {
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
-            if (charPos == charLen)
+            if (_charPos == _charLen)
             {
-                if (_isBlocked || ReadBuffer() == 0) return -1;
+                if (_isBlocked || ReadBuffer() == 0)
+                {
+                    return -1;
+                }
             }
-            return charBuffer[charPos];
+            return _charBuffer[_charPos];
         }
-        
-        public override int Read() {
-            if (stream == null)
+
+        public override int Read()
+        {
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
-            if (charPos == charLen) {
-                if (ReadBuffer() == 0) return -1;
+            if (_charPos == _charLen)
+            {
+                if (ReadBuffer() == 0)
+                {
+                    return -1;
+                }
             }
-            int result = charBuffer[charPos];
-            charPos++;
+            int result = _charBuffer[_charPos];
+            _charPos++;
             return result;
         }
-    
+
         public override int Read(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0 || count < 0)
-                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            {
+                throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
+
+            return ReadSpan(new Span<char>(buffer, index, count));
+        }
 
-            if (stream == null)
+        public override int Read(Span<char> buffer) =>
+            GetType() == typeof(StreamReader) ? ReadSpan(buffer) :
+            base.Read(buffer); // Defer to Read(char[], ...) if a derived type may have previously overridden it
+        
+        private int ReadSpan(Span<char> buffer)
+        {
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
@@ -345,120 +380,176 @@ namespace System.IO
             // As a perf optimization, if we had exactly one buffer's worth of 
             // data read in, let's try writing directly to the user's buffer.
             bool readToUserBuffer = false;
-            while (count > 0) {
-                int n = charLen - charPos;
-                if (n == 0) n = ReadBuffer(buffer, index + charsRead, count, out readToUserBuffer);
-                if (n == 0) break;  // We're at EOF
-                if (n > count) n = count;
-                if (!readToUserBuffer) {
-                    Buffer.BlockCopy(charBuffer, charPos * 2, buffer, (index + charsRead) * 2, n*2);
-                    charPos += n;
+            int count = buffer.Length;
+            while (count > 0)
+            {
+                int n = _charLen - _charPos;
+                if (n == 0)
+                {
+                    n = ReadBuffer(buffer.Slice(charsRead), out readToUserBuffer);
+                }
+                if (n == 0)
+                {
+                    break;  // We're at EOF
+                }
+                if (n > count)
+                {
+                    n = count;
+                }
+                if (!readToUserBuffer)
+                {
+                    new Span<char>(_charBuffer, _charPos, n).CopyTo(buffer.Slice(charsRead));
+                    _charPos += n;
                 }
+
                 charsRead += n;
                 count -= n;
                 // This function shouldn't block for an indefinite amount of time,
                 // or reading from a network stream won't work right.  If we got
                 // fewer bytes than we requested, then we want to break right here.
                 if (_isBlocked)
+                {
                     break;
+                }
             }
 
             return charsRead;
         }
 
-        public override String ReadToEnd()
+        public override string ReadToEnd()
         {
-            if (stream == null)
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
             // Call ReadBuffer, then pull data out of charBuffer.
-            StringBuilder sb = AcquireSharedStringBuilder(charLen - charPos);
-            do {
-                sb.Append(charBuffer, charPos, charLen - charPos);
-                charPos = charLen;  // Note we consumed these characters
+            StringBuilder sb = new StringBuilder(_charLen - _charPos);
+            do
+            {
+                sb.Append(_charBuffer, _charPos, _charLen - _charPos);
+                _charPos = _charLen;  // Note we consumed these characters
                 ReadBuffer();
-            } while (charLen > 0);
-
-            return GetStringAndReleaseSharedStringBuilder(sb);
+            } while (_charLen > 0);
+            return sb.ToString();
         }
 
         public override int ReadBlock(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0 || count < 0)
-                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            {
+                throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
-
-            if (stream == null)
+            }
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
             return base.ReadBlock(buffer, index, count);
         }
 
+        public override int ReadBlock(Span<char> buffer)
+        {
+            if (GetType() != typeof(StreamReader))
+            {
+                // Defer to Read(char[], ...) if a derived type may have previously overridden it.
+                return base.ReadBlock(buffer);
+            }
+
+            int i, n = 0;
+            do
+            {
+                i = ReadSpan(buffer.Slice(n));
+                n += i;
+            } while (i > 0 && n < buffer.Length);
+            return n;
+        }
+
         // Trims n bytes from the front of the buffer.
         private void CompressBuffer(int n)
         {
-            Debug.Assert(byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length.  Are two threads using this StreamReader at the same time?");
-            Buffer.BlockCopy(byteBuffer, n, byteBuffer, 0, byteLen - n);
-            byteLen -= n;
+            Debug.Assert(_byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length.  Are two threads using this StreamReader at the same time?");
+            Buffer.BlockCopy(_byteBuffer, n, _byteBuffer, 0, _byteLen - n);
+            _byteLen -= n;
         }
 
         private void DetectEncoding()
         {
-            if (byteLen < 2)
+            if (_byteLen < 2)
+            {
                 return;
+            }
             _detectEncoding = false;
             bool changedEncoding = false;
-            if (byteBuffer[0]==0xFE && byteBuffer[1]==0xFF) {
+            if (_byteBuffer[0] == 0xFE && _byteBuffer[1] == 0xFF)
+            {
                 // Big Endian Unicode
 
-                encoding = Encoding.BigEndianUnicode;
+                _encoding = Encoding.BigEndianUnicode;
                 CompressBuffer(2);
                 changedEncoding = true;
             }
-                     
-            else if (byteBuffer[0]==0xFF && byteBuffer[1]==0xFE) {
+
+            else if (_byteBuffer[0] == 0xFF && _byteBuffer[1] == 0xFE)
+            {
                 // Little Endian Unicode, or possibly little endian UTF32
-                if (byteLen < 4 || byteBuffer[2] != 0 || byteBuffer[3] != 0) {
-                    encoding = Encoding.Unicode;
+                if (_byteLen < 4 || _byteBuffer[2] != 0 || _byteBuffer[3] != 0)
+                {
+                    _encoding = Encoding.Unicode;
                     CompressBuffer(2);
                     changedEncoding = true;
                 }
-                else {
-                    encoding = Encoding.UTF32;
+                else
+                {
+                    _encoding = Encoding.UTF32;
                     CompressBuffer(4);
-                changedEncoding = true;
+                    changedEncoding = true;
                 }
             }
-         
-            else if (byteLen >= 3 && byteBuffer[0]==0xEF && byteBuffer[1]==0xBB && byteBuffer[2]==0xBF) {
+
+            else if (_byteLen >= 3 && _byteBuffer[0] == 0xEF && _byteBuffer[1] == 0xBB && _byteBuffer[2] == 0xBF)
+            {
                 // UTF-8
-                encoding = Encoding.UTF8;
+                _encoding = Encoding.UTF8;
                 CompressBuffer(3);
                 changedEncoding = true;
             }
-            else if (byteLen >= 4 && byteBuffer[0] == 0 && byteBuffer[1] == 0 &&
-                     byteBuffer[2] == 0xFE && byteBuffer[3] == 0xFF) {
+            else if (_byteLen >= 4 && _byteBuffer[0] == 0 && _byteBuffer[1] == 0 &&
+                _byteBuffer[2] == 0xFE && _byteBuffer[3] == 0xFF)
+            {
                 // Big Endian UTF32
-                encoding = new UTF32Encoding(true, true);
+                _encoding = new UTF32Encoding(bigEndian: true, byteOrderMark: true);
                 CompressBuffer(4);
                 changedEncoding = true;
             }
-            else if (byteLen == 2)
+            else if (_byteLen == 2)
+            {
                 _detectEncoding = true;
+            }
             // Note: in the future, if we change this algorithm significantly,
             // we can support checking for the preamble of the given encoding.
 
-            if (changedEncoding) {
-                decoder = encoding.GetDecoder();
-                _maxCharsPerBuffer = encoding.GetMaxCharCount(byteBuffer.Length);
-                charBuffer = new char[_maxCharsPerBuffer];
+            if (changedEncoding)
+            {
+                _decoder = _encoding.GetDecoder();
+                int newMaxCharsPerBuffer = _encoding.GetMaxCharCount(_byteBuffer.Length);
+                if (newMaxCharsPerBuffer > _maxCharsPerBuffer)
+                {
+                    _charBuffer = new char[newMaxCharsPerBuffer];
+                }
+                _maxCharsPerBuffer = newMaxCharsPerBuffer;
             }
         }
 
@@ -469,27 +560,35 @@ namespace System.IO
         // leading preamble bytes
         private bool IsPreamble()
         {
-            if (!_checkPreamble) 
+            if (!_checkPreamble)
+            {
                 return _checkPreamble;
+            }
+
+            ReadOnlySpan<byte> preamble = _encoding.Preamble;
 
-            Debug.Assert(bytePos <= _preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length.  Are two threads using this StreamReader at the same time?");
-            int len = (byteLen >= (_preamble.Length))? (_preamble.Length - bytePos) : (byteLen  - bytePos);
+            Debug.Assert(_bytePos <= preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length.  Are two threads using this StreamReader at the same time?");
+            int len = (_byteLen >= (preamble.Length)) ? (preamble.Length - _bytePos) : (_byteLen - _bytePos);
 
-            for(int i=0; i<len; i++, bytePos++) {
-                if (byteBuffer[bytePos] != _preamble[bytePos]) {
-                    bytePos = 0;
+            for (int i = 0; i < len; i++, _bytePos++)
+            {
+                if (_byteBuffer[_bytePos] != preamble[_bytePos])
+                {
+                    _bytePos = 0;
                     _checkPreamble = false;
                     break;
                 }
             }
 
-            Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
+            Debug.Assert(_bytePos <= preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
 
-            if (_checkPreamble) {
-                if (bytePos == _preamble.Length) {
+            if (_checkPreamble)
+            {
+                if (_bytePos == preamble.Length)
+                {
                     // We have a match
-                    CompressBuffer(_preamble.Length);
-                    bytePos = 0;
+                    CompressBuffer(preamble.Length);
+                    _bytePos = 0;
                     _checkPreamble = false;
                     _detectEncoding = false;
                 }
@@ -498,94 +597,76 @@ namespace System.IO
             return _checkPreamble;
         }
 
-        private StringBuilder AcquireSharedStringBuilder(int capacity)
+        internal virtual int ReadBuffer()
         {
-            // Do not touch the shared builder if it will be removed on release
-            if (capacity > MaxSharedBuilderCapacity)
-                return new StringBuilder(capacity);
-
-            // note that since StreamReader does not support concurrent reads it is not needed to
-            // set _builder to null to avoid parallel acquisitions.
-            StringBuilder sb = _builder;
-
-            if (sb == null)
-                return _builder = new StringBuilder(capacity);
-             
-            // Clear the shared builder. Does not remove the allocated buffers so they are reused.
-            sb.Length = 0;
-
-            // When needed, recreate the buffer backing the StringBuilder so that further Append calls
-            // are less likely to internally allocate new StringBuilders (or chunks).
-            if (sb.Capacity < capacity)
-                sb.Capacity = capacity;
-
-            return sb;
-        }
-
-        private string GetStringAndReleaseSharedStringBuilder(StringBuilder sb)
-        {
-            if (sb == _builder && sb.Capacity > MaxSharedBuilderCapacity)
-                _builder = null;
-
-            return sb.ToString();
-        }
-        
-        internal int ReadBuffer() {
-            charLen = 0;
-            charPos = 0;
+            _charLen = 0;
+            _charPos = 0;
 
             if (!_checkPreamble)
-                byteLen = 0;
-            do {
-                if (_checkPreamble) {
-                    Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
-                    int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
+            {
+                _byteLen = 0;
+            }
+
+            do
+            {
+                if (_checkPreamble)
+                {
+                    Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
+                    int len = _stream.Read(_byteBuffer, _bytePos, _byteBuffer.Length - _bytePos);
                     Debug.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
 
-                    if (len == 0) {
+                    if (len == 0)
+                    {
                         // EOF but we might have buffered bytes from previous 
                         // attempt to detect preamble that needs to be decoded now
-                        if (byteLen > 0)
+                        if (_byteLen > 0)
                         {
-                            charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
+                            _charLen += _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, _charLen);
                             // Need to zero out the byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
-                            bytePos = byteLen = 0;
+                            _bytePos = _byteLen = 0;
                         }
 
-                        return charLen;
+                        return _charLen;
                     }
 
-                    byteLen += len;
+                    _byteLen += len;
                 }
-                else {
-                    Debug.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
-                    byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
-                    Debug.Assert(byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
+                else
+                {
+                    Debug.Assert(_bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
+                    _byteLen = _stream.Read(_byteBuffer, 0, _byteBuffer.Length);
+                    Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
 
-                    if (byteLen == 0)  // We're at EOF
-                        return charLen;
+                    if (_byteLen == 0)  // We're at EOF
+                    {
+                        return _charLen;
+                    }
                 }
 
                 // _isBlocked == whether we read fewer bytes than we asked for.
                 // Note we must check it here because CompressBuffer or 
                 // DetectEncoding will change byteLen.
-                _isBlocked = (byteLen < byteBuffer.Length);
+                _isBlocked = (_byteLen < _byteBuffer.Length);
 
                 // Check for preamble before detect encoding. This is not to override the
-                // user suppplied Encoding for the one we implicitly detect. The user could
+                // user supplied Encoding for the one we implicitly detect. The user could
                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
                 if (IsPreamble())
+                {
                     continue;
+                }
 
                 // If we're supposed to detect the encoding and haven't done so yet,
                 // do it.  Note this may need to be called more than once.
-                if (_detectEncoding && byteLen >= 2)
+                if (_detectEncoding && _byteLen >= 2)
+                {
                     DetectEncoding();
+                }
 
-                charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
-            } while (charLen == 0);
+                _charLen += _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, _charLen);
+            } while (_charLen == 0);
             //Console.WriteLine("ReadBuffer called.  chars: "+charLen);
-            return charLen;
+            return _charLen;
         }
 
 
@@ -596,14 +677,16 @@ namespace System.IO
         // buffer's worth of bytes could produce.
         // This optimization, if run, will break SwitchEncoding, so we must not do 
         // this on the first call to ReadBuffer.  
-        private int ReadBuffer(char[] userBuffer, int userOffset, int desiredChars, out bool readToUserBuffer)
+        private int ReadBuffer(Span<char> userBuffer, out bool readToUserBuffer)
         {
-            charLen = 0;
-            charPos = 0;
-            
+            _charLen = 0;
+            _charPos = 0;
+
             if (!_checkPreamble)
-                byteLen = 0;
-            
+            {
+                _byteLen = 0;
+            }
+
             int charsRead = 0;
 
             // As a perf optimization, we can decode characters DIRECTLY into a
@@ -617,78 +700,92 @@ namespace System.IO
             // buffer optimization.  This affects reads where the end of the
             // Stream comes in the middle somewhere, and when you ask for 
             // fewer chars than your buffer could produce.
-            readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
+            readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer;
 
-            do {
+            do
+            {
                 Debug.Assert(charsRead == 0);
 
-                if (_checkPreamble) {
-                    Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
-                    int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
+                if (_checkPreamble)
+                {
+                    Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
+                    int len = _stream.Read(_byteBuffer, _bytePos, _byteBuffer.Length - _bytePos);
                     Debug.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
-                    
-                    if (len == 0) {
+
+                    if (len == 0)
+                    {
                         // EOF but we might have buffered bytes from previous 
                         // attempt to detect preamble that needs to be decoded now
-                        if (byteLen > 0) {
-                            if (readToUserBuffer) {
-                                charsRead = decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
-                                charLen = 0;  // StreamReader's buffer is empty.
+                        if (_byteLen > 0)
+                        {
+                            if (readToUserBuffer)
+                            {
+                                charsRead = _decoder.GetChars(new ReadOnlySpan<byte>(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush: false);
+                                _charLen = 0;  // StreamReader's buffer is empty.
                             }
-                            else {
-                                charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
-                                charLen += charsRead;  // Number of chars in StreamReader's buffer.
+                            else
+                            {
+                                charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, charsRead);
+                                _charLen += charsRead;  // Number of chars in StreamReader's buffer.
                             }
                         }
 
                         return charsRead;
                     }
-                    
-                    byteLen += len;
+
+                    _byteLen += len;
                 }
-                else {
-                    Debug.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
+                else
+                {
+                    Debug.Assert(_bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
+
+                    _byteLen = _stream.Read(_byteBuffer, 0, _byteBuffer.Length);
 
-                    byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
+                    Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
 
-                    Debug.Assert(byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
-                    
-                    if (byteLen == 0)  // EOF
+                    if (_byteLen == 0)  // EOF
+                    {
                         break;
+                    }
                 }
 
                 // _isBlocked == whether we read fewer bytes than we asked for.
                 // Note we must check it here because CompressBuffer or 
                 // DetectEncoding will change byteLen.
-                _isBlocked = (byteLen < byteBuffer.Length);
+                _isBlocked = (_byteLen < _byteBuffer.Length);
 
                 // Check for preamble before detect encoding. This is not to override the
-                // user suppplied Encoding for the one we implicitly detect. The user could
+                // user supplied Encoding for the one we implicitly detect. The user could
                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
                 // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
                 // doesn't change the encoding or affect _maxCharsPerBuffer
-                if (IsPreamble()) 
+                if (IsPreamble())
+                {
                     continue;
+                }
 
                 // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
-                if (_detectEncoding && byteLen >= 2) {
+                if (_detectEncoding && _byteLen >= 2)
+                {
                     DetectEncoding();
                     // DetectEncoding changes some buffer state.  Recompute this.
-                    readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
+                    readToUserBuffer = userBuffer.Length >= _maxCharsPerBuffer;
                 }
 
-                charPos = 0;
-                if (readToUserBuffer) {
-                    charsRead += decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
-                    charLen = 0;  // StreamReader's buffer is empty.
+                _charPos = 0;
+                if (readToUserBuffer)
+                {
+                    charsRead += _decoder.GetChars(new ReadOnlySpan<byte>(_byteBuffer, 0, _byteLen), userBuffer.Slice(charsRead), flush:false);
+                    _charLen = 0;  // StreamReader's buffer is empty.
                 }
-                else {
-                    charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
-                    charLen += charsRead;  // Number of chars in StreamReader's buffer.
+                else
+                {
+                    charsRead = _decoder.GetChars(_byteBuffer, 0, _byteLen, _charBuffer, charsRead);
+                    _charLen += charsRead;  // Number of chars in StreamReader's buffer.
                 }
             } while (charsRead == 0);
 
-            _isBlocked &= charsRead < desiredChars;
+            _isBlocked &= charsRead < userBuffer.Length;
 
             //Console.WriteLine("ReadBuffer: charsRead: "+charsRead+"  readToUserBuffer: "+readToUserBuffer);
             return charsRead;
@@ -701,82 +798,105 @@ namespace System.IO
         // contain the terminating carriage return and/or line feed. The returned
         // value is null if the end of the input stream has been reached.
         //
-        public override String ReadLine()
+        public override string ReadLine()
         {
-            if (stream == null)
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
-            if (charPos == charLen)
+            if (_charPos == _charLen)
             {
-                if (ReadBuffer() == 0) return null;
+                if (ReadBuffer() == 0)
+                {
+                    return null;
+                }
             }
 
             StringBuilder sb = null;
-            do {
-                int i = charPos;
-                do {
-                    char ch = charBuffer[i];
+            do
+            {
+                int i = _charPos;
+                do
+                {
+                    char ch = _charBuffer[i];
                     // Note the following common line feed chars:
                     // \n - UNIX   \r\n - DOS   \r - Mac
-                    if (ch == '\r' || ch == '\n') {
-                        String s;
-                        if (sb != null) {
-                            sb.Append(charBuffer, charPos, i - charPos);
-                            s = GetStringAndReleaseSharedStringBuilder(sb);
+                    if (ch == '\r' || ch == '\n')
+                    {
+                        string s;
+                        if (sb != null)
+                        {
+                            sb.Append(_charBuffer, _charPos, i - _charPos);
+                            s = sb.ToString();
                         }
-                        else {
-                            s = new String(charBuffer, charPos, i - charPos);
+                        else
+                        {
+                            s = new string(_charBuffer, _charPos, i - _charPos);
                         }
-                        charPos = i + 1;
-                        if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0)) {
-                            if (charBuffer[charPos] == '\n') charPos++;
+                        _charPos = i + 1;
+                        if (ch == '\r' && (_charPos < _charLen || ReadBuffer() > 0))
+                        {
+                            if (_charBuffer[_charPos] == '\n')
+                            {
+                                _charPos++;
+                            }
                         }
                         return s;
                     }
                     i++;
-                } while (i < charLen);
-                i = charLen - charPos;
-                if (sb == null) sb = AcquireSharedStringBuilder(i + 80);
-                sb.Append(charBuffer, charPos, i);
+                } while (i < _charLen);
+                i = _charLen - _charPos;
+                if (sb == null)
+                {
+                    sb = new StringBuilder(i + 80);
+                }
+                sb.Append(_charBuffer, _charPos, i);
             } while (ReadBuffer() > 0);
-            return GetStringAndReleaseSharedStringBuilder(sb);
+            return sb.ToString();
         }
-        
+
         #region Task based Async APIs
-        public override Task<String> ReadLineAsync()
+        public override Task<string> ReadLineAsync()
         {
             // If we have been inherited into a subclass, the following implementation could be incorrect
-            // since it does not call through to Read() which a subclass might have overriden.  
+            // since it does not call through to Read() which a subclass might have overridden.  
             // To be safe we will only use this implementation in cases where we know it is safe to do so,
             // and delegate to our base class (which will call into Read) when we are not sure.
-            if (this.GetType() != typeof(StreamReader))
+            if (GetType() != typeof(StreamReader))
+            {
                 return base.ReadLineAsync();
+            }
 
-            if (stream == null)
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
-            Task<String> task = ReadLineAsyncInternal();
+            Task<string> task = ReadLineAsyncInternal();
             _asyncReadTask = task;
 
             return task;
         }
 
-        private async Task<String> ReadLineAsyncInternal()
+        private async Task<string> ReadLineAsyncInternal()
         {
-            if (charPos == charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
+            if (_charPos == _charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
+            {
                 return null;
+            }
 
             StringBuilder sb = null;
 
             do
             {
-                char[] tmpCharBuffer = charBuffer;
-                int tmpCharLen = charLen;
-                int tmpCharPos = charPos;
+                char[] tmpCharBuffer = _charBuffer;
+                int tmpCharLen = _charLen;
+                int tmpCharPos = _charPos;
                 int i = tmpCharPos;
 
                 do
@@ -787,109 +907,151 @@ namespace System.IO
                     // \n - UNIX   \r\n - DOS   \r - Mac
                     if (ch == '\r' || ch == '\n')
                     {
-                        String s;
+                        string s;
 
                         if (sb != null)
                         {
                             sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
-                            s = GetStringAndReleaseSharedStringBuilder(sb);
+                            s = sb.ToString();
                         }
                         else
                         {
-                            s = new String(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
+                            s = new string(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
                         }
 
-                        charPos = tmpCharPos = i + 1;
+                        _charPos = tmpCharPos = i + 1;
 
                         if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync().ConfigureAwait(false)) > 0))
                         {
-                            tmpCharPos = charPos;
-                            if (charBuffer[tmpCharPos] == '\n')
-                                charPos = ++tmpCharPos;
+                            tmpCharPos = _charPos;
+                            if (_charBuffer[tmpCharPos] == '\n')
+                            {
+                                _charPos = ++tmpCharPos;
+                            }
                         }
 
                         return s;
                     }
 
                     i++;
-
                 } while (i < tmpCharLen);
 
                 i = tmpCharLen - tmpCharPos;
-                if (sb == null) sb = AcquireSharedStringBuilder(i + 80);
+                if (sb == null)
+                {
+                    sb = new StringBuilder(i + 80);
+                }
                 sb.Append(tmpCharBuffer, tmpCharPos, i);
-
             } while (await ReadBufferAsync().ConfigureAwait(false) > 0);
 
-            return GetStringAndReleaseSharedStringBuilder(sb);
+            return sb.ToString();
         }
 
-        public override Task<String> ReadToEndAsync()
+        public override Task<string> ReadToEndAsync()
         {
             // If we have been inherited into a subclass, the following implementation could be incorrect
-            // since it does not call through to Read() which a subclass might have overriden.  
+            // since it does not call through to Read() which a subclass might have overridden.  
             // To be safe we will only use this implementation in cases where we know it is safe to do so,
             // and delegate to our base class (which will call into Read) when we are not sure.
-            if (this.GetType() != typeof(StreamReader))
+            if (GetType() != typeof(StreamReader))
+            {
                 return base.ReadToEndAsync();
+            }
 
-            if (stream == null)
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
-            Task<String> task = ReadToEndAsyncInternal();
+            Task<string> task = ReadToEndAsyncInternal();
             _asyncReadTask = task;
 
             return task;
         }
 
-        private async Task<String> ReadToEndAsyncInternal()
+        private async Task<string> ReadToEndAsyncInternal()
         {
             // Call ReadBuffer, then pull data out of charBuffer.
-            StringBuilder sb = AcquireSharedStringBuilder(charLen - charPos);
+            StringBuilder sb = new StringBuilder(_charLen - _charPos);
             do
             {
-                int tmpCharPos = charPos;
-                sb.Append(charBuffer, tmpCharPos, charLen - tmpCharPos);
-                charPos = charLen;  // We consumed these characters
+                int tmpCharPos = _charPos;
+                sb.Append(_charBuffer, tmpCharPos, _charLen - tmpCharPos);
+                _charPos = _charLen;  // We consumed these characters
                 await ReadBufferAsync().ConfigureAwait(false);
-            } while (charLen > 0);
+            } while (_charLen > 0);
 
-            return GetStringAndReleaseSharedStringBuilder(sb);
+            return sb.ToString();
         }
 
         public override Task<int> ReadAsync(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0 || count < 0)
-                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            {
+                throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
 
             // If we have been inherited into a subclass, the following implementation could be incorrect
-            // since it does not call through to Read() which a subclass might have overriden.  
+            // since it does not call through to Read() which a subclass might have overridden.  
             // To be safe we will only use this implementation in cases where we know it is safe to do so,
             // and delegate to our base class (which will call into Read) when we are not sure.
-            if (this.GetType() != typeof(StreamReader))
+            if (GetType() != typeof(StreamReader))
+            {
                 return base.ReadAsync(buffer, index, count);
+            }
 
-            if (stream == null)
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
-            Task<int> task = ReadAsyncInternal(buffer, index, count);
+            Task<int> task = ReadAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
             _asyncReadTask = task;
 
             return task;
         }
 
-        internal override async Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
+        public override ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default)
+        {
+            if (GetType() != typeof(StreamReader))
+            {
+                // Ensure we use existing overrides if a class already overrode existing overloads.
+                return base.ReadAsync(buffer, cancellationToken);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+            }
+
+            return ReadAsyncInternal(buffer, cancellationToken);
+        }
+
+        internal override async ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
         {
-            if (charPos == charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
+            if (_charPos == _charLen && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
+            {
                 return 0;
+            }
 
             int charsRead = 0;
 
@@ -897,22 +1059,25 @@ namespace System.IO
             // data read in, let's try writing directly to the user's buffer.
             bool readToUserBuffer = false;
 
-            Byte[] tmpByteBuffer = byteBuffer;
-            Stream tmpStream = stream;
+            Byte[] tmpByteBuffer = _byteBuffer;
+            Stream tmpStream = _stream;
 
+            int count = buffer.Length;
             while (count > 0)
             {
                 // n is the characters available in _charBuffer
-                int n = charLen - charPos;
+                int n = _charLen - _charPos;
 
                 // charBuffer is empty, let's read from the stream
                 if (n == 0)
                 {
-                    charLen = 0;
-                    charPos = 0;
+                    _charLen = 0;
+                    _charPos = 0;
 
                     if (!_checkPreamble)
-                        byteLen = 0;
+                    {
+                        _byteLen = 0;
+                    }
 
                     readToUserBuffer = count >= _maxCharsPerBuffer;
 
@@ -924,29 +1089,29 @@ namespace System.IO
 
                         if (_checkPreamble)
                         {
-                            Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
-                            int tmpBytePos = bytePos;
-                            int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false);
+                            Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
+                            int tmpBytePos = _bytePos;
+                            int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos, cancellationToken).ConfigureAwait(false);
                             Debug.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
 
                             if (len == 0)
                             {
                                 // EOF but we might have buffered bytes from previous 
                                 // attempts to detect preamble that needs to be decoded now
-                                if (byteLen > 0)
+                                if (_byteLen > 0)
                                 {
                                     if (readToUserBuffer)
                                     {
-                                        n = decoder.GetChars(tmpByteBuffer, 0, byteLen, buffer, index + charsRead);
-                                        charLen = 0;  // StreamReader's buffer is empty.
+                                        n = _decoder.GetChars(new ReadOnlySpan<byte>(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false);
+                                        _charLen = 0;  // StreamReader's buffer is empty.
                                     }
                                     else
                                     {
-                                        n = decoder.GetChars(tmpByteBuffer, 0, byteLen, charBuffer, 0);
-                                        charLen += n;  // Number of chars in StreamReader's buffer.
+                                        n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0);
+                                        _charLen += n;  // Number of chars in StreamReader's buffer.
                                     }
                                 }
-                                        
+
                                 // How can part of the preamble yield any chars?
                                 Debug.Assert(n == 0);
 
@@ -955,18 +1120,18 @@ namespace System.IO
                             }
                             else
                             {
-                                byteLen += len;
+                                _byteLen += len;
                             }
                         }
                         else
                         {
-                            Debug.Assert(bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
+                            Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
 
-                            byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
+                            _byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length, cancellationToken).ConfigureAwait(false);
 
-                            Debug.Assert(byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
+                            Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
 
-                            if (byteLen == 0)  // EOF
+                            if (_byteLen == 0)  // EOF
                             {
                                 _isBlocked = true;
                                 break;
@@ -976,18 +1141,20 @@ namespace System.IO
                         // _isBlocked == whether we read fewer bytes than we asked for.
                         // Note we must check it here because CompressBuffer or 
                         // DetectEncoding will change _byteLen.
-                        _isBlocked = (byteLen < tmpByteBuffer.Length);
+                        _isBlocked = (_byteLen < tmpByteBuffer.Length);
 
                         // Check for preamble before detect encoding. This is not to override the
-                        // user suppplied Encoding for the one we implicitly detect. The user could
+                        // user supplied Encoding for the one we implicitly detect. The user could
                         // customize the encoding which we will loose, such as ThrowOnError on UTF8
                         // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
                         // doesn't change the encoding or affect _maxCharsPerBuffer
                         if (IsPreamble())
+                        {
                             continue;
+                        }
 
                         // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
-                        if (_detectEncoding && byteLen >= 2)
+                        if (_detectEncoding && _byteLen >= 2)
                         {
                             DetectEncoding();
                             // DetectEncoding changes some buffer state.  Recompute this.
@@ -996,39 +1163,43 @@ namespace System.IO
 
                         Debug.Assert(n == 0);
 
-                        charPos = 0;
+                        _charPos = 0;
                         if (readToUserBuffer)
                         {
-                            n += decoder.GetChars(tmpByteBuffer, 0, byteLen, buffer, index + charsRead);
-                                        
+                            n += _decoder.GetChars(new ReadOnlySpan<byte>(tmpByteBuffer, 0, _byteLen), buffer.Span.Slice(charsRead), flush: false);
+
                             // Why did the bytes yield no chars?
                             Debug.Assert(n > 0);
 
-                            charLen = 0;  // StreamReader's buffer is empty.
+                            _charLen = 0;  // StreamReader's buffer is empty.
                         }
                         else
                         {
-                            n = decoder.GetChars(tmpByteBuffer, 0, byteLen, charBuffer, 0);
-                                        
+                            n = _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, 0);
+
                             // Why did the bytes yield no chars?
                             Debug.Assert(n > 0);
 
-                            charLen += n;  // Number of chars in StreamReader's buffer.
+                            _charLen += n;  // Number of chars in StreamReader's buffer.
                         }
-
                     } while (n == 0);
 
-                    if (n == 0) break;  // We're at EOF
+                    if (n == 0)
+                    {
+                        break;  // We're at EOF
+                    }
                 }  // if (n == 0)
 
                 // Got more chars in charBuffer than the user requested
                 if (n > count)
+                {
                     n = count;
+                }
 
                 if (!readToUserBuffer)
                 {
-                    Buffer.BlockCopy(charBuffer, charPos * 2, buffer, (index + charsRead) * 2, n * 2);
-                    charPos += n;
+                    new Span<char>(_charBuffer, _charPos, n).CopyTo(buffer.Span.Slice(charsRead));
+                    _charPos += n;
                 }
 
                 charsRead += n;
@@ -1038,7 +1209,9 @@ namespace System.IO
                 // or reading from a network stream won't work right.  If we got
                 // fewer bytes than we requested, then we want to break right here.
                 if (_isBlocked)
+                {
                     break;
+                }
             }  // while (count > 0)
 
             return charsRead;
@@ -1046,22 +1219,32 @@ namespace System.IO
 
         public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0 || count < 0)
-                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            {
+                throw new ArgumentOutOfRangeException(index < 0 ? nameof(index) : nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
 
             // If we have been inherited into a subclass, the following implementation could be incorrect
-            // since it does not call through to Read() which a subclass might have overriden.  
+            // since it does not call through to Read() which a subclass might have overridden.  
             // To be safe we will only use this implementation in cases where we know it is safe to do so,
             // and delegate to our base class (which will call into Read) when we are not sure.
-            if (this.GetType() != typeof(StreamReader))
+            if (GetType() != typeof(StreamReader))
+            {
                 return base.ReadBlockAsync(buffer, index, count);
+            }
 
-            if (stream == null)
+            if (_stream == null)
+            {
                 throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
 
             CheckAsyncTaskInProgress();
 
@@ -1071,82 +1254,131 @@ namespace System.IO
             return task;
         }
 
+        public override ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default)
+        {
+            if (GetType() != typeof(StreamReader))
+            {
+                // If a derived type may have overridden ReadBlockAsync(char[], ...) before this overload
+                // was introduced, defer to it.
+                return base.ReadBlockAsync(buffer, cancellationToken);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_ReaderClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return new ValueTask<int>(Task.FromCanceled<int>(cancellationToken));
+            }
+
+            ValueTask<int> vt = ReadBlockAsyncInternal(buffer, cancellationToken);
+            if (vt.IsCompletedSuccessfully)
+            {
+                return vt;
+            }
+
+            Task<int> t = vt.AsTask();
+            _asyncReadTask = t;
+            return new ValueTask<int>(t);
+        }
+
         private async Task<int> ReadBufferAsync()
         {
-            charLen = 0;
-            charPos = 0;
-            Byte[] tmpByteBuffer = byteBuffer;
-            Stream tmpStream = stream;
-            
+            _charLen = 0;
+            _charPos = 0;
+            Byte[] tmpByteBuffer = _byteBuffer;
+            Stream tmpStream = _stream;
+
             if (!_checkPreamble)
-                byteLen = 0;
-            do {
-                if (_checkPreamble) {
-                    Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
-                    int tmpBytePos = bytePos;
+            {
+                _byteLen = 0;
+            }
+            do
+            {
+                if (_checkPreamble)
+                {
+                    Debug.Assert(_bytePos <= _encoding.Preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
+                    int tmpBytePos = _bytePos;
                     int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false);
                     Debug.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
-                    
-                    if (len == 0) {
+
+                    if (len == 0)
+                    {
                         // EOF but we might have buffered bytes from previous 
                         // attempt to detect preamble that needs to be decoded now
-                        if (byteLen > 0)
+                        if (_byteLen > 0)
                         {
-                            charLen += decoder.GetChars(tmpByteBuffer, 0, byteLen, charBuffer, charLen);
+                            _charLen += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, _charLen);
                             // Need to zero out the _byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
-                            bytePos = 0; byteLen = 0;
+                            _bytePos = 0; _byteLen = 0;
                         }
-                        
-                        return charLen;
+
+                        return _charLen;
                     }
-                    
-                    byteLen += len;
+
+                    _byteLen += len;
                 }
-                else {
-                    Debug.Assert(bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
-                    byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
-                    Debug.Assert(byteLen >= 0, "Stream.Read returned a negative number!  Bug in stream class.");
-                    
-                    if (byteLen == 0)  // We're at EOF
-                        return charLen;
+                else
+                {
+                    Debug.Assert(_bytePos == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
+                    _byteLen = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
+                    Debug.Assert(_byteLen >= 0, "Stream.Read returned a negative number!  Bug in stream class.");
+
+                    if (_byteLen == 0)  // We're at EOF
+                    {
+                        return _charLen;
+                    }
                 }
 
                 // _isBlocked == whether we read fewer bytes than we asked for.
                 // Note we must check it here because CompressBuffer or 
                 // DetectEncoding will change _byteLen.
-                _isBlocked = (byteLen < tmpByteBuffer.Length);
-                
+                _isBlocked = (_byteLen < tmpByteBuffer.Length);
+
                 // Check for preamble before detect encoding. This is not to override the
-                // user suppplied Encoding for the one we implicitly detect. The user could
+                // user supplied Encoding for the one we implicitly detect. The user could
                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
-                if (IsPreamble()) 
+                if (IsPreamble())
+                {
                     continue;
+                }
 
                 // If we're supposed to detect the encoding and haven't done so yet,
                 // do it.  Note this may need to be called more than once.
-                if (_detectEncoding && byteLen >= 2)
+                if (_detectEncoding && _byteLen >= 2)
+                {
                     DetectEncoding();
+                }
 
-                charLen += decoder.GetChars(tmpByteBuffer, 0, byteLen, charBuffer, charLen);
-            } while (charLen == 0);
-            
-            return charLen;
+                _charLen += _decoder.GetChars(tmpByteBuffer, 0, _byteLen, _charBuffer, _charLen);
+            } while (_charLen == 0);
+
+            return _charLen;
         }
-        #endregion
+#endregion
+
 
+        // No data, class doesn't need to be serializable.
         // Note this class is threadsafe.
         private class NullStreamReader : StreamReader
         {
             // Instantiating Encoding causes unnecessary perf hit. 
-            internal NullStreamReader() {
+            internal NullStreamReader()
+            {
                 Init(Stream.Null);
             }
 
-            public override Stream BaseStream {
+            public override Stream BaseStream
+            {
                 get { return Stream.Null; }
             }
 
-            public override Encoding CurrentEncoding {
+            public override Encoding CurrentEncoding
+            {
                 get { return Encoding.Unicode; }
             }
 
@@ -1165,17 +1397,25 @@ namespace System.IO
                 return -1;
             }
 
-            public override int Read(char[] buffer, int index, int count) {
+            [SuppressMessage("Microsoft.Contracts", "CC1055")]  // Skip extra error checking to avoid *potential* AppCompat problems.
+            public override int Read(char[] buffer, int index, int count)
+            {
                 return 0;
             }
-            
-            public override String ReadLine() {
+
+            public override string ReadLine()
+            {
                 return null;
             }
 
-            public override String ReadToEnd()
+            public override string ReadToEnd()
             {
-                return String.Empty;
+                return string.Empty;
+            }
+
+            internal override int ReadBuffer()
+            {
+                return 0;
             }
         }
     }
diff --git a/src/mscorlib/shared/System/IO/StreamWriter.cs b/src/mscorlib/shared/System/IO/StreamWriter.cs
new file mode 100644 (file)
index 0000000..5826f91
--- /dev/null
@@ -0,0 +1,986 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO
+{
+    // This class implements a TextWriter for writing characters to a Stream.
+    // This is designed for character output in a particular Encoding, 
+    // whereas the Stream class is designed for byte input and output.  
+    public class StreamWriter : TextWriter
+    {
+        // For UTF-8, the values of 1K for the default buffer size and 4K for the
+        // file stream buffer size are reasonable & give very reasonable
+        // performance for in terms of construction time for the StreamWriter and
+        // write perf.  Note that for UTF-8, we end up allocating a 4K byte buffer,
+        // which means we take advantage of adaptive buffering code.
+        // The performance using UnicodeEncoding is acceptable.  
+        private const int DefaultBufferSize = 1024;   // char[]
+        private const int DefaultFileStreamBufferSize = 4096;
+        private const int MinBufferSize = 128;
+
+        private const int DontCopyOnWriteLineThreshold = 512;
+
+        // Bit bucket - Null has no backing store. Non closable.
+        public new static readonly StreamWriter Null = new StreamWriter(Stream.Null, UTF8NoBOM, MinBufferSize, true);
+
+        private Stream _stream;
+        private Encoding _encoding;
+        private Encoder _encoder;
+        private byte[] _byteBuffer;
+        private char[] _charBuffer;
+        private int _charPos;
+        private int _charLen;
+        private bool _autoFlush;
+        private bool _haveWrittenPreamble;
+        private bool _closable;
+
+        // We don't guarantee thread safety on StreamWriter, but we should at 
+        // least prevent users from trying to write anything while an Async
+        // write from the same thread is in progress.
+        private volatile Task _asyncWriteTask;
+
+        private void CheckAsyncTaskInProgress()
+        {
+            // We are not locking the access to _asyncWriteTask because this is not meant to guarantee thread safety. 
+            // We are simply trying to deter calling any Write APIs while an async Write from the same thread is in progress.
+
+            Task t = _asyncWriteTask;
+
+            if (t != null && !t.IsCompleted)
+                throw new InvalidOperationException(SR.InvalidOperation_AsyncIOInProgress);
+        }
+
+        // The high level goal is to be tolerant of encoding errors when we read and very strict 
+        // when we write. Hence, default StreamWriter encoding will throw on encoding error.   
+        // Note: when StreamWriter throws on invalid encoding chars (for ex, high surrogate character 
+        // D800-DBFF without a following low surrogate character DC00-DFFF), it will cause the 
+        // internal StreamWriter's state to be irrecoverable as it would have buffered the 
+        // illegal chars and any subsequent call to Flush() would hit the encoding error again. 
+        // Even Close() will hit the exception as it would try to flush the unwritten data. 
+        // Maybe we can add a DiscardBufferedData() method to get out of such situation (like 
+        // StreamReader though for different reason). Either way, the buffered data will be lost!
+        private static Encoding UTF8NoBOM => EncodingCache.UTF8NoBOM;
+
+
+        internal StreamWriter() : base(null)
+        { // Ask for CurrentCulture all the time 
+        }
+
+        public StreamWriter(Stream stream)
+            : this(stream, UTF8NoBOM, DefaultBufferSize, false)
+        {
+        }
+
+        public StreamWriter(Stream stream, Encoding encoding)
+            : this(stream, encoding, DefaultBufferSize, false)
+        {
+        }
+
+        // Creates a new StreamWriter for the given stream.  The 
+        // character encoding is set by encoding and the buffer size, 
+        // in number of 16-bit characters, is set by bufferSize.  
+        // 
+        public StreamWriter(Stream stream, Encoding encoding, int bufferSize)
+            : this(stream, encoding, bufferSize, false)
+        {
+        }
+
+        public StreamWriter(Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)
+            : base(null) // Ask for CurrentCulture all the time
+        {
+            if (stream == null || encoding == null)
+            {
+                throw new ArgumentNullException(stream == null ? nameof(stream) : nameof(encoding));
+            }
+            if (!stream.CanWrite)
+            {
+                throw new ArgumentException(SR.Argument_StreamNotWritable);
+            }
+            if (bufferSize <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+            }
+
+            Init(stream, encoding, bufferSize, leaveOpen);
+        }
+
+        public StreamWriter(string path)
+            : this(path, false, UTF8NoBOM, DefaultBufferSize)
+        {
+        }
+
+        public StreamWriter(string path, bool append)
+            : this(path, append, UTF8NoBOM, DefaultBufferSize)
+        {
+        }
+
+        public StreamWriter(string path, bool append, Encoding encoding)
+            : this(path, append, encoding, DefaultBufferSize)
+        {
+        }
+
+        public StreamWriter(string path, bool append, Encoding encoding, int bufferSize)
+        { 
+            if (path == null)
+                throw new ArgumentNullException(nameof(path));
+            if (encoding == null)
+                throw new ArgumentNullException(nameof(encoding));
+            if (path.Length == 0)
+                throw new ArgumentException(SR.Argument_EmptyPath);
+            if (bufferSize <= 0)
+                throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+
+            Stream stream = new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read,
+                DefaultFileStreamBufferSize, FileOptions.SequentialScan);
+            Init(stream, encoding, bufferSize, shouldLeaveOpen: false);
+        }
+
+        private void Init(Stream streamArg, Encoding encodingArg, int bufferSize, bool shouldLeaveOpen)
+        {
+            _stream = streamArg;
+            _encoding = encodingArg;
+            _encoder = _encoding.GetEncoder();
+            if (bufferSize < MinBufferSize)
+            {
+                bufferSize = MinBufferSize;
+            }
+
+            _charBuffer = new char[bufferSize];
+            _byteBuffer = new byte[_encoding.GetMaxByteCount(bufferSize)];
+            _charLen = bufferSize;
+            // If we're appending to a Stream that already has data, don't write
+            // the preamble.
+            if (_stream.CanSeek && _stream.Position > 0)
+            {
+                _haveWrittenPreamble = true;
+            }
+
+            _closable = !shouldLeaveOpen;
+        }
+
+        public override void Close()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            try
+            {
+                // We need to flush any buffered data if we are being closed/disposed.
+                // Also, we never close the handles for stdout & friends.  So we can safely 
+                // write any buffered data to those streams even during finalization, which 
+                // is generally the right thing to do.
+                if (_stream != null)
+                {
+                    // Note: flush on the underlying stream can throw (ex., low disk space)
+                    if (disposing /* || (LeaveOpen && stream is __ConsoleStream) */)
+                    {
+                        CheckAsyncTaskInProgress();
+
+                        Flush(true, true);
+                    }
+                }
+            }
+            finally
+            {
+                // Dispose of our resources if this StreamWriter is closable. 
+                // Note: Console.Out and other such non closable streamwriters should be left alone 
+                if (!LeaveOpen && _stream != null)
+                {
+                    try
+                    {
+                        // Attempt to close the stream even if there was an IO error from Flushing.
+                        // Note that Stream.Close() can potentially throw here (may or may not be
+                        // due to the same Flush error). In this case, we still need to ensure 
+                        // cleaning up internal resources, hence the finally block.  
+                        if (disposing)
+                        {
+                            _stream.Close();
+                        }
+                    }
+                    finally
+                    {
+                        _stream = null;
+                        _byteBuffer = null;
+                        _charBuffer = null;
+                        _encoding = null;
+                        _encoder = null;
+                        _charLen = 0;
+                        base.Dispose(disposing);
+                    }
+                }
+            }
+        }
+
+        public override void Flush()
+        {
+            CheckAsyncTaskInProgress();
+
+            Flush(true, true);
+        }
+
+        private void Flush(bool flushStream, bool flushEncoder)
+        {
+            // flushEncoder should be true at the end of the file and if
+            // the user explicitly calls Flush (though not if AutoFlush is true).
+            // This is required to flush any dangling characters from our UTF-7 
+            // and UTF-8 encoders.  
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            // Perf boost for Flush on non-dirty writers.
+            if (_charPos == 0 && !flushStream && !flushEncoder)
+            {
+                return;
+            }
+
+            if (!_haveWrittenPreamble)
+            {
+                _haveWrittenPreamble = true;
+                ReadOnlySpan<byte> preamble = _encoding.Preamble;
+                if (preamble.Length > 0)
+                {
+                    _stream.Write(preamble);
+                }
+            }
+
+            int count = _encoder.GetBytes(_charBuffer, 0, _charPos, _byteBuffer, 0, flushEncoder);
+            _charPos = 0;
+            if (count > 0)
+            {
+                _stream.Write(_byteBuffer, 0, count);
+            }
+            // By definition, calling Flush should flush the stream, but this is
+            // only necessary if we passed in true for flushStream.  The Web
+            // Services guys have some perf tests where flushing needlessly hurts.
+            if (flushStream)
+            {
+                _stream.Flush();
+            }
+        }
+
+        public virtual bool AutoFlush
+        {
+            get { return _autoFlush; }
+
+            set
+            {
+                CheckAsyncTaskInProgress();
+
+                _autoFlush = value;
+                if (value)
+                {
+                    Flush(true, false);
+                }
+            }
+        }
+
+        public virtual Stream BaseStream
+        {
+            get { return _stream; }
+        }
+
+        internal bool LeaveOpen
+        {
+            get { return !_closable; }
+        }
+
+        internal bool HaveWrittenPreamble
+        {
+            set { _haveWrittenPreamble = value; }
+        }
+
+        public override Encoding Encoding
+        {
+            get { return _encoding; }
+        }
+
+        public override void Write(char value)
+        {
+            CheckAsyncTaskInProgress();
+
+            if (_charPos == _charLen)
+            {
+                Flush(false, false);
+            }
+
+            _charBuffer[_charPos] = value;
+            _charPos++;
+            if (_autoFlush)
+            {
+                Flush(true, false);
+            }
+        }
+
+        public override void Write(char[] buffer)
+        {
+            if (buffer != null)
+            {
+                WriteCore(buffer, _autoFlush);
+            }
+        }
+
+        public override void Write(char[] buffer, int index, int count)
+        {
+            if (buffer == null)
+            {
+                throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
+            if (index < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (buffer.Length - index < count)
+            {
+                throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
+
+            WriteCore(new ReadOnlySpan<char>(buffer, index, count), _autoFlush);
+        }
+
+        public override void Write(ReadOnlySpan<char> buffer)
+        {
+            if (GetType() == typeof(StreamWriter))
+            {
+                WriteCore(buffer, _autoFlush);
+            }
+            else
+            {
+                // If a derived class may have overridden existing Write behavior,
+                // we need to make sure we use it.
+                base.Write(buffer);
+            }
+        }
+
+        private unsafe void WriteCore(ReadOnlySpan<char> buffer, bool autoFlush)
+        {
+            CheckAsyncTaskInProgress();
+
+            if (buffer.Length <= 4 && // Threshold of 4 chosen based on perf experimentation
+                buffer.Length <= _charLen - _charPos)
+            {
+                // For very short buffers and when we don't need to worry about running out of space
+                // in the char buffer, just copy the chars individually.
+                for (int i = 0; i < buffer.Length; i++)
+                {
+                    _charBuffer[_charPos++] = buffer[i];
+                }
+            }
+            else
+            {
+                // For larger buffers or when we may run out of room in the internal char buffer, copy in chunks.
+                // Use unsafe code until https://github.com/dotnet/coreclr/issues/13827 is addressed, as spans are
+                // resulting in significant overhead (even when the if branch above is taken rather than this
+                // else) due to temporaries that need to be cleared.  Given the use of unsafe code, we also
+                // make local copies of instance state to protect against potential concurrent misuse.
+
+                char[] charBuffer = _charBuffer;
+                if (charBuffer == null)
+                {
+                    throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+                }
+
+                fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer))
+                fixed (char* dstPtr = &charBuffer[0])
+                {
+                    char* srcPtr = bufferPtr;
+                    int count = buffer.Length;
+                    int dstPos = _charPos; // use a local copy of _charPos for safety
+                    while (count > 0)
+                    {
+                        if (dstPos == charBuffer.Length)
+                        {
+                            Flush(false, false);
+                            dstPos = 0;
+                        }
+
+                        int n = Math.Min(charBuffer.Length - dstPos, count);
+                        int bytesToCopy = n * sizeof(char);
+
+                        Buffer.MemoryCopy(srcPtr, dstPtr + dstPos, bytesToCopy, bytesToCopy);
+
+                        _charPos += n;
+                        dstPos += n;
+                        srcPtr += n;
+                        count -= n;
+                    }
+                }
+            }
+
+            if (autoFlush)
+            {
+                Flush(true, false);
+            }
+        }
+
+        public override void Write(string value)
+        {
+            if (value != null)
+            {
+                WriteCore(value.AsReadOnlySpan(), _autoFlush);
+            }
+        }
+
+        //
+        // Optimize the most commonly used WriteLine overload. This optimization is important for System.Console in particular
+        // because of it will make one WriteLine equal to one call to the OS instead of two in the common case.
+        //
+        public override void WriteLine(string value)
+        {
+            CheckAsyncTaskInProgress();
+            if (value != null)
+            {
+                WriteCore(value.AsReadOnlySpan(), autoFlush: false);
+            }
+            WriteCore(new ReadOnlySpan<char>(CoreNewLine), autoFlush: true);
+        }
+
+        public override void WriteLine(ReadOnlySpan<char> value)
+        {
+            if (GetType() == typeof(StreamWriter))
+            {
+                CheckAsyncTaskInProgress();
+                WriteCore(value, autoFlush: false);
+                WriteCore(new ReadOnlySpan<char>(CoreNewLine), autoFlush: true);
+            }
+            else
+            {
+                // If a derived class may have overridden existing WriteLine behavior,
+                // we need to make sure we use it.
+                base.WriteLine(value);
+            }
+        }
+
+        #region Task based Async APIs
+        public override Task WriteAsync(char value)
+        {
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteAsync(value);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+        // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+        // to ensure performant access inside the state machine that corresponds this async method.
+        // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
+        private static async Task WriteAsyncInternal(StreamWriter _this, char value,
+                                                     char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
+                                                     bool autoFlush, bool appendNewLine)
+        {
+            if (charPos == charLen)
+            {
+                await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+                Debug.Assert(_this._charPos == 0);
+                charPos = 0;
+            }
+
+            charBuffer[charPos] = value;
+            charPos++;
+
+            if (appendNewLine)
+            {
+                for (int i = 0; i < coreNewLine.Length; i++)   // Expect 2 iterations, no point calling BlockCopy
+                {
+                    if (charPos == charLen)
+                    {
+                        await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+                        Debug.Assert(_this._charPos == 0);
+                        charPos = 0;
+                    }
+
+                    charBuffer[charPos] = coreNewLine[i];
+                    charPos++;
+                }
+            }
+
+            if (autoFlush)
+            {
+                await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
+                Debug.Assert(_this._charPos == 0);
+                charPos = 0;
+            }
+
+            _this.CharPos_Prop = charPos;
+        }
+
+        public override Task WriteAsync(string value)
+        {
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteAsync(value);
+            }
+
+            if (value != null)
+            {
+                if (_stream == null)
+                {
+                    throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+                }
+
+                CheckAsyncTaskInProgress();
+
+                Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false);
+                _asyncWriteTask = task;
+
+                return task;
+            }
+            else
+            {
+                return Task.CompletedTask;
+            }
+        }
+
+        // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+        // to ensure performant access inside the state machine that corresponds this async method.
+        // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
+        private static async Task WriteAsyncInternal(StreamWriter _this, string value,
+                                                     char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
+                                                     bool autoFlush, bool appendNewLine)
+        {
+            Debug.Assert(value != null);
+
+            int count = value.Length;
+            int index = 0;
+
+            while (count > 0)
+            {
+                if (charPos == charLen)
+                {
+                    await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+                    Debug.Assert(_this._charPos == 0);
+                    charPos = 0;
+                }
+
+                int n = charLen - charPos;
+                if (n > count)
+                {
+                    n = count;
+                }
+
+                Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress!  This is most likely a race condition in user code.");
+
+                value.CopyTo(index, charBuffer, charPos, n);
+
+                charPos += n;
+                index += n;
+                count -= n;
+            }
+
+            if (appendNewLine)
+            {
+                for (int i = 0; i < coreNewLine.Length; i++)   // Expect 2 iterations, no point calling BlockCopy
+                {
+                    if (charPos == charLen)
+                    {
+                        await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
+                        Debug.Assert(_this._charPos == 0);
+                        charPos = 0;
+                    }
+
+                    charBuffer[charPos] = coreNewLine[i];
+                    charPos++;
+                }
+            }
+
+            if (autoFlush)
+            {
+                await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
+                Debug.Assert(_this._charPos == 0);
+                charPos = 0;
+            }
+
+            _this.CharPos_Prop = charPos;
+        }
+
+        public override Task WriteAsync(char[] buffer, int index, int count)
+        {
+            if (buffer == null)
+            {
+                throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
+            if (index < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (buffer.Length - index < count)
+            {
+                throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
+
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteAsync(buffer, index, count);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: default);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+        public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
+        {
+            if (GetType() != typeof(StreamWriter))
+            {
+                // If a derived type may have overridden existing WriteASync(char[], ...) behavior, make sure we use it.
+                return base.WriteAsync(buffer, cancellationToken);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return Task.FromCanceled(cancellationToken);
+            }
+
+            Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: false, cancellationToken: cancellationToken);
+            _asyncWriteTask = task;
+            return task;
+        }
+
+        // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+        // to ensure performant access inside the state machine that corresponds this async method.
+        // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
+        private static async Task WriteAsyncInternal(StreamWriter _this, ReadOnlyMemory<char> source,
+                                                     char[] charBuffer, int charPos, int charLen, char[] coreNewLine,
+                                                     bool autoFlush, bool appendNewLine, CancellationToken cancellationToken)
+        {
+            int copied = 0;
+            while (copied < source.Length)
+            {
+                if (charPos == charLen)
+                {
+                    await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
+                    Debug.Assert(_this._charPos == 0);
+                    charPos = 0;
+                }
+
+                int n = Math.Min(charLen - charPos, source.Length - copied);
+                Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress!  This is most likely a race condition in user code.");
+
+                source.Span.Slice(copied, n).CopyTo(new Span<char>(charBuffer, charPos, n));
+                charPos += n;
+                copied += n;
+            }
+
+            if (appendNewLine)
+            {
+                for (int i = 0; i < coreNewLine.Length; i++)   // Expect 2 iterations, no point calling BlockCopy
+                {
+                    if (charPos == charLen)
+                    {
+                        await _this.FlushAsyncInternal(false, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
+                        Debug.Assert(_this._charPos == 0);
+                        charPos = 0;
+                    }
+
+                    charBuffer[charPos] = coreNewLine[i];
+                    charPos++;
+                }
+            }
+
+            if (autoFlush)
+            {
+                await _this.FlushAsyncInternal(true, false, charBuffer, charPos, cancellationToken).ConfigureAwait(false);
+                Debug.Assert(_this._charPos == 0);
+                charPos = 0;
+            }
+
+            _this.CharPos_Prop = charPos;
+        }
+
+        public override Task WriteLineAsync()
+        {
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteLineAsync();
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = WriteAsyncInternal(this, ReadOnlyMemory<char>.Empty, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+
+        public override Task WriteLineAsync(char value)
+        {
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteLineAsync(value);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+
+        public override Task WriteLineAsync(string value)
+        {
+            if (value == null)
+            {
+                return WriteLineAsync();
+            }
+
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteLineAsync(value);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = WriteAsyncInternal(this, value, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+
+        public override Task WriteLineAsync(char[] buffer, int index, int count)
+        {
+            if (buffer == null)
+            {
+                throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
+            if (index < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (buffer.Length - index < count)
+            {
+                throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
+
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Write() which a subclass might have overridden.  
+            // To be safe we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Write) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteLineAsync(buffer, index, count);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = WriteAsyncInternal(this, new ReadOnlyMemory<char>(buffer, index, count), _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: default);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+        public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
+        {
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.WriteLineAsync(buffer, cancellationToken);
+            }
+
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return Task.FromCanceled(cancellationToken);
+            }
+
+            Task task = WriteAsyncInternal(this, buffer, _charBuffer, _charPos, _charLen, CoreNewLine, _autoFlush, appendNewLine: true, cancellationToken: cancellationToken);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+
+        public override Task FlushAsync()
+        {
+            // If we have been inherited into a subclass, the following implementation could be incorrect
+            // since it does not call through to Flush() which a subclass might have overridden.  To be safe 
+            // we will only use this implementation in cases where we know it is safe to do so,
+            // and delegate to our base class (which will call into Flush) when we are not sure.
+            if (GetType() != typeof(StreamWriter))
+            {
+                return base.FlushAsync();
+            }
+
+            // flushEncoder should be true at the end of the file and if
+            // the user explicitly calls Flush (though not if AutoFlush is true).
+            // This is required to flush any dangling characters from our UTF-7 
+            // and UTF-8 encoders.  
+            if (_stream == null)
+            {
+                throw new ObjectDisposedException(null, SR.ObjectDisposed_WriterClosed);
+            }
+
+            CheckAsyncTaskInProgress();
+
+            Task task = FlushAsyncInternal(true, true, _charBuffer, _charPos);
+            _asyncWriteTask = task;
+
+            return task;
+        }
+
+        private int CharPos_Prop
+        {
+            set { _charPos = value; }
+        }
+
+        private bool HaveWrittenPreamble_Prop
+        {
+            set { _haveWrittenPreamble = value; }
+        }
+
+        private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
+                                        char[] sCharBuffer, int sCharPos, CancellationToken cancellationToken = default)
+        {
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return Task.FromCanceled(cancellationToken);
+            }
+
+            // Perf boost for Flush on non-dirty writers.
+            if (sCharPos == 0 && !flushStream && !flushEncoder)
+            {
+                return Task.CompletedTask;
+            }
+
+            Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, _haveWrittenPreamble,
+                                                _encoding, _encoder, _byteBuffer, _stream, cancellationToken);
+
+            _charPos = 0;
+            return flushTask;
+        }
+
+
+        // We pass in private instance fields of this MarshalByRefObject-derived type as local params
+        // to ensure performant access inside the state machine that corresponds this async method.
+        private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder,
+                                                     char[] charBuffer, int charPos, bool haveWrittenPreamble,
+                                                     Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream, CancellationToken cancellationToken)
+        {
+            if (!haveWrittenPreamble)
+            {
+                _this.HaveWrittenPreamble_Prop = true;
+                byte[] preamble = encoding.GetPreamble();
+                if (preamble.Length > 0)
+                {
+                    await stream.WriteAsync(preamble, 0, preamble.Length, cancellationToken).ConfigureAwait(false);
+                }
+            }
+
+            int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
+            if (count > 0)
+            {
+                await stream.WriteAsync(byteBuffer, 0, count, cancellationToken).ConfigureAwait(false);
+            }
+
+            // By definition, calling Flush should flush the stream, but this is
+            // only necessary if we passed in true for flushStream.  The Web
+            // Services guys have some perf tests where flushing needlessly hurts.
+            if (flushStream)
+            {
+                await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
+            }
+        }
+        #endregion
+    }  // class StreamWriter
+}  // namespace
similarity index 51%
rename from src/mscorlib/src/System/IO/TextReader.cs
rename to src/mscorlib/shared/System/IO/TextReader.cs
index 868d08a..c4727cd 100644 (file)
@@ -2,27 +2,15 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-/*============================================================
-**
-** 
-** 
-**
-**
-** Purpose: Abstract base class for all Text-only Readers.
-** Subclasses will include StreamReader & StringReader.
-**
-**
-===========================================================*/
-
 using System.Text;
-using System.Runtime.InteropServices;
-using System.Runtime.CompilerServices;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
 using System.Threading;
 using System.Threading.Tasks;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Buffers;
 
-namespace System.IO {
+namespace System.IO
+{
     // This abstract base class represents a reader that can read a sequential
     // stream of characters.  This is not intended for reading bytes -
     // there are methods on the Stream class to read bytes.
@@ -30,18 +18,13 @@ namespace System.IO {
     //
     // This class is intended for character input, not bytes.  
     // There are methods on the Stream class for reading bytes. 
-    internal abstract class TextReader : MarshalByRefObject, IDisposable {
+    public abstract partial class TextReader : MarshalByRefObject, IDisposable
+    {
         public static readonly TextReader Null = new NullTextReader();
-    
-        protected TextReader() {}
-    
-        // Closes this TextReader and releases any system resources associated with the
-        // TextReader. Following a call to Close, any operations on the TextReader
-        // may raise exceptions.
-        // 
-        // This default method is empty, but descendant classes can override the
-        // method to provide the appropriate functionality.
-        public virtual void Close() 
+
+        protected TextReader() { }
+
+        public virtual void Close()
         {
             Dispose(true);
             GC.SuppressFinalize(this);
@@ -64,11 +47,11 @@ namespace System.IO {
         // 
         // This default method simply returns -1.
         //
-        public virtual int Peek() 
+        public virtual int Peek()
         {
             return -1;
         }
-    
+
         // Reads the next character from the input stream. The returned value is
         // -1 if no further characters are available.
         // 
@@ -78,40 +61,74 @@ namespace System.IO {
         {
             return -1;
         }
-    
+
         // Reads a block of characters. This method will read up to
         // count characters from this TextReader into the
         // buffer character array starting at position
         // index. Returns the actual number of characters read.
         //
-        public virtual int Read(char[] buffer, int index, int count) 
+        public virtual int Read(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0)
+            {
                 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (count < 0)
+            {
                 throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
-    
-            int n = 0;
-            do {
+            }
+
+            int n;
+            for (n = 0; n < count; n++)
+            {
                 int ch = Read();
                 if (ch == -1) break;
-                buffer[index + n++] = (char)ch;
-            } while (n < count);
+                buffer[index + n] = (char)ch;
+            }
+            
             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<char> buffer)
+        {
+            char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+            try
+            {
+                int numRead = Read(array, 0, buffer.Length);
+                if ((uint)numRead > buffer.Length)
+                {
+                    throw new IOException(SR.IO_InvalidReadLength);
+                }
+                new Span<char>(array, 0, numRead).CopyTo(buffer);
+                return numRead;
+            }
+            finally
+            {
+                ArrayPool<char>.Shared.Return(array);
+            }
+        }
+
         // Reads all characters from the current position to the end of the 
         // TextReader, and returns them as one string.
-        public virtual String ReadToEnd()
+        public virtual string ReadToEnd()
         {
             char[] chars = new char[4096];
             int len;
             StringBuilder sb = new StringBuilder(4096);
-            while((len=Read(chars, 0, chars.Length)) != 0) 
+            while ((len = Read(chars, 0, chars.Length)) != 0)
             {
                 sb.Append(chars, 0, len);
             }
@@ -121,40 +138,73 @@ namespace System.IO {
         // Blocking version of read.  Returns only when count
         // characters have been read or the end of the file was reached.
         // 
-        public virtual int ReadBlock(char[] buffer, int index, int count) 
+        public virtual int ReadBlock(char[] buffer, int index, int count)
         {
             int i, n = 0;
-            do {
+            do
+            {
                 n += (i = Read(buffer, index + n, count - n));
             } while (i > 0 && n < 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<char> buffer)
+        {
+            char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+            try
+            {
+                int numRead = ReadBlock(array, 0, buffer.Length);
+                if ((uint)numRead > buffer.Length)
+                {
+                    throw new IOException(SR.IO_InvalidReadLength);
+                }
+                new Span<char>(array, 0, numRead).CopyTo(buffer);
+                return numRead;
+            }
+            finally
+            {
+                ArrayPool<char>.Shared.Return(array);
+            }
+        }
+
         // 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
         // contain the terminating carriage return and/or line feed. The returned
         // value is null if the end of the input stream has been reached.
         //
-        public virtual String ReadLine() 
+        public virtual string ReadLine()
         {
             StringBuilder sb = new StringBuilder();
-            while (true) {
+            while (true)
+            {
                 int ch = Read();
                 if (ch == -1) break;
-                if (ch == '\r' || ch == '\n') 
+                if (ch == '\r' || ch == '\n')
                 {
-                    if (ch == '\r' && Peek() == '\n') Read();
+                    if (ch == '\r' && Peek() == '\n')
+                    {
+                        Read();
+                    }
+
                     return sb.ToString();
                 }
                 sb.Append((char)ch);
             }
-            if (sb.Length > 0) return sb.ToString();
+            if (sb.Length > 0)
+            {
+                return sb.ToString();
+            }
+
             return null;
         }
 
         #region Task based Async APIs
-        public virtual Task<String> ReadLineAsync()
+        public virtual Task<string> ReadLineAsync()
         {
             return Task<String>.Factory.StartNew(state =>
             {
@@ -163,197 +213,191 @@ namespace System.IO {
             this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
         }
 
-        public async virtual Task<String> ReadToEndAsync()
+        public async virtual Task<string> ReadToEndAsync()
         {
-            char[] chars = new char[4096];
-            int len;
-            StringBuilder sb = new StringBuilder(4096);
-            while((len = await ReadAsyncInternal(chars, 0, chars.Length).ConfigureAwait(false)) != 0) 
+            var sb = new StringBuilder(4096);
+            char[] chars = ArrayPool<char>.Shared.Rent(4096);
+            try
             {
-                sb.Append(chars, 0, len);
+                int len;
+                while ((len = await ReadAsyncInternal(chars, default).ConfigureAwait(false)) != 0)
+                {
+                    sb.Append(chars, 0, len);
+                }
+            }
+            finally
+            {
+                ArrayPool<char>.Shared.Return(chars);
             }
             return sb.ToString();
         }
 
         public virtual Task<int> ReadAsync(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0 || count < 0)
-                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            {
+                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index): nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
 
-            return ReadAsyncInternal(buffer, index, count);
+            return ReadAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
         }
 
-        internal virtual Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
-        {
-            Debug.Assert(buffer != null);
-            Debug.Assert(index >= 0);
-            Debug.Assert(count >= 0);
-            Debug.Assert(buffer.Length - index >= count);
+        public virtual ValueTask<int> ReadAsync(Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+            new ValueTask<int>(buffer.TryGetArray(out ArraySegment<char> array) ?
+                ReadAsync(array.Array, array.Offset, array.Count) :
+                Task<int>.Factory.StartNew(state =>
+                {
+                    var t = (Tuple<TextReader, Memory<char>>)state;
+                    return t.Item1.Read(t.Item2.Span);
+                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
 
-            var tuple = new Tuple<TextReader, char[], int, int>(this, buffer, index, count);
-            return Task<int>.Factory.StartNew(state =>
+        internal virtual ValueTask<int> ReadAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
+        {
+            var tuple = new Tuple<TextReader, Memory<char>>(this, buffer);
+            return new ValueTask<int>(Task<int>.Factory.StartNew(state =>
             {
-                var t = (Tuple<TextReader, char[], int, int>)state;
-                return t.Item1.Read(t.Item2, t.Item3, t.Item4);
+                var t = (Tuple<TextReader, Memory<char>>)state;
+                return t.Item1.Read(t.Item2.Span);
             },
-            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+            tuple, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
         }
 
         public virtual Task<int> ReadBlockAsync(char[] buffer, int index, int count)
         {
-            if (buffer==null)
+            if (buffer == null)
+            {
                 throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
             if (index < 0 || count < 0)
-                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            {
+                throw new ArgumentOutOfRangeException((index < 0 ? nameof(index): nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
             if (buffer.Length - index < count)
+            {
                 throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
 
+            return ReadBlockAsyncInternal(new Memory<char>(buffer, index, count), default).AsTask();
+        }
 
-            return ReadBlockAsyncInternal(buffer, index, count);
-         }
+        public virtual ValueTask<int> ReadBlockAsync(Memory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+            new ValueTask<int>(buffer.TryGetArray(out ArraySegment<char> array) ?
+                ReadBlockAsync(array.Array, array.Offset, array.Count) :
+                Task<int>.Factory.StartNew(state =>
+                {
+                    var t = (Tuple<TextReader, Memory<char>>)state;
+                    return t.Item1.ReadBlock(t.Item2.Span);
+                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
 
-        private async Task<int> ReadBlockAsyncInternal(char[] buffer, int index, int count)
+        internal async ValueTask<int> ReadBlockAsyncInternal(Memory<char> buffer, CancellationToken cancellationToken)
         {
-            Debug.Assert(buffer != null);
-            Debug.Assert(index >= 0);
-            Debug.Assert(count >= 0);
-            Debug.Assert(buffer.Length - index >= count);
-
-            int i, n = 0;
+            int n = 0, i;
             do
             {
-                i = await ReadAsyncInternal(buffer, index + n, count - n).ConfigureAwait(false);
+                i = await ReadAsyncInternal(buffer.Slice(n), cancellationToken).ConfigureAwait(false);
                 n += i;
-            } while (i > 0 && n < count);
+            } while (i > 0 && n < buffer.Length);
 
             return n;
         }
         #endregion
 
-        public static TextReader Synchronized(TextReader reader) 
-        {
-            if (reader==null)
-                throw new ArgumentNullException(nameof(reader));
-
-            if (reader is SyncTextReader)
-                return reader;
-            
-            return new SyncTextReader(reader);
-        }
-        
         private sealed class NullTextReader : TextReader
         {
-            public NullTextReader(){}
+            public NullTextReader() { }
 
-            public override int Read(char[] buffer, int index, int count) 
+            public override int Read(char[] buffer, int index, int count)
             {
                 return 0;
             }
-            
-            public override String ReadLine() 
+
+            public override string ReadLine()
             {
                 return null;
             }
         }
-        
-        internal sealed class SyncTextReader : TextReader 
+
+        public static TextReader Synchronized(TextReader reader)
         {
-            internal TextReader _in;
-            
-            internal SyncTextReader(TextReader t) 
-            {
-                _in = t;        
-            }
-            
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override void Close() 
+            if (reader == null)
+                throw new ArgumentNullException(nameof(reader));
+
+            return reader is SyncTextReader ? reader : new SyncTextReader(reader);
+        }
+
+        internal sealed class SyncTextReader : TextReader
+        {
+            internal readonly TextReader _in;
+
+            internal SyncTextReader(TextReader t)
             {
-                // So that any overriden Close() gets run
-                _in.Close();
+                _in = t;
             }
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            protected override void Dispose(bool disposing) 
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Close() => _in.Close();
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            protected override void Dispose(bool disposing)
             {
                 // Explicitly pick up a potentially methodimpl'ed Dispose
                 if (disposing)
                     ((IDisposable)_in).Dispose();
             }
-            
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override int Peek() 
-            {
-                return _in.Peek();
-            }
-            
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override int Read() 
-            {
-                return _in.Read();
-            }
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override int Read(char[] buffer, int index, int count) 
-            {
-                return _in.Read(buffer, index, count);
-            }
-            
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override int ReadBlock(char[] buffer, int index, int count) 
-            {
-                return _in.ReadBlock(buffer, index, count);
-            }
-            
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override String ReadLine() 
-            {
-                return _in.ReadLine();
-            }
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override int Peek() => _in.Peek();
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override String ReadToEnd() 
-            {
-                return _in.ReadToEnd();
-            }
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override int Read() => _in.Read();
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override int Read(char[] buffer, int index, int count) => _in.Read(buffer, index, count);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override int ReadBlock(char[] buffer, int index, int count) => _in.ReadBlock(buffer, index, count);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override string ReadLine() => _in.ReadLine();
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override string ReadToEnd() => _in.ReadToEnd();
 
             //
             // On SyncTextReader all APIs should run synchronously, even the async ones.
             //
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override Task<String> ReadLineAsync()
-            {
-                return Task.FromResult(ReadLine());
-            }
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task<string> ReadLineAsync() => Task.FromResult(ReadLine());
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
-            public override Task<String> ReadToEndAsync()
-            {
-                return Task.FromResult(ReadToEnd());
-            }
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task<string> ReadToEndAsync() => Task.FromResult(ReadToEnd());
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
+            [MethodImpl(MethodImplOptions.Synchronized)]
             public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
             {
-                if (buffer==null)
+                if (buffer == null)
                     throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
                 if (index < 0 || count < 0)
                     throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
                 if (buffer.Length - index < count)
                     throw new ArgumentException(SR.Argument_InvalidOffLen);
 
-
                 return Task.FromResult(ReadBlock(buffer, index, count));
             }
 
-            [MethodImplAttribute(MethodImplOptions.Synchronized)]
+            [MethodImpl(MethodImplOptions.Synchronized)]
             public override Task<int> ReadAsync(char[] buffer, int index, int count)
             {
-                if (buffer==null)
+                if (buffer == null)
                     throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
                 if (index < 0 || count < 0)
                     throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), SR.ArgumentOutOfRange_NeedNonNegNum);
diff --git a/src/mscorlib/shared/System/IO/TextWriter.cs b/src/mscorlib/shared/System/IO/TextWriter.cs
new file mode 100644 (file)
index 0000000..48e702b
--- /dev/null
@@ -0,0 +1,870 @@
+// 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;
+using System.Threading;
+using System.Globalization;
+using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Buffers;
+
+namespace System.IO
+{
+    // This abstract base class represents a writer that can write a sequential
+    // stream of characters. A subclass must minimally implement the 
+    // Write(char) method.
+    //
+    // This class is intended for character output, not bytes.  
+    // There are methods on the Stream class for writing bytes. 
+    public abstract partial class TextWriter : MarshalByRefObject, IDisposable
+    {
+        public static readonly TextWriter Null = new NullTextWriter();
+
+        // We don't want to allocate on every TextWriter creation, so cache the char array.  
+        private static readonly char[] s_coreNewLine = Environment.NewLine.ToCharArray();
+
+        /// <summary>
+        /// This is the 'NewLine' property expressed as a char[].   
+        /// It is exposed to subclasses as a protected field for read-only
+        /// purposes.  You should only modify it by using the 'NewLine' property.  
+        /// In particular you should never modify the elements of the array 
+        /// as they are shared among many instances of TextWriter.  
+        /// </summary>
+        protected char[] CoreNewLine = s_coreNewLine;
+        private string CoreNewLineStr = Environment.NewLine;
+
+        // Can be null - if so, ask for the Thread's CurrentCulture every time.
+        private IFormatProvider _internalFormatProvider;
+
+        protected TextWriter()
+        {
+            _internalFormatProvider = null;  // Ask for CurrentCulture all the time.
+        }
+
+        protected TextWriter(IFormatProvider formatProvider)
+        {
+            _internalFormatProvider = formatProvider;
+        }
+
+        public virtual IFormatProvider FormatProvider
+        {
+            get
+            {
+                if (_internalFormatProvider == null)
+                {
+                    return CultureInfo.CurrentCulture;
+                }
+                else
+                {
+                    return _internalFormatProvider;
+                }
+            }
+        }
+
+        public virtual void Close()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        // Clears all buffers for this TextWriter and causes any buffered data to be
+        // written to the underlying device. This default method is empty, but
+        // descendant classes can override the method to provide the appropriate
+        // functionality.
+        public virtual void Flush()
+        {
+        }
+
+        public abstract Encoding Encoding
+        {
+            get;
+        }
+
+        /// <summary>
+        /// Returns the line terminator string used by this TextWriter. The default line
+        /// terminator string is Environment.NewLine, which is platform specific. 
+        /// On Windows this is a carriage return followed by a line feed ("\r\n").
+        /// On OSX and Linux this is a line feed ("\n").
+        /// </summary>
+        /// <remarks>
+        /// The line terminator string is written to the text stream whenever one of the
+        /// WriteLine methods are called. In order for text written by
+        /// the TextWriter to be readable by a TextReader, only one of the following line
+        /// terminator strings should be used: "\r", "\n", or "\r\n".
+        /// </remarks>
+        public virtual string NewLine
+        {
+            get { return CoreNewLineStr; }
+            set
+            {
+                if (value == null)
+                {
+                    value = Environment.NewLine;
+                }
+
+                CoreNewLineStr = value;
+                CoreNewLine = value.ToCharArray();
+            }
+        }
+
+
+        // Writes a character to the text stream. This default method is empty,
+        // but descendant classes can override the method to provide the
+        // appropriate functionality.
+        //
+        public virtual void Write(char value)
+        {
+        }
+
+        // Writes a character array to the text stream. This default method calls
+        // Write(char) for each of the characters in the character array.
+        // If the character array is null, nothing is written.
+        //
+        public virtual void Write(char[] buffer)
+        {
+            if (buffer != null)
+            {
+                Write(buffer, 0, buffer.Length);
+            }
+        }
+
+        // Writes a range of a character array to the text stream. This method will
+        // write count characters of data into this TextWriter from the
+        // buffer character array starting at position index.
+        //
+        public virtual void Write(char[] buffer, int index, int count)
+        {
+            if (buffer == null)
+            {
+                throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+            }
+            if (index < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (count < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+            }
+            if (buffer.Length - index < count)
+            {
+                throw new ArgumentException(SR.Argument_InvalidOffLen);
+            }
+
+            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<char> buffer)
+        {
+            char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+            try
+            {
+                buffer.CopyTo(new Span<char>(array));
+                Write(array, 0, buffer.Length);
+            }
+            finally
+            {
+                ArrayPool<char>.Shared.Return(array);
+            }
+        }
+
+        // Writes the text representation of a boolean to the text stream. This
+        // method outputs either Boolean.TrueString or Boolean.FalseString.
+        //
+        public virtual void Write(bool value)
+        {
+            Write(value ? "True" : "False");
+        }
+
+        // Writes the text representation of an integer to the text stream. The
+        // text representation of the given value is produced by calling the
+        // Int32.ToString() method.
+        //
+        public virtual void Write(int value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        // Writes the text representation of an integer to the text stream. The
+        // text representation of the given value is produced by calling the
+        // UInt32.ToString() method.
+        //
+        [CLSCompliant(false)]
+        public virtual void Write(uint value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        // Writes the text representation of a long to the text stream. The
+        // text representation of the given value is produced by calling the
+        // Int64.ToString() method.
+        //
+        public virtual void Write(long value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        // Writes the text representation of an unsigned long to the text 
+        // stream. The text representation of the given value is produced 
+        // by calling the UInt64.ToString() method.
+        //
+        [CLSCompliant(false)]
+        public virtual void Write(ulong value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        // Writes the text representation of a float to the text stream. The
+        // text representation of the given value is produced by calling the
+        // Float.toString(float) method.
+        //
+        public virtual void Write(float value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        // Writes the text representation of a double to the text stream. The
+        // text representation of the given value is produced by calling the
+        // Double.toString(double) method.
+        //
+        public virtual void Write(double value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        public virtual void Write(decimal value)
+        {
+            Write(value.ToString(FormatProvider));
+        }
+
+        // Writes a string to the text stream. If the given string is null, nothing
+        // is written to the text stream.
+        //
+        public virtual void Write(string value)
+        {
+            if (value != null)
+            {
+                Write(value.ToCharArray());
+            }
+        }
+
+        // Writes the text representation of an object to the text stream. If the
+        // given object is null, nothing is written to the text stream.
+        // Otherwise, the object's ToString method is called to produce the
+        // string representation, and the resulting string is then written to the
+        // output stream.
+        //
+        public virtual void Write(object value)
+        {
+            if (value != null)
+            {
+                IFormattable f = value as IFormattable;
+                if (f != null)
+                {
+                    Write(f.ToString(null, FormatProvider));
+                }
+                else
+                    Write(value.ToString());
+            }
+        }
+
+        // Writes out a formatted string.  Uses the same semantics as
+        // String.Format.
+        // 
+        public virtual void Write(string format, object arg0)
+        {
+            Write(string.Format(FormatProvider, format, arg0));
+        }
+
+        // Writes out a formatted string.  Uses the same semantics as
+        // String.Format.
+        // 
+        public virtual void Write(string format, object arg0, object arg1)
+        {
+            Write(string.Format(FormatProvider, format, arg0, arg1));
+        }
+
+        // Writes out a formatted string.  Uses the same semantics as
+        // String.Format.
+        // 
+        public virtual void Write(string format, object arg0, object arg1, object arg2)
+        {
+            Write(string.Format(FormatProvider, format, arg0, arg1, arg2));
+        }
+
+        // Writes out a formatted string.  Uses the same semantics as
+        // String.Format.
+        // 
+        public virtual void Write(string format, params object[] arg)
+        {
+            Write(string.Format(FormatProvider, format, arg));
+        }
+
+
+        // Writes a line terminator to the text stream. The default line terminator
+        // is Environment.NewLine, but this value can be changed by setting the NewLine property.
+        //
+        public virtual void WriteLine()
+        {
+            Write(CoreNewLine);
+        }
+
+        // Writes a character followed by a line terminator to the text stream.
+        //
+        public virtual void WriteLine(char value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes an array of characters followed by a line terminator to the text
+        // stream.
+        //
+        public virtual void WriteLine(char[] buffer)
+        {
+            Write(buffer);
+            WriteLine();
+        }
+
+        // Writes a range of a character array followed by a line terminator to the
+        // text stream.
+        //
+        public virtual void WriteLine(char[] buffer, int index, int count)
+        {
+            Write(buffer, index, count);
+            WriteLine();
+        }
+
+        public virtual void WriteLine(ReadOnlySpan<char> buffer)
+        {
+            char[] array = ArrayPool<char>.Shared.Rent(buffer.Length);
+
+            try
+            {
+                buffer.CopyTo(new Span<char>(array));
+                WriteLine(array, 0, buffer.Length);
+            }
+            finally
+            {
+                ArrayPool<char>.Shared.Return(array);
+            }
+        }
+
+        // Writes the text representation of a boolean followed by a line
+        // terminator to the text stream.
+        //
+        public virtual void WriteLine(bool value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes the text representation of an integer followed by a line
+        // terminator to the text stream.
+        //
+        public virtual void WriteLine(int value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes the text representation of an unsigned integer followed by 
+        // a line terminator to the text stream.
+        //
+        [CLSCompliant(false)]
+        public virtual void WriteLine(uint value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes the text representation of a long followed by a line terminator
+        // to the text stream.
+        //
+        public virtual void WriteLine(long value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes the text representation of an unsigned long followed by 
+        // a line terminator to the text stream.
+        //
+        [CLSCompliant(false)]
+        public virtual void WriteLine(ulong value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes the text representation of a float followed by a line terminator
+        // to the text stream.
+        //
+        public virtual void WriteLine(float value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes the text representation of a double followed by a line terminator
+        // to the text stream.
+        //
+        public virtual void WriteLine(double value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        public virtual void WriteLine(decimal value)
+        {
+            Write(value);
+            WriteLine();
+        }
+
+        // Writes a string followed by a line terminator to the text stream.
+        //
+        public virtual void WriteLine(string value)
+        {
+            if (value != null)
+            {
+                Write(value);
+            }
+            Write(CoreNewLineStr);
+        }
+
+        // Writes the text representation of an object followed by a line
+        // terminator to the text stream.
+        //
+        public virtual void WriteLine(object value)
+        {
+            if (value == null)
+            {
+                WriteLine();
+            }
+            else
+            {
+                // Call WriteLine(value.ToString), not Write(Object), WriteLine().
+                // This makes calls to WriteLine(Object) atomic.
+                IFormattable f = value as IFormattable;
+                if (f != null)
+                {
+                    WriteLine(f.ToString(null, FormatProvider));
+                }
+                else
+                {
+                    WriteLine(value.ToString());
+                }
+            }
+        }
+
+        // Writes out a formatted string and a new line.  Uses the same 
+        // semantics as String.Format.
+        // 
+        public virtual void WriteLine(string format, object arg0)
+        {
+            WriteLine(string.Format(FormatProvider, format, arg0));
+        }
+
+        // Writes out a formatted string and a new line.  Uses the same 
+        // semantics as String.Format.
+        // 
+        public virtual void WriteLine(string format, object arg0, object arg1)
+        {
+            WriteLine(string.Format(FormatProvider, format, arg0, arg1));
+        }
+
+        // Writes out a formatted string and a new line.  Uses the same 
+        // semantics as String.Format.
+        // 
+        public virtual void WriteLine(string format, object arg0, object arg1, object arg2)
+        {
+            WriteLine(string.Format(FormatProvider, format, arg0, arg1, arg2));
+        }
+
+        // Writes out a formatted string and a new line.  Uses the same 
+        // semantics as String.Format.
+        // 
+        public virtual void WriteLine(string format, params object[] arg)
+        {
+            WriteLine(string.Format(FormatProvider, format, arg));
+        }
+
+        #region Task based Async APIs
+        public virtual Task WriteAsync(char value)
+        {
+            var tuple = new Tuple<TextWriter, char>(this, value);
+            return Task.Factory.StartNew(state =>
+            {
+                var t = (Tuple<TextWriter, char>)state;
+                t.Item1.Write(t.Item2);
+            },
+            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+
+        public virtual Task WriteAsync(string value)
+        {
+            var tuple = new Tuple<TextWriter, string>(this, value);
+            return Task.Factory.StartNew(state =>
+            {
+                var t = (Tuple<TextWriter, string>)state;
+                t.Item1.Write(t.Item2);
+            },
+            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+
+        public Task WriteAsync(char[] buffer)
+        {
+            if (buffer == null)
+            {
+                return Task.CompletedTask;
+            }
+
+            return WriteAsync(buffer, 0, buffer.Length);
+        }
+
+        public virtual Task WriteAsync(char[] buffer, int index, int count)
+        {
+            var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
+            return Task.Factory.StartNew(state =>
+            {
+                var t = (Tuple<TextWriter, char[], int, int>)state;
+                t.Item1.Write(t.Item2, t.Item3, t.Item4);
+            },
+            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+
+        public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+            MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
+                WriteAsync(array.Array, array.Offset, array.Count) :
+                Task.Factory.StartNew(state =>
+                {
+                    var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state;
+                    t.Item1.Write(t.Item2.Span);
+                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+
+        public virtual Task WriteLineAsync(char value)
+        {
+            var tuple = new Tuple<TextWriter, char>(this, value);
+            return Task.Factory.StartNew(state =>
+            {
+                var t = (Tuple<TextWriter, char>)state;
+                t.Item1.WriteLine(t.Item2);
+            },
+            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+
+        public virtual Task WriteLineAsync(string value)
+        {
+            var tuple = new Tuple<TextWriter, string>(this, value);
+            return Task.Factory.StartNew(state =>
+            {
+                var t = (Tuple<TextWriter, string>)state;
+                t.Item1.WriteLine(t.Item2);
+            },
+            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+
+        public Task WriteLineAsync(char[] buffer)
+        {
+            if (buffer == null)
+            {
+                return WriteLineAsync();
+            }
+
+            return WriteLineAsync(buffer, 0, buffer.Length);
+        }
+
+        public virtual Task WriteLineAsync(char[] buffer, int index, int count)
+        {
+            var tuple = new Tuple<TextWriter, char[], int, int>(this, buffer, index, count);
+            return Task.Factory.StartNew(state =>
+            {
+                var t = (Tuple<TextWriter, char[], int, int>)state;
+                t.Item1.WriteLine(t.Item2, t.Item3, t.Item4);
+            },
+            tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+
+        public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default(CancellationToken)) =>
+            MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
+                WriteLineAsync(array.Array, array.Offset, array.Count) :
+                Task.Factory.StartNew(state =>
+                {
+                    var t = (Tuple<TextWriter, ReadOnlyMemory<char>>)state;
+                    t.Item1.WriteLine(t.Item2.Span);
+                }, Tuple.Create(this, buffer), cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+
+        public virtual Task WriteLineAsync()
+        {
+            return WriteAsync(CoreNewLine);
+        }
+
+        public virtual Task FlushAsync()
+        {
+            return Task.Factory.StartNew(state =>
+            {
+                ((TextWriter)state).Flush();
+            },
+            this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+        }
+        #endregion
+
+        private sealed class NullTextWriter : TextWriter
+        {
+            internal NullTextWriter() : base(CultureInfo.InvariantCulture)
+            {
+            }
+
+            public override Encoding Encoding
+            {
+                get
+                {
+                    return Encoding.Unicode;
+                }
+            }
+
+            public override void Write(char[] buffer, int index, int count)
+            {
+            }
+
+            public override void Write(string value)
+            {
+            }
+
+            // Not strictly necessary, but for perf reasons
+            public override void WriteLine()
+            {
+            }
+
+            // Not strictly necessary, but for perf reasons
+            public override void WriteLine(string value)
+            {
+            }
+
+            public override void WriteLine(object value)
+            {
+            }
+
+            public override void Write(char value)
+            {
+            }
+        }
+
+        public static TextWriter Synchronized(TextWriter writer)
+        {
+            if (writer == null)
+                throw new ArgumentNullException(nameof(writer));
+
+            return writer is SyncTextWriter ? writer : new SyncTextWriter(writer);
+        }
+
+        internal sealed class SyncTextWriter : TextWriter, IDisposable
+        {
+            private readonly TextWriter _out;
+
+            internal SyncTextWriter(TextWriter t) : base(t.FormatProvider)
+            {
+                _out = t;
+            }
+
+            public override Encoding Encoding =>  _out.Encoding;
+
+            public override IFormatProvider FormatProvider => _out.FormatProvider;
+
+            public override string NewLine
+            {
+                [MethodImpl(MethodImplOptions.Synchronized)]
+                get { return _out.NewLine; }
+                [MethodImpl(MethodImplOptions.Synchronized)]
+                set { _out.NewLine = value; }
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Close() => _out.Close();
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            protected override void Dispose(bool disposing)
+            {
+                // Explicitly pick up a potentially methodimpl'ed Dispose
+                if (disposing)
+                    ((IDisposable)_out).Dispose();
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Flush() => _out.Flush();
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(char value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(char[] buffer) => _out.Write(buffer);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(char[] buffer, int index, int count) => _out.Write(buffer, index, count);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(bool value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(int value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(uint value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(long value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(ulong value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(float value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(double value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(Decimal value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(string value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(object value) => _out.Write(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(string format, object arg0) => _out.Write(format, arg0);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(string format, object arg0, object arg1) => _out.Write(format, arg0, arg1);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(string format, object arg0, object arg1, object arg2) => _out.Write(format, arg0, arg1, arg2);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void Write(string format, object[] arg) => _out.Write(format, arg);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine() => _out.WriteLine();
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(char value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(decimal value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(char[] buffer) => _out.WriteLine(buffer);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(char[] buffer, int index, int count) => _out.WriteLine(buffer, index, count);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(bool value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(int value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(uint value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(long value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(ulong value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(float value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(double value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(string value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(object value) => _out.WriteLine(value);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(string format, object arg0) => _out.WriteLine(format, arg0);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(string format, object arg0, object arg1) => _out.WriteLine(format, arg0, arg1);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(string format, object arg0, object arg1, object arg2) => _out.WriteLine(format, arg0, arg1, arg2);
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override void WriteLine(string format, object[] arg) => _out.WriteLine(format, arg);
+
+            //
+            // On SyncTextWriter all APIs should run synchronously, even the async ones.
+            //
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task WriteAsync(char value)
+            {
+                Write(value);
+                return Task.CompletedTask;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task WriteAsync(string value)
+            {
+                Write(value);
+                return Task.CompletedTask;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task WriteAsync(char[] buffer, int index, int count)
+            {
+                Write(buffer, index, count);
+                return Task.CompletedTask;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task WriteLineAsync(char value)
+            {
+                WriteLine(value);
+                return Task.CompletedTask;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task WriteLineAsync(string value)
+            {
+                WriteLine(value);
+                return Task.CompletedTask;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task WriteLineAsync(char[] buffer, int index, int count)
+            {
+                WriteLine(buffer, index, count);
+                return Task.CompletedTask;
+            }
+
+            [MethodImpl(MethodImplOptions.Synchronized)]
+            public override Task FlushAsync()
+            {
+                Flush();
+                return Task.CompletedTask;
+            }
+        }
+    }
+}