3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
10 ** <OWNER>gpaperin</OWNER>
13 ** Purpose: For writing text to streams in a particular
17 ===========================================================*/
20 using System.Threading;
21 using System.Globalization;
22 using System.Runtime.CompilerServices;
23 using System.Runtime.Versioning;
24 using System.Security.Permissions;
25 using System.Runtime.Serialization;
26 using System.Diagnostics.Contracts;
27 using System.Runtime.InteropServices;
29 using System.Threading.Tasks;
34 // This class implements a TextWriter for writing characters to a Stream.
35 // This is designed for character output in a particular Encoding,
36 // whereas the Stream class is designed for byte input and output.
40 public class StreamWriter : TextWriter
42 // For UTF-8, the values of 1K for the default buffer size and 4K for the
43 // file stream buffer size are reasonable & give very reasonable
44 // performance for in terms of construction time for the StreamWriter and
45 // write perf. Note that for UTF-8, we end up allocating a 4K byte buffer,
46 // which means we take advantage of adaptive buffering code.
47 // The performance using UnicodeEncoding is acceptable.
48 internal const int DefaultBufferSize = 1024; // char[]
49 private const int DefaultFileStreamBufferSize = 4096;
50 private const int MinBufferSize = 128;
53 private const Int32 DontCopyOnWriteLineThreshold = 512;
56 // Bit bucket - Null has no backing store. Non closable.
57 public new static readonly StreamWriter Null = new StreamWriter(Stream.Null, new UTF8Encoding(false, true), MinBufferSize, true);
59 private Stream stream;
60 private Encoding encoding;
61 private Encoder encoder;
62 private byte[] byteBuffer;
63 private char[] charBuffer;
66 private bool autoFlush;
67 private bool haveWrittenPreamble;
68 private bool closable;
72 // For StreamWriterBufferedDataLost MDA
73 private MdaHelper mdaHelper;
77 // We don't guarantee thread safety on StreamWriter, but we should at
78 // least prevent users from trying to write anything while an Async
79 // write from the same thread is in progress.
81 private volatile Task _asyncWriteTask;
83 private void CheckAsyncTaskInProgress()
85 // We are not locking the access to _asyncWriteTask because this is not meant to guarantee thread safety.
86 // We are simply trying to deter calling any Write APIs while an async Write from the same thread is in progress.
88 Task t = _asyncWriteTask;
90 if (t != null && !t.IsCompleted)
91 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncIOInProgress"));
95 // The high level goal is to be tolerant of encoding errors when we read and very strict
96 // when we write. Hence, default StreamWriter encoding will throw on encoding error.
97 // Note: when StreamWriter throws on invalid encoding chars (for ex, high surrogate character
98 // D800-DBFF without a following low surrogate character DC00-DFFF), it will cause the
99 // internal StreamWriter's state to be irrecoverable as it would have buffered the
100 // illegal chars and any subsequent call to Flush() would hit the encoding error again.
101 // Even Close() will hit the exception as it would try to flush the unwritten data.
102 // Maybe we can add a DiscardBufferedData() method to get out of such situation (like
103 // StreamReader though for different reason). Either way, the buffered data will be lost!
104 private static volatile Encoding _UTF8NoBOM;
106 internal static Encoding UTF8NoBOM {
107 [FriendAccessAllowed]
109 if (_UTF8NoBOM == null) {
110 // No need for double lock - we just want to avoid extra
111 // allocations in the common case.
112 UTF8Encoding noBOM = new UTF8Encoding(false, true);
113 Thread.MemoryBarrier();
121 internal StreamWriter(): base(null) { // Ask for CurrentCulture all the time
124 public StreamWriter(Stream stream)
125 : this(stream, UTF8NoBOM, DefaultBufferSize, false) {
128 public StreamWriter(Stream stream, Encoding encoding)
129 : this(stream, encoding, DefaultBufferSize, false) {
132 // Creates a new StreamWriter for the given stream. The
133 // character encoding is set by encoding and the buffer size,
134 // in number of 16-bit characters, is set by bufferSize.
136 public StreamWriter(Stream stream, Encoding encoding, int bufferSize)
137 : this(stream, encoding, bufferSize, false) {
140 public StreamWriter(Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)
141 : base(null) // Ask for CurrentCulture all the time
143 if (stream == null || encoding == null)
144 throw new ArgumentNullException((stream == null ? "stream" : "encoding"));
145 if (!stream.CanWrite)
146 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable"));
147 if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
148 Contract.EndContractBlock();
150 Init(stream, encoding, bufferSize, leaveOpen);
153 [ResourceExposure(ResourceScope.Machine)]
154 [ResourceConsumption(ResourceScope.Machine)]
155 public StreamWriter(String path)
156 : this(path, false, UTF8NoBOM, DefaultBufferSize) {
159 [ResourceExposure(ResourceScope.Machine)]
160 [ResourceConsumption(ResourceScope.Machine)]
161 public StreamWriter(String path, bool append)
162 : this(path, append, UTF8NoBOM, DefaultBufferSize) {
165 [ResourceExposure(ResourceScope.Machine)]
166 [ResourceConsumption(ResourceScope.Machine)]
167 public StreamWriter(String path, bool append, Encoding encoding)
168 : this(path, append, encoding, DefaultBufferSize) {
171 [System.Security.SecuritySafeCritical]
172 [ResourceExposure(ResourceScope.Machine)]
173 [ResourceConsumption(ResourceScope.Machine)]
174 public StreamWriter(String path, bool append, Encoding encoding, int bufferSize): this(path, append, encoding, bufferSize, true) {
177 [System.Security.SecurityCritical]
178 [ResourceExposure(ResourceScope.Machine)]
179 [ResourceConsumption(ResourceScope.Machine)]
180 internal StreamWriter(String path, bool append, Encoding encoding, int bufferSize, bool checkHost)
182 { // Ask for CurrentCulture all the time
184 throw new ArgumentNullException("path");
185 if (encoding == null)
186 throw new ArgumentNullException("encoding");
187 if (path.Length == 0)
188 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
189 if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
190 Contract.EndContractBlock();
192 Stream stream = CreateFile(path, append, checkHost);
193 Init(stream, encoding, bufferSize, false);
196 [System.Security.SecuritySafeCritical]
197 private void Init(Stream streamArg, Encoding encodingArg, int bufferSize, bool shouldLeaveOpen)
199 this.stream = streamArg;
200 this.encoding = encodingArg;
201 this.encoder = encoding.GetEncoder();
202 if (bufferSize < MinBufferSize) bufferSize = MinBufferSize;
203 charBuffer = new char[bufferSize];
204 byteBuffer = new byte[encoding.GetMaxByteCount(bufferSize)];
205 charLen = bufferSize;
206 // If we're appending to a Stream that already has data, don't write
208 if (stream.CanSeek && stream.Position > 0)
209 haveWrittenPreamble = true;
210 closable = !shouldLeaveOpen;
212 if (Mda.StreamWriterBufferedDataLost.Enabled) {
213 String callstack = null;
214 if (Mda.StreamWriterBufferedDataLost.CaptureAllocatedCallStack)
215 callstack = Environment.GetStackTrace(null, false);
216 mdaHelper = new MdaHelper(this, callstack);
221 [System.Security.SecurityCritical]
222 [ResourceExposure(ResourceScope.Machine)]
223 [ResourceConsumption(ResourceScope.Machine)]
224 private static Stream CreateFile(String path, bool append, bool checkHost) {
225 FileMode mode = append? FileMode.Append: FileMode.Create;
226 FileStream f = new FileStream(path, mode, FileAccess.Write, FileShare.Read,
227 DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);
231 public override void Close() {
233 GC.SuppressFinalize(this);
236 protected override void Dispose(bool disposing) {
238 // We need to flush any buffered data if we are being closed/disposed.
239 // Also, we never close the handles for stdout & friends. So we can safely
240 // write any buffered data to those streams even during finalization, which
241 // is generally the right thing to do.
242 if (stream != null) {
243 // Note: flush on the underlying stream can throw (ex., low disk space)
244 if (disposing || (LeaveOpen && stream is __ConsoleStream))
247 CheckAsyncTaskInProgress();
252 // Disable buffered data loss mda
253 if (mdaHelper != null)
254 GC.SuppressFinalize(mdaHelper);
260 // Dispose of our resources if this StreamWriter is closable.
261 // Note: Console.Out and other such non closable streamwriters should be left alone
262 if (!LeaveOpen && stream != null) {
264 // Attempt to close the stream even if there was an IO error from Flushing.
265 // Note that Stream.Close() can potentially throw here (may or may not be
266 // due to the same Flush error). In this case, we still need to ensure
267 // cleaning up internal resources, hence the finally block.
278 base.Dispose(disposing);
284 public override void Flush()
287 CheckAsyncTaskInProgress();
293 private void Flush(bool flushStream, bool flushEncoder)
295 // flushEncoder should be true at the end of the file and if
296 // the user explicitly calls Flush (though not if AutoFlush is true).
297 // This is required to flush any dangling characters from our UTF-7
298 // and UTF-8 encoders.
300 __Error.WriterClosed();
302 // Perf boost for Flush on non-dirty writers.
303 if (charPos==0 && ((!flushStream && !flushEncoder) || CompatibilitySwitches.IsAppEarlierThanWindowsPhone8))
306 if (!haveWrittenPreamble) {
307 haveWrittenPreamble = true;
308 byte[] preamble = encoding.GetPreamble();
309 if (preamble.Length > 0)
310 stream.Write(preamble, 0, preamble.Length);
313 int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
316 stream.Write(byteBuffer, 0, count);
317 // By definition, calling Flush should flush the stream, but this is
318 // only necessary if we passed in true for flushStream. The Web
319 // Services guys have some perf tests where flushing needlessly hurts.
324 public virtual bool AutoFlush {
325 get { return autoFlush; }
330 CheckAsyncTaskInProgress();
334 if (value) Flush(true, false);
338 public virtual Stream BaseStream {
339 get { return stream; }
342 internal bool LeaveOpen {
343 get { return !closable; }
346 internal bool HaveWrittenPreamble {
347 set { haveWrittenPreamble= value; }
350 public override Encoding Encoding {
351 get { return encoding; }
354 public override void Write(char value)
358 CheckAsyncTaskInProgress();
361 if (charPos == charLen) Flush(false, false);
362 charBuffer[charPos] = value;
364 if (autoFlush) Flush(true, false);
367 public override void Write(char[] buffer)
369 // This may be faster than the one with the index & count since it
370 // has to do less argument checking.
375 CheckAsyncTaskInProgress();
379 int count = buffer.Length;
381 if (charPos == charLen) Flush(false, false);
382 int n = charLen - charPos;
383 if (n > count) n = count;
384 Contract.Assert(n > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a ---- in user code.");
385 Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char));
390 if (autoFlush) Flush(true, false);
393 public override void Write(char[] buffer, int index, int count) {
395 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
397 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
399 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
400 if (buffer.Length - index < count)
401 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
402 Contract.EndContractBlock();
405 CheckAsyncTaskInProgress();
409 if (charPos == charLen) Flush(false, false);
410 int n = charLen - charPos;
411 if (n > count) n = count;
412 Contract.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
413 Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char));
418 if (autoFlush) Flush(true, false);
421 public override void Write(String value)
427 CheckAsyncTaskInProgress();
430 int count = value.Length;
433 if (charPos == charLen) Flush(false, false);
434 int n = charLen - charPos;
435 if (n > count) n = count;
436 Contract.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
437 value.CopyTo(index, charBuffer, charPos, n);
442 if (autoFlush) Flush(true, false);
447 #region Task based Async APIs
448 [HostProtection(ExternalThreading = true)]
450 public override Task WriteAsync(char value)
452 // If we have been inherited into a subclass, the following implementation could be incorrect
453 // since it does not call through to Write() which a subclass might have overriden.
454 // To be safe we will only use this implementation in cases where we know it is safe to do so,
455 // and delegate to our base class (which will call into Write) when we are not sure.
456 if (this.GetType() != typeof(StreamWriter))
457 return base.WriteAsync(value);
460 __Error.WriterClosed();
462 CheckAsyncTaskInProgress();
464 Task task = WriteAsyncInternal(this, value, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: false);
465 _asyncWriteTask = task;
470 // We pass in private instance fields of this MarshalByRefObject-derived type as local params
471 // to ensure performant access inside the state machine that corresponds this async method.
472 // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
473 private static async Task WriteAsyncInternal(StreamWriter _this, Char value,
474 Char[] charBuffer, Int32 charPos, Int32 charLen, Char[] coreNewLine,
475 bool autoFlush, bool appendNewLine)
477 if (charPos == charLen) {
478 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
479 Contract.Assert(_this.charPos == 0);
483 charBuffer[charPos] = value;
488 for (Int32 i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
490 if (charPos == charLen) {
491 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
492 Contract.Assert(_this.charPos == 0);
496 charBuffer[charPos] = coreNewLine[i];
502 await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
503 Contract.Assert(_this.charPos == 0);
507 _this.CharPos_Prop = charPos;
510 [HostProtection(ExternalThreading = true)]
512 public override Task WriteAsync(String value)
514 // If we have been inherited into a subclass, the following implementation could be incorrect
515 // since it does not call through to Write() which a subclass might have overriden.
516 // To be safe we will only use this implementation in cases where we know it is safe to do so,
517 // and delegate to our base class (which will call into Write) when we are not sure.
518 if (this.GetType() != typeof(StreamWriter))
519 return base.WriteAsync(value);
524 __Error.WriterClosed();
526 CheckAsyncTaskInProgress();
528 Task task = WriteAsyncInternal(this, value, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: false);
529 _asyncWriteTask = task;
535 return Task.CompletedTask;
539 // We pass in private instance fields of this MarshalByRefObject-derived type as local params
540 // to ensure performant access inside the state machine that corresponds this async method.
541 // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
542 private static async Task WriteAsyncInternal(StreamWriter _this, String value,
543 Char[] charBuffer, Int32 charPos, Int32 charLen, Char[] coreNewLine,
544 bool autoFlush, bool appendNewLine)
546 Contract.Requires(value != null);
548 int count = value.Length;
553 if (charPos == charLen) {
554 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
555 Contract.Assert(_this.charPos == 0);
559 int n = charLen - charPos;
563 Contract.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code.");
565 value.CopyTo(index, charBuffer, charPos, n);
574 for (Int32 i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
576 if (charPos == charLen) {
577 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
578 Contract.Assert(_this.charPos == 0);
582 charBuffer[charPos] = coreNewLine[i];
588 await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
589 Contract.Assert(_this.charPos == 0);
593 _this.CharPos_Prop = charPos;
596 [HostProtection(ExternalThreading = true)]
598 public override Task WriteAsync(char[] buffer, int index, int count)
601 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
603 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
605 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
606 if (buffer.Length - index < count)
607 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
608 Contract.EndContractBlock();
610 // If we have been inherited into a subclass, the following implementation could be incorrect
611 // since it does not call through to Write() which a subclass might have overriden.
612 // To be safe we will only use this implementation in cases where we know it is safe to do so,
613 // and delegate to our base class (which will call into Write) when we are not sure.
614 if (this.GetType() != typeof(StreamWriter))
615 return base.WriteAsync(buffer, index, count);
618 __Error.WriterClosed();
620 CheckAsyncTaskInProgress();
622 Task task = WriteAsyncInternal(this, buffer, index, count, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: false);
623 _asyncWriteTask = task;
628 // We pass in private instance fields of this MarshalByRefObject-derived type as local params
629 // to ensure performant access inside the state machine that corresponds this async method.
630 // Fields that are written to must be assigned at the end of the method *and* before instance invocations.
631 private static async Task WriteAsyncInternal(StreamWriter _this, Char[] buffer, Int32 index, Int32 count,
632 Char[] charBuffer, Int32 charPos, Int32 charLen, Char[] coreNewLine,
633 bool autoFlush, bool appendNewLine)
635 Contract.Requires(count == 0 || (count > 0 && buffer != null));
636 Contract.Requires(index >= 0);
637 Contract.Requires(count >= 0);
638 Contract.Requires(buffer == null || (buffer != null && buffer.Length - index >= count));
642 if (charPos == charLen) {
643 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
644 Contract.Assert(_this.charPos == 0);
648 int n = charLen - charPos;
649 if (n > count) n = count;
651 Contract.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code.");
653 Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char));
662 for (Int32 i = 0; i < coreNewLine.Length; i++) // Expect 2 iterations, no point calling BlockCopy
664 if (charPos == charLen) {
665 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
666 Contract.Assert(_this.charPos == 0);
670 charBuffer[charPos] = coreNewLine[i];
676 await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
677 Contract.Assert(_this.charPos == 0);
681 _this.CharPos_Prop = charPos;
684 [HostProtection(ExternalThreading = true)]
686 public override Task WriteLineAsync()
688 // If we have been inherited into a subclass, the following implementation could be incorrect
689 // since it does not call through to Write() which a subclass might have overriden.
690 // To be safe we will only use this implementation in cases where we know it is safe to do so,
691 // and delegate to our base class (which will call into Write) when we are not sure.
692 if (this.GetType() != typeof(StreamWriter))
693 return base.WriteLineAsync();
696 __Error.WriterClosed();
698 CheckAsyncTaskInProgress();
700 Task task = WriteAsyncInternal(this, null, 0, 0, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
701 _asyncWriteTask = task;
707 [HostProtection(ExternalThreading = true)]
709 public override Task WriteLineAsync(char value)
711 // If we have been inherited into a subclass, the following implementation could be incorrect
712 // since it does not call through to Write() which a subclass might have overriden.
713 // To be safe we will only use this implementation in cases where we know it is safe to do so,
714 // and delegate to our base class (which will call into Write) when we are not sure.
715 if (this.GetType() != typeof(StreamWriter))
716 return base.WriteLineAsync(value);
719 __Error.WriterClosed();
721 CheckAsyncTaskInProgress();
723 Task task = WriteAsyncInternal(this, value, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
724 _asyncWriteTask = task;
730 [HostProtection(ExternalThreading = true)]
732 public override Task WriteLineAsync(String value)
734 // If we have been inherited into a subclass, the following implementation could be incorrect
735 // since it does not call through to Write() which a subclass might have overriden.
736 // To be safe we will only use this implementation in cases where we know it is safe to do so,
737 // and delegate to our base class (which will call into Write) when we are not sure.
738 if (this.GetType() != typeof(StreamWriter))
739 return base.WriteLineAsync(value);
742 __Error.WriterClosed();
744 CheckAsyncTaskInProgress();
746 Task task = WriteAsyncInternal(this, value ?? "", charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
747 _asyncWriteTask = task;
753 [HostProtection(ExternalThreading = true)]
755 public override Task WriteLineAsync(char[] buffer, int index, int count)
758 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
760 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
762 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
763 if (buffer.Length - index < count)
764 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
765 Contract.EndContractBlock();
767 // If we have been inherited into a subclass, the following implementation could be incorrect
768 // since it does not call through to Write() which a subclass might have overriden.
769 // To be safe we will only use this implementation in cases where we know it is safe to do so,
770 // and delegate to our base class (which will call into Write) when we are not sure.
771 if (this.GetType() != typeof(StreamWriter))
772 return base.WriteLineAsync(buffer, index, count);
775 __Error.WriterClosed();
777 CheckAsyncTaskInProgress();
779 Task task = WriteAsyncInternal(this, buffer, index, count, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
780 _asyncWriteTask = task;
786 [HostProtection(ExternalThreading = true)]
788 public override Task FlushAsync()
790 // If we have been inherited into a subclass, the following implementation could be incorrect
791 // since it does not call through to Flush() which a subclass might have overriden. To be safe
792 // we will only use this implementation in cases where we know it is safe to do so,
793 // and delegate to our base class (which will call into Flush) when we are not sure.
794 if (this.GetType() != typeof(StreamWriter))
795 return base.FlushAsync();
797 // flushEncoder should be true at the end of the file and if
798 // the user explicitly calls Flush (though not if AutoFlush is true).
799 // This is required to flush any dangling characters from our UTF-7
800 // and UTF-8 encoders.
802 __Error.WriterClosed();
804 CheckAsyncTaskInProgress();
806 Task task = FlushAsyncInternal(true, true, charBuffer, charPos);
807 _asyncWriteTask = task;
812 private Int32 CharPos_Prop {
813 set { this.charPos = value; }
816 private bool HaveWrittenPreamble_Prop {
817 set { this.haveWrittenPreamble = value; }
820 private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
821 Char[] sCharBuffer, Int32 sCharPos) {
823 // Perf boost for Flush on non-dirty writers.
824 if (sCharPos == 0 && !flushStream && !flushEncoder)
825 return Task.CompletedTask;
827 Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, this.haveWrittenPreamble,
828 this.encoding, this.encoder, this.byteBuffer, this.stream);
835 // We pass in private instance fields of this MarshalByRefObject-derived type as local params
836 // to ensure performant access inside the state machine that corresponds this async method.
837 private static async Task FlushAsyncInternal(StreamWriter _this, bool flushStream, bool flushEncoder,
838 Char[] charBuffer, Int32 charPos, bool haveWrittenPreamble,
839 Encoding encoding, Encoder encoder, Byte[] byteBuffer, Stream stream)
841 if (!haveWrittenPreamble)
843 _this.HaveWrittenPreamble_Prop = true;
844 byte[] preamble = encoding.GetPreamble();
845 if (preamble.Length > 0)
846 await stream.WriteAsync(preamble, 0, preamble.Length).ConfigureAwait(false);
849 int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
851 await stream.WriteAsync(byteBuffer, 0, count).ConfigureAwait(false);
853 // By definition, calling Flush should flush the stream, but this is
854 // only necessary if we passed in true for flushStream. The Web
855 // Services guys have some perf tests where flushing needlessly hurts.
857 await stream.FlushAsync().ConfigureAwait(false);
860 #endif //FEATURE_ASYNC_IO
863 // StreamWriterBufferedDataLost MDA
864 // Instead of adding a finalizer to StreamWriter for detecting buffered data loss
865 // (ie, when the user forgets to call Close/Flush on the StreamWriter), we will
866 // have a separate object with normal finalization semantics that maintains a
867 // back pointer to this StreamWriter and alerts about any data loss
868 private sealed class MdaHelper
870 private StreamWriter streamWriter;
871 private String allocatedCallstack; // captures the callstack when this streamwriter was allocated
873 internal MdaHelper(StreamWriter sw, String cs)
876 allocatedCallstack = cs;
882 // Make sure people closed this StreamWriter, exclude StreamWriter::Null.
883 if (streamWriter.charPos != 0 && streamWriter.stream != null && streamWriter.stream != Stream.Null) {
884 String fileName = (streamWriter.stream is FileStream) ? ((FileStream)streamWriter.stream).NameInternal : "<unknown>";
885 String callStack = allocatedCallstack;
887 if (callStack == null)
888 callStack = Environment.GetResourceString("IO_StreamWriterBufferedDataLostCaptureAllocatedFromCallstackNotEnabled");
890 String message = Environment.GetResourceString("IO_StreamWriterBufferedDataLost", streamWriter.stream.GetType().FullName, fileName, callStack);
892 Mda.StreamWriterBufferedDataLost.ReportError(message);
896 #endif // MDA_SUPPORTED
898 } // class StreamWriter