98be2379e9ce7ee354e59b711ffeb7b9fd279e22
[mono.git] / mcs / class / referencesource / mscorlib / system / io / streamreader.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  StreamReader
9 ** 
10 ** <OWNER>gpaperin</OWNER>
11 **
12 **
13 ** Purpose: For reading text from streams in a particular 
14 ** encoding.
15 **
16 **
17 ===========================================================*/
18
19 using System;
20 using System.Text;
21 using System.Runtime.InteropServices;
22 using System.Runtime.Versioning;
23 using System.Diagnostics.CodeAnalysis;
24 using System.Diagnostics.Contracts;
25 using System.Security.Permissions;
26 #if FEATURE_ASYNC_IO
27 using System.Threading.Tasks;
28 #endif
29
30 namespace System.IO
31 {
32     // This class implements a TextReader for reading characters to a Stream.
33     // This is designed for character input in a particular Encoding, 
34     // whereas the Stream class is designed for byte input and output.  
35     // 
36     [Serializable]
37     [System.Runtime.InteropServices.ComVisible(true)]
38     public class StreamReader : TextReader
39     {
40         // StreamReader.Null is threadsafe.
41         public new static readonly StreamReader Null = new NullStreamReader();
42
43         // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well
44         // perf-wise.  On even a 40 MB text file, any perf loss by using a 4K
45         // buffer is negated by the win of allocating a smaller byte[], which 
46         // saves construction time.  This does break adaptive buffering,
47         // but this is slightly faster.
48         internal static int DefaultBufferSize
49         {
50             get
51             {
52 #if FEATURE_LEGACYNETCF
53                 // Quirk for Mango app compatibility
54                 if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
55                 {
56                     return 4096;
57                 }
58 #endif // FEATURE_LEGACYNETCF
59                 return 1024;
60             }
61         }
62
63         private const int DefaultFileStreamBufferSize = 4096;
64         private const int MinBufferSize = 128;
65     
66         private Stream stream;
67         private Encoding encoding;
68         private Decoder decoder;
69         private byte[] byteBuffer;
70         private char[] charBuffer;
71         private byte[] _preamble;   // Encoding's preamble, which identifies this encoding.
72         private int charPos;
73         private int charLen;
74         // Record the number of valid bytes in the byteBuffer, for a few checks.
75         private int byteLen;
76         // This is used only for preamble detection
77         private int bytePos;
78
79         // This is the maximum number of chars we can get from one call to 
80         // ReadBuffer.  Used so ReadBuffer can tell when to copy data into
81         // a user's char[] directly, instead of our internal char[].
82         private int _maxCharsPerBuffer;
83
84         // We will support looking for byte order marks in the stream and trying
85         // to decide what the encoding might be from the byte order marks, IF they
86         // exist.  But that's all we'll do.  
87         private bool _detectEncoding;
88
89         // Whether we must still check for the encoding's given preamble at the
90         // beginning of this file.
91         private bool _checkPreamble;
92
93         // Whether the stream is most likely not going to give us back as much 
94         // data as we want the next time we call it.  We must do the computation
95         // before we do any byte order mark handling and save the result.  Note
96         // that we need this to allow users to handle streams used for an 
97         // interactive protocol, where they block waiting for the remote end 
98         // to send a response, like logging in on a Unix machine.
99         private bool _isBlocked;
100
101         // The intent of this field is to leave open the underlying stream when 
102         // disposing of this StreamReader.  A name like _leaveOpen is better, 
103         // but this type is serializable, and this field's name was _closable.
104         private bool _closable;  // Whether to close the underlying stream.
105
106 #if FEATURE_ASYNC_IO
107         // We don't guarantee thread safety on StreamReader, but we should at 
108         // least prevent users from trying to read anything while an Async
109         // read from the same thread is in progress.
110         [NonSerialized]
111         private volatile Task _asyncReadTask;
112
113         private void CheckAsyncTaskInProgress()
114         {           
115             // We are not locking the access to _asyncReadTask because this is not meant to guarantee thread safety. 
116             // We are simply trying to deter calling any Read APIs while an async Read from the same thread is in progress.
117            
118             Task t = _asyncReadTask;
119
120             if (t != null && !t.IsCompleted)
121                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncIOInProgress"));
122         }
123 #endif
124
125         // StreamReader by default will ignore illegal UTF8 characters. We don't want to 
126         // throw here because we want to be able to read ill-formed data without choking. 
127         // The high level goal is to be tolerant of encoding errors when we read and very strict 
128         // when we write. Hence, default StreamWriter encoding will throw on error.   
129         
130         internal StreamReader() {
131         }
132         
133         public StreamReader(Stream stream) 
134             : this(stream, true) {
135         }
136
137         public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks) 
138             : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
139         }
140         
141         public StreamReader(Stream stream, Encoding encoding) 
142             : this(stream, encoding, true, DefaultBufferSize, false) {
143         }
144         
145         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
146             : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
147         }
148
149         // Creates a new StreamReader for the given stream.  The 
150         // character encoding is set by encoding and the buffer size, 
151         // in number of 16-bit characters, is set by bufferSize.  
152         // 
153         // Note that detectEncodingFromByteOrderMarks is a very
154         // loose attempt at detecting the encoding by looking at the first
155         // 3 bytes of the stream.  It will recognize UTF-8, little endian
156         // unicode, and big endian unicode text, but that's it.  If neither
157         // of those three match, it will use the Encoding you provided.
158         // 
159         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
160             : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false) {
161         }
162
163         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
164         {
165             if (stream == null || encoding == null)
166                 throw new ArgumentNullException((stream == null ? "stream" : "encoding"));
167             if (!stream.CanRead)
168                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
169             if (bufferSize <= 0)
170                 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
171             Contract.EndContractBlock();
172
173             Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen);
174         }
175
176 #if FEATURE_LEGACYNETCF
177         [System.Security.SecuritySafeCritical]
178 #endif // FEATURE_LEGACYNETCF
179         [ResourceExposure(ResourceScope.Machine)]
180         [ResourceConsumption(ResourceScope.Machine)]
181         public StreamReader(String path) 
182             : this(path, true) {
183 #if FEATURE_LEGACYNETCF
184             if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
185                 System.Reflection.Assembly callingAssembly = System.Reflection.Assembly.GetCallingAssembly();
186                 if(callingAssembly != null && !callingAssembly.IsProfileAssembly) {
187                     string caller = new System.Diagnostics.StackFrame(1).GetMethod().FullName;
188                     string callee = System.Reflection.MethodBase.GetCurrentMethod().FullName;
189                     throw new MethodAccessException(String.Format(
190                         System.Globalization.CultureInfo.CurrentCulture,
191                         Environment.GetResourceString("Arg_MethodAccessException_WithCaller"),
192                         caller,
193                         callee));
194                 }
195             }
196 #endif // FEATURE_LEGACYNETCF
197                                }
198
199         [ResourceExposure(ResourceScope.Machine)]
200         [ResourceConsumption(ResourceScope.Machine)]
201         public StreamReader(String path, bool detectEncodingFromByteOrderMarks) 
202             : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
203         }
204
205         [ResourceExposure(ResourceScope.Machine)]
206         [ResourceConsumption(ResourceScope.Machine)]
207         public StreamReader(String path, Encoding encoding) 
208             : this(path, encoding, true, DefaultBufferSize) {
209         }
210
211         [ResourceExposure(ResourceScope.Machine)]
212         [ResourceConsumption(ResourceScope.Machine)]
213         public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks) 
214             : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
215         }
216
217         [System.Security.SecuritySafeCritical]
218         [ResourceExposure(ResourceScope.Machine)]
219         [ResourceConsumption(ResourceScope.Machine)]
220         public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
221             : this(path, encoding, detectEncodingFromByteOrderMarks, bufferSize, true) {
222         }
223
224         [System.Security.SecurityCritical]
225         [ResourceExposure(ResourceScope.Machine)]
226         [ResourceConsumption(ResourceScope.Machine)]
227         internal StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool checkHost)
228         {
229             // Don't open a Stream before checking for invalid arguments,
230             // or we'll create a FileStream on disk and we won't close it until
231             // the finalizer runs, causing problems for applications.
232             if (path==null || encoding==null)
233                 throw new ArgumentNullException((path==null ? "path" : "encoding"));
234             if (path.Length==0)
235                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
236             if (bufferSize <= 0)
237                 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
238             Contract.EndContractBlock();
239
240             Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost);
241             Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false);
242         }
243         
244         private void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen) {
245             this.stream = stream;
246             this.encoding = encoding;
247             decoder = encoding.GetDecoder();
248             if (bufferSize < MinBufferSize) bufferSize = MinBufferSize;
249             byteBuffer = new byte[bufferSize];
250             _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
251             charBuffer = new char[_maxCharsPerBuffer];
252             byteLen = 0;
253             bytePos = 0;
254             _detectEncoding = detectEncodingFromByteOrderMarks;
255             _preamble = encoding.GetPreamble();
256             _checkPreamble = (_preamble.Length > 0);
257             _isBlocked = false;
258             _closable = !leaveOpen;
259         }
260
261         // Init used by NullStreamReader, to delay load encoding
262         internal void Init(Stream stream)
263         {
264             this.stream = stream;
265             _closable = true;
266         }
267
268         public override void Close()
269         {
270             Dispose(true);
271         }
272         
273         protected override void Dispose(bool disposing)
274         {
275             // Dispose of our resources if this StreamReader is closable.
276             // Note that Console.In should be left open.
277             try {
278                 // Note that Stream.Close() can potentially throw here. So we need to 
279                 // ensure cleaning up internal resources, inside the finally block.  
280                 if (!LeaveOpen && disposing && (stream != null))
281                     stream.Close();
282             }
283             finally {
284                 if (!LeaveOpen && (stream != null)) {
285                     stream = null;
286                     encoding = null;
287                     decoder = null;
288                     byteBuffer = null;
289                     charBuffer = null;
290                     charPos = 0;
291                     charLen = 0;
292                     base.Dispose(disposing);
293                 }
294             }
295         }
296         
297         public virtual Encoding CurrentEncoding {
298             get { return encoding; }
299         }
300         
301         public virtual Stream BaseStream {
302             get { return stream; }
303         }
304
305         internal bool LeaveOpen {
306             get { return !_closable; }
307         }
308
309         // DiscardBufferedData tells StreamReader to throw away its internal
310         // buffer contents.  This is useful if the user needs to seek on the
311         // underlying stream to a known location then wants the StreamReader
312         // to start reading from this new point.  This method should be called
313         // very sparingly, if ever, since it can lead to very poor performance.
314         // However, it may be the only way of handling some scenarios where 
315         // users need to re-read the contents of a StreamReader a second time.
316         public void DiscardBufferedData()
317         {
318
319 #if FEATURE_ASYNC_IO
320             CheckAsyncTaskInProgress();
321 #endif
322
323             byteLen = 0;
324             charLen = 0;
325             charPos = 0;
326             // in general we'd like to have an invariant that encoding isn't null. However,
327             // for startup improvements for NullStreamReader, we want to delay load encoding. 
328             if (encoding != null) {
329                 decoder = encoding.GetDecoder();
330             }
331             _isBlocked = false;
332         }
333
334         public bool EndOfStream {
335             get {
336                 if (stream == null)
337                     __Error.ReaderClosed();
338
339 #if FEATURE_ASYNC_IO
340                 CheckAsyncTaskInProgress();
341 #endif
342
343                 if (charPos < charLen)
344                     return false;
345
346                 // This may block on pipes!
347                 int numRead = ReadBuffer();
348                 return numRead == 0;
349             }
350         }
351
352         [Pure]
353         public override int Peek() {
354             if (stream == null)
355                 __Error.ReaderClosed();
356
357 #if FEATURE_ASYNC_IO
358             CheckAsyncTaskInProgress();
359 #endif
360
361             if (charPos == charLen)
362             {
363                 if (_isBlocked || ReadBuffer() == 0) return -1;
364             }
365             return charBuffer[charPos];
366         }
367
368 #if MONO
369         //
370         // Used internally by our console, as it previously depended on Peek() being a
371         // routine that would not block.
372         //
373         internal bool DataAvailable ()
374         {
375             return charPos < charLen;
376         }
377 #endif
378         
379         public override int Read() {
380             if (stream == null)
381                 __Error.ReaderClosed();
382
383 #if FEATURE_ASYNC_IO
384             CheckAsyncTaskInProgress();
385 #endif
386
387             if (charPos == charLen) {
388                 if (ReadBuffer() == 0) return -1;
389             }
390             int result = charBuffer[charPos];
391             charPos++;
392             return result;
393         }
394     
395         public override int Read([In, Out] char[] buffer, int index, int count)
396         {
397             if (buffer==null)
398                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
399             if (index < 0 || count < 0)
400                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
401             if (buffer.Length - index < count)
402                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
403             Contract.EndContractBlock();
404
405             if (stream == null)
406                 __Error.ReaderClosed();
407
408 #if FEATURE_ASYNC_IO
409             CheckAsyncTaskInProgress();
410 #endif
411
412             int charsRead = 0;
413             // As a perf optimization, if we had exactly one buffer's worth of 
414             // data read in, let's try writing directly to the user's buffer.
415             bool readToUserBuffer = false;
416             while (count > 0) {
417                 int n = charLen - charPos;
418                 if (n == 0) n = ReadBuffer(buffer, index + charsRead, count, out readToUserBuffer);
419                 if (n == 0) break;  // We're at EOF
420                 if (n > count) n = count;
421                 if (!readToUserBuffer) {
422                     Buffer.InternalBlockCopy(charBuffer, charPos * 2, buffer, (index + charsRead) * 2, n*2);
423                     charPos += n;
424                 }
425                 charsRead += n;
426                 count -= n;
427                 // This function shouldn't block for an indefinite amount of time,
428                 // or reading from a network stream won't work right.  If we got
429                 // fewer bytes than we requested, then we want to break right here.
430                 if (_isBlocked)
431                     break;
432             }
433
434             return charsRead;
435         }
436
437         public override String ReadToEnd()
438         {
439             if (stream == null)
440                 __Error.ReaderClosed();
441
442 #if FEATURE_ASYNC_IO
443             CheckAsyncTaskInProgress();
444 #endif
445
446             // Call ReadBuffer, then pull data out of charBuffer.
447             StringBuilder sb = new StringBuilder(charLen - charPos);
448             do {
449                 sb.Append(charBuffer, charPos, charLen - charPos);
450                 charPos = charLen;  // Note we consumed these characters
451                 ReadBuffer();
452             } while (charLen > 0);
453             return sb.ToString();
454         }
455
456         public override int ReadBlock([In, Out] char[] buffer, int index, int count)
457         {
458             if (buffer==null)
459                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
460             if (index < 0 || count < 0)
461                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
462             if (buffer.Length - index < count)
463                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
464             Contract.EndContractBlock();
465
466             if (stream == null)
467                 __Error.ReaderClosed();
468
469 #if FEATURE_ASYNC_IO
470             CheckAsyncTaskInProgress();
471 #endif
472
473             return base.ReadBlock(buffer, index, count);
474         }
475
476         // Trims n bytes from the front of the buffer.
477         private void CompressBuffer(int n)
478         {
479             Contract.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?");
480             Buffer.InternalBlockCopy(byteBuffer, n, byteBuffer, 0, byteLen - n);
481             byteLen -= n;
482         }
483
484         private void DetectEncoding()
485         {
486             if (byteLen < 2)
487                 return;
488             _detectEncoding = false;
489             bool changedEncoding = false;
490             if (byteBuffer[0]==0xFE && byteBuffer[1]==0xFF) {
491                 // Big Endian Unicode
492
493                 encoding = new UnicodeEncoding(true, true);
494                 CompressBuffer(2);
495                 changedEncoding = true;
496             }
497                      
498             else if (byteBuffer[0]==0xFF && byteBuffer[1]==0xFE) {
499                 // Little Endian Unicode, or possibly little endian UTF32
500                 if (byteLen < 4 || byteBuffer[2] != 0 || byteBuffer[3] != 0) {
501                     encoding = new UnicodeEncoding(false, true);
502                     CompressBuffer(2);
503                     changedEncoding = true;
504                 }
505 #if FEATURE_UTF32   
506                 else {
507                     encoding = new UTF32Encoding(false, true);
508                     CompressBuffer(4);
509                 changedEncoding = true;
510             }
511 #endif            
512             }
513          
514             else if (byteLen >= 3 && byteBuffer[0]==0xEF && byteBuffer[1]==0xBB && byteBuffer[2]==0xBF) {
515                 // UTF-8
516                 encoding = Encoding.UTF8;
517                 CompressBuffer(3);
518                 changedEncoding = true;
519             }
520 #if FEATURE_UTF32            
521             else if (byteLen >= 4 && byteBuffer[0] == 0 && byteBuffer[1] == 0 &&
522                      byteBuffer[2] == 0xFE && byteBuffer[3] == 0xFF) {
523                 // Big Endian UTF32
524                 encoding = new UTF32Encoding(true, true);
525                 CompressBuffer(4);
526                 changedEncoding = true;
527             }
528 #endif            
529             else if (byteLen == 2)
530                 _detectEncoding = true;
531             // Note: in the future, if we change this algorithm significantly,
532             // we can support checking for the preamble of the given encoding.
533
534             if (changedEncoding) {
535                 decoder = encoding.GetDecoder();
536                 _maxCharsPerBuffer = encoding.GetMaxCharCount(byteBuffer.Length);
537                 charBuffer = new char[_maxCharsPerBuffer];
538             }
539         }
540
541         // Trims the preamble bytes from the byteBuffer. This routine can be called multiple times
542         // and we will buffer the bytes read until the preamble is matched or we determine that
543         // there is no match. If there is no match, every byte read previously will be available 
544         // for further consumption. If there is a match, we will compress the buffer for the 
545         // leading preamble bytes
546         private bool IsPreamble()
547         {
548             if (!_checkPreamble) 
549                 return _checkPreamble;
550
551             Contract.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?");
552             int len = (byteLen >= (_preamble.Length))? (_preamble.Length - bytePos) : (byteLen  - bytePos);
553
554             for(int i=0; i<len; i++, bytePos++) {
555                 if (byteBuffer[bytePos] != _preamble[bytePos]) {
556                     bytePos = 0;
557                     _checkPreamble = false;
558                     break;
559                 }
560             }
561
562             Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
563
564             if (_checkPreamble) {
565                 if (bytePos == _preamble.Length) {
566                     // We have a match
567                     CompressBuffer(_preamble.Length);
568                     bytePos = 0;
569                     _checkPreamble = false;
570                     _detectEncoding = false;
571                 }
572             }
573
574             return _checkPreamble;
575         }
576
577         internal virtual int ReadBuffer() {
578             charLen = 0;
579             charPos = 0;
580
581             if (!_checkPreamble)
582                 byteLen = 0;
583             do {
584                 if (_checkPreamble) {
585                     Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
586                     int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
587                     Contract.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
588
589                     if (len == 0) {
590                         // EOF but we might have buffered bytes from previous 
591                         // attempt to detect preamble that needs to be decoded now
592                         if (byteLen > 0)
593                         {
594                             charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
595                             // Need to zero out the byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
596                             bytePos = byteLen = 0;
597                         }
598
599                         return charLen;
600                     }
601
602                     byteLen += len;
603                 }
604                 else {
605                     Contract.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?");
606                     byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
607                     Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
608
609                     if (byteLen == 0)  // We're at EOF
610                         return charLen;
611                 }
612
613                 // _isBlocked == whether we read fewer bytes than we asked for.
614                 // Note we must check it here because CompressBuffer or 
615                 // DetectEncoding will change byteLen.
616                 _isBlocked = (byteLen < byteBuffer.Length);
617
618                 // Check for preamble before detect encoding. This is not to override the
619                 // user suppplied Encoding for the one we implicitly detect. The user could
620                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
621                 if (IsPreamble())
622                     continue;
623
624                 // If we're supposed to detect the encoding and haven't done so yet,
625                 // do it.  Note this may need to be called more than once.
626                 if (_detectEncoding && byteLen >= 2)
627                     DetectEncoding();
628
629                 charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
630             } while (charLen == 0);
631             //Console.WriteLine("ReadBuffer called.  chars: "+charLen);
632             return charLen;
633         }
634
635
636         // This version has a perf optimization to decode data DIRECTLY into the 
637         // user's buffer, bypassing StreamReader's own buffer.
638         // This gives a > 20% perf improvement for our encodings across the board,
639         // but only when asking for at least the number of characters that one
640         // buffer's worth of bytes could produce.
641         // This optimization, if run, will break SwitchEncoding, so we must not do 
642         // this on the first call to ReadBuffer.  
643         private int ReadBuffer(char[] userBuffer, int userOffset, int desiredChars, out bool readToUserBuffer)
644         {
645             charLen = 0;
646             charPos = 0;
647             
648             if (!_checkPreamble)
649                 byteLen = 0;
650             
651             int charsRead = 0;
652
653             // As a perf optimization, we can decode characters DIRECTLY into a
654             // user's char[].  We absolutely must not write more characters 
655             // into the user's buffer than they asked for.  Calculating 
656             // encoding.GetMaxCharCount(byteLen) each time is potentially very 
657             // expensive - instead, cache the number of chars a full buffer's 
658             // worth of data may produce.  Yes, this makes the perf optimization 
659             // less aggressive, in that all reads that asked for fewer than AND 
660             // returned fewer than _maxCharsPerBuffer chars won't get the user 
661             // buffer optimization.  This affects reads where the end of the
662             // Stream comes in the middle somewhere, and when you ask for 
663             // fewer chars than your buffer could produce.
664             readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
665
666             do {
667                 Contract.Assert(charsRead == 0);
668
669                 if (_checkPreamble) {
670                     Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
671                     int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
672                     Contract.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
673                     
674                     if (len == 0) {
675                         // EOF but we might have buffered bytes from previous 
676                         // attempt to detect preamble that needs to be decoded now
677                         if (byteLen > 0) {
678                             if (readToUserBuffer) {
679                                 charsRead = decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
680                                 charLen = 0;  // StreamReader's buffer is empty.
681                             }
682                             else {
683                                 charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
684                                 charLen += charsRead;  // Number of chars in StreamReader's buffer.
685                             }
686                         }
687
688                         return charsRead;
689                     }
690                     
691                     byteLen += len;
692                 }
693                 else {
694                     Contract.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?");
695
696                     byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
697
698                     Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
699                     
700                     if (byteLen == 0)  // EOF
701                         break;
702                 }
703
704                 // _isBlocked == whether we read fewer bytes than we asked for.
705                 // Note we must check it here because CompressBuffer or 
706                 // DetectEncoding will change byteLen.
707                 _isBlocked = (byteLen < byteBuffer.Length);
708
709                 // Check for preamble before detect encoding. This is not to override the
710                 // user suppplied Encoding for the one we implicitly detect. The user could
711                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
712                 // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
713                 // doesn't change the encoding or affect _maxCharsPerBuffer
714                 if (IsPreamble()) 
715                     continue;
716
717                 // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
718                 if (_detectEncoding && byteLen >= 2) {
719                     DetectEncoding();
720                     // DetectEncoding changes some buffer state.  Recompute this.
721                     readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
722                 }
723
724                 charPos = 0;
725                 if (readToUserBuffer) {
726                     charsRead += decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
727                     charLen = 0;  // StreamReader's buffer is empty.
728                 }
729                 else {
730                     charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
731                     charLen += charsRead;  // Number of chars in StreamReader's buffer.
732                 }
733             } while (charsRead == 0);
734
735             _isBlocked &= charsRead < desiredChars;
736
737             //Console.WriteLine("ReadBuffer: charsRead: "+charsRead+"  readToUserBuffer: "+readToUserBuffer);
738             return charsRead;
739         }
740
741
742         // Reads a line. A line is defined as a sequence of characters followed by
743         // a carriage return ('\r'), a line feed ('\n'), or a carriage return
744         // immediately followed by a line feed. The resulting string does not
745         // contain the terminating carriage return and/or line feed. The returned
746         // value is null if the end of the input stream has been reached.
747         //
748         public override String ReadLine()
749         {
750             if (stream == null)
751                 __Error.ReaderClosed();
752
753 #if FEATURE_ASYNC_IO
754             CheckAsyncTaskInProgress();
755 #endif
756
757             if (charPos == charLen)
758             {
759                 if (ReadBuffer() == 0) return null;
760             }
761
762             StringBuilder sb = null;
763             do {
764                 int i = charPos;
765                 do {
766                     char ch = charBuffer[i];
767                     // Note the following common line feed chars:
768                     // \n - UNIX   \r\n - DOS   \r - Mac
769                     if (ch == '\r' || ch == '\n') {
770                         String s;
771                         if (sb != null) {
772                             sb.Append(charBuffer, charPos, i - charPos);
773                             s = sb.ToString();
774                         }
775                         else {
776                             s = new String(charBuffer, charPos, i - charPos);
777                         }
778                         charPos = i + 1;
779                         if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0)) {
780                             if (charBuffer[charPos] == '\n') charPos++;
781                         }
782                         return s;
783                     }
784                     i++;
785                 } while (i < charLen);
786                 i = charLen - charPos;
787                 if (sb == null) sb = new StringBuilder(i + 80);
788                 sb.Append(charBuffer, charPos, i);
789             } while (ReadBuffer() > 0);
790             return sb.ToString();
791         }
792         
793 #if FEATURE_ASYNC_IO
794         #region Task based Async APIs
795         [HostProtection(ExternalThreading=true)]
796         [ComVisible(false)]
797         public override Task<String> ReadLineAsync()
798         {
799             // If we have been inherited into a subclass, the following implementation could be incorrect
800             // since it does not call through to Read() which a subclass might have overriden.  
801             // To be safe we will only use this implementation in cases where we know it is safe to do so,
802             // and delegate to our base class (which will call into Read) when we are not sure.
803             if (this.GetType() != typeof(StreamReader))
804                 return base.ReadLineAsync();
805
806             if (stream == null)
807                 __Error.ReaderClosed();
808
809             CheckAsyncTaskInProgress();
810
811             Task<String> task = ReadLineAsyncInternal();
812             _asyncReadTask = task;
813
814             return task;
815         }
816
817         private async Task<String> ReadLineAsyncInternal()
818         {
819             if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
820                 return null;
821
822             StringBuilder sb = null;
823
824             do
825             {
826                 char[] tmpCharBuffer = CharBuffer_Prop;
827                 int tmpCharLen = CharLen_Prop;
828                 int tmpCharPos = CharPos_Prop;
829                 int i = tmpCharPos;
830
831                 do
832                 {
833                     char ch = tmpCharBuffer[i];
834
835                     // Note the following common line feed chars:
836                     // \n - UNIX   \r\n - DOS   \r - Mac
837                     if (ch == '\r' || ch == '\n')
838                     {
839                         String s;
840
841                         if (sb != null)
842                         {
843                             sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
844                             s = sb.ToString();
845                         }
846                         else
847                         {
848                             s = new String(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
849                         }
850
851                         CharPos_Prop = tmpCharPos = i + 1;
852
853                         if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync().ConfigureAwait(false)) > 0))
854                         {
855                             tmpCharPos = CharPos_Prop;
856                             if (CharBuffer_Prop[tmpCharPos] == '\n')
857                                 CharPos_Prop = ++tmpCharPos;
858                         }
859
860                         return s;
861                     }
862
863                     i++;
864
865                 } while (i < tmpCharLen);
866
867                 i = tmpCharLen - tmpCharPos;
868                 if (sb == null) sb = new StringBuilder(i + 80);
869                 sb.Append(tmpCharBuffer, tmpCharPos, i);
870
871             } while (await ReadBufferAsync().ConfigureAwait(false) > 0);
872
873             return sb.ToString();
874         }
875
876         [HostProtection(ExternalThreading=true)]
877         [ComVisible(false)]
878         public override Task<String> ReadToEndAsync()
879         {
880             // If we have been inherited into a subclass, the following implementation could be incorrect
881             // since it does not call through to Read() which a subclass might have overriden.  
882             // To be safe we will only use this implementation in cases where we know it is safe to do so,
883             // and delegate to our base class (which will call into Read) when we are not sure.
884             if (this.GetType() != typeof(StreamReader))
885                 return base.ReadToEndAsync();
886
887             if (stream == null)
888                 __Error.ReaderClosed();
889
890             CheckAsyncTaskInProgress();
891
892             Task<String> task = ReadToEndAsyncInternal();
893             _asyncReadTask = task;
894
895             return task;
896         }
897
898         private async Task<String> ReadToEndAsyncInternal()
899         {
900             // Call ReadBuffer, then pull data out of charBuffer.
901             StringBuilder sb = new StringBuilder(CharLen_Prop - CharPos_Prop);
902             do
903             {
904                 int tmpCharPos = CharPos_Prop;
905                 sb.Append(CharBuffer_Prop, tmpCharPos, CharLen_Prop - tmpCharPos);
906                 CharPos_Prop = CharLen_Prop;  // We consumed these characters
907                 await ReadBufferAsync().ConfigureAwait(false);
908             } while (CharLen_Prop > 0);
909
910             return sb.ToString();
911         }
912
913         [HostProtection(ExternalThreading=true)]
914         [ComVisible(false)]
915         public override Task<int> ReadAsync(char[] buffer, int index, int count)
916         {
917             if (buffer==null)
918                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
919             if (index < 0 || count < 0)
920                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
921             if (buffer.Length - index < count)
922                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
923             Contract.EndContractBlock();
924
925             // If we have been inherited into a subclass, the following implementation could be incorrect
926             // since it does not call through to Read() which a subclass might have overriden.  
927             // To be safe we will only use this implementation in cases where we know it is safe to do so,
928             // and delegate to our base class (which will call into Read) when we are not sure.
929             if (this.GetType() != typeof(StreamReader))
930                 return base.ReadAsync(buffer, index, count);
931
932             if (stream == null)
933                 __Error.ReaderClosed();
934
935             CheckAsyncTaskInProgress();
936
937             Task<int> task = ReadAsyncInternal(buffer, index, count);
938             _asyncReadTask = task;
939
940             return task;
941         }
942
943         internal override async Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
944         {
945             if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
946                 return 0;
947
948             int charsRead = 0;
949
950             // As a perf optimization, if we had exactly one buffer's worth of 
951             // data read in, let's try writing directly to the user's buffer.
952             bool readToUserBuffer = false;
953
954             Byte[] tmpByteBuffer = ByteBuffer_Prop;
955             Stream tmpStream = Stream_Prop;
956
957             while (count > 0)
958             {
959                 // n is the cha----ters avaialbe in _charBuffer
960                 int n = CharLen_Prop - CharPos_Prop;
961
962                 // charBuffer is empty, let's read from the stream
963                 if (n == 0)
964                 {
965                     CharLen_Prop = 0;
966                     CharPos_Prop = 0;
967
968                     if (!CheckPreamble_Prop)
969                         ByteLen_Prop = 0;
970
971                     readToUserBuffer = count >= MaxCharsPerBuffer_Prop;
972
973                     // We loop here so that we read in enough bytes to yield at least 1 char.
974                     // We break out of the loop if the stream is blocked (EOF is reached).
975                     do
976                     {
977                         Contract.Assert(n == 0);
978
979                         if (CheckPreamble_Prop)
980                         {
981                             Contract.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
982                             int tmpBytePos = BytePos_Prop;
983                             int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false);
984                             Contract.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
985
986                             if (len == 0)
987                             {
988                                 // EOF but we might have buffered bytes from previous 
989                                 // attempts to detect preamble that needs to be decoded now
990                                 if (ByteLen_Prop > 0)
991                                 {
992                                     if (readToUserBuffer)
993                                     {
994                                         n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead);
995                                         CharLen_Prop = 0;  // StreamReader's buffer is empty.
996                                     }
997                                     else
998                                     {
999                                         n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0);
1000                                         CharLen_Prop += n;  // Number of chars in StreamReader's buffer.
1001                                     }
1002                                 }
1003                                         
1004                                 // How can part of the preamble yield any chars?
1005                                 Contract.Assert(n == 0);
1006
1007                                 IsBlocked_Prop = true;
1008                                 break;
1009                             }
1010                             else
1011                             {
1012                                 ByteLen_Prop += len;
1013                             }
1014                         }
1015                         else
1016                         {
1017                             Contract.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble.  Are two threads using this StreamReader at the same time?");
1018
1019                             ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
1020
1021                             Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
1022
1023                             if (ByteLen_Prop == 0)  // EOF
1024                             {
1025                                 IsBlocked_Prop = true;
1026                                 break;
1027                             }
1028                         }
1029
1030                         // _isBlocked == whether we read fewer bytes than we asked for.
1031                         // Note we must check it here because CompressBuffer or 
1032                         // DetectEncoding will change _byteLen.
1033                         IsBlocked_Prop = (ByteLen_Prop < tmpByteBuffer.Length);
1034
1035                         // Check for preamble before detect encoding. This is not to override the
1036                         // user suppplied Encoding for the one we implicitly detect. The user could
1037                         // customize the encoding which we will loose, such as ThrowOnError on UTF8
1038                         // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
1039                         // doesn't change the encoding or affect _maxCharsPerBuffer
1040                         if (IsPreamble())
1041                             continue;
1042
1043                         // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
1044                         if (DetectEncoding_Prop && ByteLen_Prop >= 2)
1045                         {
1046                             DetectEncoding();
1047                             // DetectEncoding changes some buffer state.  Recompute this.
1048                             readToUserBuffer = count >= MaxCharsPerBuffer_Prop;
1049                         }
1050
1051                         Contract.Assert(n == 0);
1052
1053                         CharPos_Prop = 0;
1054                         if (readToUserBuffer)
1055                         {
1056                             n += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead);
1057                                         
1058                             // Why did the bytes yield no chars?
1059                             Contract.Assert(n > 0);
1060
1061                             CharLen_Prop = 0;  // StreamReader's buffer is empty.
1062                         }
1063                         else
1064                         {
1065                             n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0);
1066                                         
1067                             // Why did the bytes yield no chars?
1068                             Contract.Assert(n > 0);
1069
1070                             CharLen_Prop += n;  // Number of chars in StreamReader's buffer.
1071                         }
1072
1073                     } while (n == 0);
1074
1075                     if (n == 0) break;  // We're at EOF
1076                 }  // if (n == 0)
1077
1078                 // Got more chars in charBuffer than the user requested
1079                 if (n > count)
1080                     n = count;
1081
1082                 if (!readToUserBuffer)
1083                 {
1084                     Buffer.InternalBlockCopy(CharBuffer_Prop, CharPos_Prop * 2, buffer, (index + charsRead) * 2, n * 2);
1085                     CharPos_Prop += n;
1086                 }
1087
1088                 charsRead += n;
1089                 count -= n;
1090
1091                 // This function shouldn't block for an indefinite amount of time,
1092                 // or reading from a network stream won't work right.  If we got
1093                 // fewer bytes than we requested, then we want to break right here.
1094                 if (IsBlocked_Prop)
1095                     break;
1096             }  // while (count > 0)
1097
1098             return charsRead;
1099         }
1100
1101         [HostProtection(ExternalThreading=true)]
1102         [ComVisible(false)]
1103         public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
1104         {
1105             if (buffer==null)
1106                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
1107             if (index < 0 || count < 0)
1108                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
1109             if (buffer.Length - index < count)
1110                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
1111             Contract.EndContractBlock();
1112
1113             // If we have been inherited into a subclass, the following implementation could be incorrect
1114             // since it does not call through to Read() which a subclass might have overriden.  
1115             // To be safe we will only use this implementation in cases where we know it is safe to do so,
1116             // and delegate to our base class (which will call into Read) when we are not sure.
1117             if (this.GetType() != typeof(StreamReader))
1118                 return base.ReadBlockAsync(buffer, index, count);
1119
1120             if (stream == null)
1121                 __Error.ReaderClosed();
1122
1123             CheckAsyncTaskInProgress();
1124
1125             Task<int> task = base.ReadBlockAsync(buffer, index, count);
1126             _asyncReadTask = task;
1127
1128             return task;
1129         }
1130
1131         #region Private properties for async method performance
1132         // Access to instance fields of MarshalByRefObject-derived types requires special JIT helpers that check
1133         // if the instance operated on is remote. This is optimised for fields on \93this\94 but if a method is Async
1134         // and is thus lifted to a state machine type, access will be slow.
1135         // As a workaround, we either cache instance fields in locals or use properties to access such fields.
1136
1137         // See Dev11 
1138         
1139         private Int32 CharLen_Prop {
1140             get { return charLen; }
1141             set { charLen = value; }
1142         }
1143
1144         private Int32 CharPos_Prop {
1145             get { return charPos; }
1146             set { charPos = value; }
1147         }
1148
1149         private Int32 ByteLen_Prop {
1150             get { return byteLen; }
1151             set { byteLen = value; }
1152         }
1153
1154         private Int32 BytePos_Prop {
1155             get { return bytePos; }
1156             set { bytePos = value; }
1157         }
1158
1159         private Byte[] Preamble_Prop {
1160             get { return _preamble; }
1161         }
1162
1163         private bool CheckPreamble_Prop {
1164             get { return _checkPreamble; }
1165         }
1166
1167         private Decoder Decoder_Prop {
1168             get { return decoder; }
1169         }
1170
1171         private bool DetectEncoding_Prop {
1172             get { return _detectEncoding; }
1173         }
1174
1175         private Char[]  CharBuffer_Prop {
1176             get { return charBuffer; }
1177         }
1178
1179         private Byte[]  ByteBuffer_Prop {
1180             get { return byteBuffer; }
1181         }
1182
1183         private bool IsBlocked_Prop {            
1184             get { return _isBlocked; }
1185             set { _isBlocked = value; }
1186         }
1187
1188         private Stream Stream_Prop {
1189             get { return stream; }
1190         }
1191
1192         private Int32 MaxCharsPerBuffer_Prop {
1193             get { return _maxCharsPerBuffer; }
1194         }
1195         #endregion Private properties for async method performance
1196         private async Task<int> ReadBufferAsync()
1197         {
1198             CharLen_Prop = 0;
1199             CharPos_Prop = 0;
1200             Byte[] tmpByteBuffer = ByteBuffer_Prop;
1201             Stream tmpStream = Stream_Prop;
1202             
1203             if (!CheckPreamble_Prop)
1204                 ByteLen_Prop = 0;
1205             do {
1206                 if (CheckPreamble_Prop) {
1207                     Contract.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
1208                     int tmpBytePos = BytePos_Prop;
1209                     int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false);
1210                     Contract.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
1211                     
1212                     if (len == 0) {
1213                         // EOF but we might have buffered bytes from previous 
1214                         // attempt to detect preamble that needs to be decoded now
1215                         if (ByteLen_Prop > 0)
1216                         {
1217                             CharLen_Prop += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, CharLen_Prop);
1218                             // Need to zero out the _byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
1219                             BytePos_Prop = 0; ByteLen_Prop = 0;
1220                         }
1221                         
1222                         return CharLen_Prop;
1223                     }
1224                     
1225                     ByteLen_Prop += len;
1226                 }
1227                 else {
1228                     Contract.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
1229                     ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
1230                     Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number!  Bug in stream class.");
1231                     
1232                     if (ByteLen_Prop == 0)  // We're at EOF
1233                         return CharLen_Prop;
1234                 }
1235
1236                 // _isBlocked == whether we read fewer bytes than we asked for.
1237                 // Note we must check it here because CompressBuffer or 
1238                 // DetectEncoding will change _byteLen.
1239                 IsBlocked_Prop = (ByteLen_Prop < tmpByteBuffer.Length);
1240                 
1241                 // Check for preamble before detect encoding. This is not to override the
1242                 // user suppplied Encoding for the one we implicitly detect. The user could
1243                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
1244                 if (IsPreamble()) 
1245                     continue;
1246
1247                 // If we're supposed to detect the encoding and haven't done so yet,
1248                 // do it.  Note this may need to be called more than once.
1249                 if (DetectEncoding_Prop && ByteLen_Prop >= 2)
1250                     DetectEncoding();
1251
1252                 CharLen_Prop += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, CharLen_Prop);
1253             } while (CharLen_Prop == 0);
1254             
1255             return CharLen_Prop;
1256         }
1257         #endregion
1258 #endif //FEATURE_ASYNC_IO
1259
1260
1261         // No data, class doesn't need to be serializable.
1262         // Note this class is threadsafe.
1263         private class NullStreamReader : StreamReader
1264         {
1265             // Instantiating Encoding causes unnecessary perf hit. 
1266             internal NullStreamReader() {
1267                 Init(Stream.Null);
1268             }
1269
1270             public override Stream BaseStream {
1271                 get { return Stream.Null; }
1272             }
1273
1274             public override Encoding CurrentEncoding {
1275                 get { return Encoding.Unicode; }
1276             }
1277
1278             protected override void Dispose(bool disposing)
1279             {
1280                 // Do nothing - this is essentially unclosable.
1281             }
1282
1283             public override int Peek()
1284             {
1285                 return -1;
1286             }
1287
1288             public override int Read()
1289             {
1290                 return -1;
1291             }
1292
1293             [SuppressMessage("Microsoft.Contracts", "CC1055")]  // Skip extra error checking to avoid *potential* AppCompat problems.
1294             public override int Read(char[] buffer, int index, int count) {
1295                 return 0;
1296             }
1297             
1298             public override String ReadLine() {
1299                 return null;
1300             }
1301
1302             public override String ReadToEnd()
1303             {
1304                 return String.Empty;
1305             }
1306
1307             internal override int ReadBuffer() 
1308             {
1309                 return 0;
1310             }
1311
1312         }
1313     }
1314 }