Using running process to determine mono exe path on windows
[mono.git] / mcs / class / referencesource / mscorlib / system / io / streamwriter.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  StreamWriter
9 **
10 ** <OWNER>gpaperin</OWNER>
11 **
12 **
13 ** Purpose: For writing text to streams in a particular 
14 ** encoding.
15 **
16 **
17 ===========================================================*/
18 using System;
19 using System.Text;
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;
28 #if FEATURE_ASYNC_IO
29 using System.Threading.Tasks;
30 #endif
31
32 namespace System.IO
33 {
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.  
37     // 
38     [Serializable]
39     [ComVisible(true)]
40     public class StreamWriter : TextWriter
41     {
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;
51
52 #if FEATURE_ASYNC_IO
53         private const Int32 DontCopyOnWriteLineThreshold = 512;
54 #endif
55
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);
58
59         private Stream stream;
60         private Encoding encoding;
61         private Encoder encoder;
62         private byte[] byteBuffer;
63         private char[] charBuffer;
64         private int charPos;
65         private int charLen;
66         private bool autoFlush;
67         private bool haveWrittenPreamble;
68         private bool closable;
69
70 #if MDA_SUPPORTED
71         [NonSerialized] 
72         // For StreamWriterBufferedDataLost MDA
73         private MdaHelper mdaHelper;
74 #endif
75
76 #if FEATURE_ASYNC_IO
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.
80         [NonSerialized]
81         private volatile Task _asyncWriteTask;
82
83         private void CheckAsyncTaskInProgress()
84         {
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.
87             
88             Task t = _asyncWriteTask;
89
90             if (t != null && !t.IsCompleted)
91                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncIOInProgress"));
92         }
93 #endif
94
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;
105         
106         internal static Encoding UTF8NoBOM {
107             [FriendAccessAllowed]
108             get { 
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();
114                     _UTF8NoBOM = noBOM;
115                 }
116                 return _UTF8NoBOM;
117             }
118         }
119                 
120
121         internal StreamWriter(): base(null) { // Ask for CurrentCulture all the time 
122         }
123         
124         public StreamWriter(Stream stream) 
125             : this(stream, UTF8NoBOM, DefaultBufferSize, false) {
126         }
127     
128         public StreamWriter(Stream stream, Encoding encoding) 
129             : this(stream, encoding, DefaultBufferSize, false) {
130         }
131         
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.  
135         // 
136         public StreamWriter(Stream stream, Encoding encoding, int bufferSize)
137             : this(stream, encoding, bufferSize, false) { 
138         }
139
140         public StreamWriter(Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)
141             : base(null) // Ask for CurrentCulture all the time
142         {
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();
149
150             Init(stream, encoding, bufferSize, leaveOpen);
151         }
152
153         [ResourceExposure(ResourceScope.Machine)]
154         [ResourceConsumption(ResourceScope.Machine)]
155         public StreamWriter(String path) 
156             : this(path, false, UTF8NoBOM, DefaultBufferSize) {
157         }
158
159         [ResourceExposure(ResourceScope.Machine)]
160         [ResourceConsumption(ResourceScope.Machine)]
161         public StreamWriter(String path, bool append) 
162             : this(path, append, UTF8NoBOM, DefaultBufferSize) {
163         }
164
165         [ResourceExposure(ResourceScope.Machine)]
166         [ResourceConsumption(ResourceScope.Machine)]
167         public StreamWriter(String path, bool append, Encoding encoding) 
168             : this(path, append, encoding, DefaultBufferSize) {
169         }
170
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) { 
175         }
176
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)
181             : base(null)
182         { // Ask for CurrentCulture all the time
183             if (path == null)
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();
191
192             Stream stream = CreateFile(path, append, checkHost);
193             Init(stream, encoding, bufferSize, false);
194         }
195
196         [System.Security.SecuritySafeCritical]
197         private void Init(Stream streamArg, Encoding encodingArg, int bufferSize, bool shouldLeaveOpen)
198         {
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
207             // the preamble.
208             if (stream.CanSeek && stream.Position > 0)
209                 haveWrittenPreamble = true;
210             closable = !shouldLeaveOpen;
211 #if MDA_SUPPORTED
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);
217             }
218 #endif
219         }
220
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);
228             return f;
229         }
230
231         public override void Close() {
232             Dispose(true);
233             GC.SuppressFinalize(this);
234         }
235
236         protected override void Dispose(bool disposing) {
237             try {
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))
245                     {
246 #if FEATURE_ASYNC_IO
247                         CheckAsyncTaskInProgress();
248 #endif
249
250                         Flush(true, true);
251 #if MDA_SUPPORTED
252                         // Disable buffered data loss mda
253                         if (mdaHelper != null)
254                             GC.SuppressFinalize(mdaHelper);
255 #endif
256                     }
257                 }
258             }
259             finally {
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) {
263                     try {
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.  
268                         if (disposing)
269                             stream.Close();
270                     }
271                     finally {
272                         stream = null;
273                         byteBuffer = null;
274                         charBuffer = null;
275                         encoding = null;
276                         encoder = null;
277                         charLen = 0;
278                         base.Dispose(disposing);
279                     }
280                 }
281             }
282         }
283
284         public override void Flush()
285         {
286 #if FEATURE_ASYNC_IO
287             CheckAsyncTaskInProgress();
288 #endif
289
290             Flush(true, true);
291         }
292     
293         private void Flush(bool flushStream, bool flushEncoder)
294         {
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.  
299             if (stream == null)
300                 __Error.WriterClosed();
301
302             // Perf boost for Flush on non-dirty writers.
303             if (charPos==0 && ((!flushStream && !flushEncoder) || CompatibilitySwitches.IsAppEarlierThanWindowsPhone8))
304                 return;
305
306             if (!haveWrittenPreamble) {
307                 haveWrittenPreamble = true;
308                 byte[] preamble = encoding.GetPreamble();
309                 if (preamble.Length > 0)
310                     stream.Write(preamble, 0, preamble.Length);
311             }
312
313             int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);
314             charPos = 0;
315             if (count > 0)
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.
320             if (flushStream)
321                 stream.Flush();
322         }
323     
324         public virtual bool AutoFlush {
325             get { return autoFlush; }
326
327             set
328             {
329 #if FEATURE_ASYNC_IO
330                 CheckAsyncTaskInProgress();
331 #endif
332
333                 autoFlush = value;
334                 if (value) Flush(true, false);
335             }
336         }
337     
338         public virtual Stream BaseStream {
339             get { return stream; }
340         }
341
342         internal bool LeaveOpen {
343             get { return !closable; }
344         }
345
346         internal bool HaveWrittenPreamble {
347             set { haveWrittenPreamble= value; }
348         }
349
350         public override Encoding Encoding {
351             get { return encoding; }
352         }
353
354         public override void Write(char value)
355         {
356
357 #if FEATURE_ASYNC_IO
358             CheckAsyncTaskInProgress();
359 #endif
360
361             if (charPos == charLen) Flush(false, false);
362             charBuffer[charPos] = value;
363             charPos++;
364             if (autoFlush) Flush(true, false);
365         }
366     
367         public override void Write(char[] buffer)
368         {
369             // This may be faster than the one with the index & count since it
370             // has to do less argument checking.
371             if (buffer==null)
372                 return;
373
374 #if FEATURE_ASYNC_IO
375             CheckAsyncTaskInProgress();
376 #endif
377
378             int index = 0;
379             int count = buffer.Length;
380             while (count > 0) {
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));
386                 charPos += n;
387                 index += n;
388                 count -= n;
389             }
390             if (autoFlush) Flush(true, false);
391         }
392
393         public override void Write(char[] buffer, int index, int count) {
394             if (buffer==null)
395                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
396             if (index < 0)
397                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
398             if (count < 0)
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();
403     
404 #if FEATURE_ASYNC_IO
405             CheckAsyncTaskInProgress();
406 #endif
407
408             while (count > 0) {
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));
414                 charPos += n;
415                 index += n;
416                 count -= n;
417             }
418             if (autoFlush) Flush(true, false);
419         }
420     
421         public override void Write(String value)
422         {
423             if (value != null)
424             {
425
426 #if FEATURE_ASYNC_IO
427                 CheckAsyncTaskInProgress();
428 #endif
429
430                 int count = value.Length;
431                 int index = 0;
432                 while (count > 0) {
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);
438                     charPos += n;
439                     index += n;
440                     count -= n;
441                 }
442                 if (autoFlush) Flush(true, false);
443             }
444         }
445
446 #if FEATURE_ASYNC_IO
447         #region Task based Async APIs
448         [HostProtection(ExternalThreading = true)]
449         [ComVisible(false)]
450         public override Task WriteAsync(char value)
451         {
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);
458
459             if (stream == null)
460                 __Error.WriterClosed();
461
462             CheckAsyncTaskInProgress();
463
464             Task task = WriteAsyncInternal(this, value, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: false);
465             _asyncWriteTask = task;
466
467             return task;
468         }
469
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)
476         {            
477             if (charPos == charLen) {
478                 await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
479                 Contract.Assert(_this.charPos == 0);
480                 charPos = 0;
481             }
482
483             charBuffer[charPos] = value;
484             charPos++;
485
486             if (appendNewLine)
487             {
488                 for (Int32 i = 0; i < coreNewLine.Length; i++)   // Expect 2 iterations, no point calling BlockCopy
489                 {
490                     if (charPos == charLen) {                        
491                         await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
492                         Contract.Assert(_this.charPos == 0);
493                         charPos = 0;
494                     }
495
496                     charBuffer[charPos] = coreNewLine[i];
497                     charPos++;
498                 }
499             }            
500
501             if (autoFlush) {
502                 await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
503                 Contract.Assert(_this.charPos == 0);
504                 charPos = 0;
505             }
506
507             _this.CharPos_Prop = charPos;
508         }
509
510         [HostProtection(ExternalThreading = true)]
511         [ComVisible(false)]
512         public override Task WriteAsync(String value)
513         {
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);
520
521             if (value != null)
522             {
523                 if (stream == null)
524                     __Error.WriterClosed();
525
526                 CheckAsyncTaskInProgress();
527
528                 Task task = WriteAsyncInternal(this, value, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: false);
529                 _asyncWriteTask = task;
530
531                 return task;
532             }
533             else
534             {
535                 return Task.CompletedTask;
536             }
537         }
538
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)
545         {
546             Contract.Requires(value != null);
547
548             int count = value.Length;
549             int index = 0;
550
551             while (count > 0)
552             {
553                 if (charPos == charLen) {
554                     await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
555                     Contract.Assert(_this.charPos == 0);
556                     charPos = 0;
557                 }
558
559                 int n = charLen - charPos;
560                 if (n > count)
561                     n = count;
562
563                 Contract.Assert(n > 0, "StreamWriter::Write(String) isn't making progress!  This is most likely a race condition in user code.");
564
565                 value.CopyTo(index, charBuffer, charPos, n);
566
567                 charPos += n;
568                 index += n;
569                 count -= n;
570             }
571
572             if (appendNewLine)
573             {
574                 for (Int32 i = 0; i < coreNewLine.Length; i++)   // Expect 2 iterations, no point calling BlockCopy
575                 {
576                     if (charPos == charLen) {
577                         await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
578                         Contract.Assert(_this.charPos == 0);
579                         charPos = 0;
580                     }
581
582                     charBuffer[charPos] = coreNewLine[i];
583                     charPos++;
584                 }
585             }
586
587             if (autoFlush) {
588                 await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
589                 Contract.Assert(_this.charPos == 0);
590                 charPos = 0;
591             }
592
593             _this.CharPos_Prop = charPos;
594         }
595
596         [HostProtection(ExternalThreading = true)]
597         [ComVisible(false)]
598         public override Task WriteAsync(char[] buffer, int index, int count)
599         {
600             if (buffer==null)
601                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
602             if (index < 0)
603                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
604             if (count < 0)
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();
609
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);
616
617             if (stream == null)
618                 __Error.WriterClosed();
619
620             CheckAsyncTaskInProgress();
621
622             Task task = WriteAsyncInternal(this, buffer, index, count, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: false);
623             _asyncWriteTask = task;
624
625             return task;
626         }
627
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)
634         {
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));
639
640             while (count > 0)
641             {
642                 if (charPos == charLen) {
643                     await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
644                     Contract.Assert(_this.charPos == 0);
645                     charPos = 0;
646                 }
647
648                 int n = charLen - charPos;
649                 if (n > count) n = count;
650
651                 Contract.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress!  This is most likely a race condition in user code.");
652
653                 Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char));
654
655                 charPos += n;
656                 index += n;
657                 count -= n;
658             }
659
660             if (appendNewLine)
661             {
662                 for (Int32 i = 0; i < coreNewLine.Length; i++)   // Expect 2 iterations, no point calling BlockCopy
663                 {
664                     if (charPos == charLen) {
665                         await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false);
666                         Contract.Assert(_this.charPos == 0);
667                         charPos = 0;
668                     }
669
670                     charBuffer[charPos] = coreNewLine[i];
671                     charPos++;
672                 }
673             }
674
675             if (autoFlush) {
676                 await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false);
677                 Contract.Assert(_this.charPos == 0);
678                 charPos = 0;
679             }
680
681             _this.CharPos_Prop = charPos;
682         }
683
684         [HostProtection(ExternalThreading = true)]
685         [ComVisible(false)]
686         public override Task WriteLineAsync()
687         {
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();
694
695             if (stream == null)
696                 __Error.WriterClosed();
697
698             CheckAsyncTaskInProgress();
699
700             Task task = WriteAsyncInternal(this, null, 0, 0, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
701             _asyncWriteTask = task;
702
703             return task;
704         }
705         
706
707         [HostProtection(ExternalThreading = true)]
708         [ComVisible(false)]
709         public override Task WriteLineAsync(char value)
710         {
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);
717
718             if (stream == null)
719                 __Error.WriterClosed();
720
721             CheckAsyncTaskInProgress();
722
723             Task task = WriteAsyncInternal(this, value, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
724             _asyncWriteTask = task;
725
726             return task;
727         }
728         
729
730         [HostProtection(ExternalThreading = true)]
731         [ComVisible(false)]
732         public override Task WriteLineAsync(String value)
733         {
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);
740
741             if (stream == null)
742                 __Error.WriterClosed();
743
744             CheckAsyncTaskInProgress();
745
746             Task task = WriteAsyncInternal(this, value ?? "", charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
747             _asyncWriteTask = task;
748
749             return task;
750         }
751
752
753         [HostProtection(ExternalThreading = true)]
754         [ComVisible(false)]
755         public override Task WriteLineAsync(char[] buffer, int index, int count)
756         {
757             if (buffer==null)
758                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
759             if (index < 0)
760                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
761             if (count < 0)
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();
766
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);
773     
774             if (stream == null)
775                 __Error.WriterClosed();
776
777             CheckAsyncTaskInProgress();
778
779             Task task = WriteAsyncInternal(this, buffer, index, count, charBuffer, charPos, charLen, CoreNewLine, autoFlush, appendNewLine: true);
780             _asyncWriteTask = task;
781
782             return task;
783         }
784         
785
786         [HostProtection(ExternalThreading = true)]
787         [ComVisible(false)]
788         public override Task FlushAsync()
789         {
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();
796
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.  
801             if (stream == null)
802                 __Error.WriterClosed();
803
804             CheckAsyncTaskInProgress();
805
806             Task task = FlushAsyncInternal(true, true, charBuffer, charPos);
807             _asyncWriteTask = task;
808
809             return task;
810         }
811
812         private Int32 CharPos_Prop {
813             set { this.charPos = value; }
814         }
815
816         private bool HaveWrittenPreamble_Prop {
817             set { this.haveWrittenPreamble = value; }
818         }
819
820         private Task FlushAsyncInternal(bool flushStream, bool flushEncoder,
821                                         Char[] sCharBuffer, Int32 sCharPos) {
822             
823             // Perf boost for Flush on non-dirty writers.
824             if (sCharPos == 0 && !flushStream && !flushEncoder)
825                 return Task.CompletedTask;
826
827             Task flushTask = FlushAsyncInternal(this, flushStream, flushEncoder, sCharBuffer, sCharPos, this.haveWrittenPreamble,
828                                                 this.encoding, this.encoder, this.byteBuffer, this.stream);
829                                       
830             this.charPos = 0;
831             return flushTask;
832         }
833
834
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)
840         {            
841             if (!haveWrittenPreamble)
842             {
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);
847             }
848
849             int count = encoder.GetBytes(charBuffer, 0, charPos, byteBuffer, 0, flushEncoder);            
850             if (count > 0)
851                 await stream.WriteAsync(byteBuffer, 0, count).ConfigureAwait(false);
852
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.
856             if (flushStream)
857                 await stream.FlushAsync().ConfigureAwait(false);
858         }
859         #endregion
860 #endif //FEATURE_ASYNC_IO
861
862 #if MDA_SUPPORTED
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
869         {
870             private StreamWriter streamWriter;
871             private String allocatedCallstack;    // captures the callstack when this streamwriter was allocated
872
873             internal MdaHelper(StreamWriter sw, String cs)
874             {
875                 streamWriter = sw;
876                 allocatedCallstack = cs;
877             }
878
879             // Finalizer
880             ~MdaHelper()
881             {
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;
886
887                     if (callStack == null)
888                         callStack = Environment.GetResourceString("IO_StreamWriterBufferedDataLostCaptureAllocatedFromCallstackNotEnabled");
889
890                     String message = Environment.GetResourceString("IO_StreamWriterBufferedDataLost", streamWriter.stream.GetType().FullName, fileName, callStack);
891
892                     Mda.StreamWriterBufferedDataLost.ReportError(message);
893                 }
894             }
895         }  // class MdaHelper
896 #endif  // MDA_SUPPORTED
897
898     }  // class StreamWriter
899 }  // namespace