3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
10 ** <OWNER>gpaperin</OWNER>
13 ** Purpose: For reading text from streams in a particular
17 ===========================================================*/
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;
27 using System.Threading.Tasks;
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.
37 [System.Runtime.InteropServices.ComVisible(true)]
38 public class StreamReader : TextReader
40 // StreamReader.Null is threadsafe.
41 public new static readonly StreamReader Null = new NullStreamReader();
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
52 #if FEATURE_LEGACYNETCF
53 // Quirk for Mango app compatibility
54 if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
58 #endif // FEATURE_LEGACYNETCF
63 private const int DefaultFileStreamBufferSize = 4096;
64 private const int MinBufferSize = 128;
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.
74 // Record the number of valid bytes in the byteBuffer, for a few checks.
76 // This is used only for preamble detection
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;
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;
89 // Whether we must still check for the encoding's given preamble at the
90 // beginning of this file.
91 private bool _checkPreamble;
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;
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.
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.
111 private volatile Task _asyncReadTask;
113 private void CheckAsyncTaskInProgress()
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.
118 Task t = _asyncReadTask;
120 if (t != null && !t.IsCompleted)
121 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_AsyncIOInProgress"));
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.
130 internal StreamReader() {
133 public StreamReader(Stream stream)
134 : this(stream, true) {
137 public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
138 : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
141 public StreamReader(Stream stream, Encoding encoding)
142 : this(stream, encoding, true, DefaultBufferSize, false) {
145 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
146 : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize, false) {
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.
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.
159 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
160 : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false) {
163 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
165 if (stream == null || encoding == null)
166 throw new ArgumentNullException((stream == null ? "stream" : "encoding"));
168 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
170 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
171 Contract.EndContractBlock();
173 Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen);
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)
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"),
196 #endif // FEATURE_LEGACYNETCF
199 [ResourceExposure(ResourceScope.Machine)]
200 [ResourceConsumption(ResourceScope.Machine)]
201 public StreamReader(String path, bool detectEncodingFromByteOrderMarks)
202 : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
205 [ResourceExposure(ResourceScope.Machine)]
206 [ResourceConsumption(ResourceScope.Machine)]
207 public StreamReader(String path, Encoding encoding)
208 : this(path, encoding, true, DefaultBufferSize) {
211 [ResourceExposure(ResourceScope.Machine)]
212 [ResourceConsumption(ResourceScope.Machine)]
213 public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
214 : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
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) {
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)
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"));
235 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
237 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
238 Contract.EndContractBlock();
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);
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];
254 _detectEncoding = detectEncodingFromByteOrderMarks;
255 _preamble = encoding.GetPreamble();
256 _checkPreamble = (_preamble.Length > 0);
258 _closable = !leaveOpen;
261 // Init used by NullStreamReader, to delay load encoding
262 internal void Init(Stream stream)
264 this.stream = stream;
268 public override void Close()
273 protected override void Dispose(bool disposing)
275 // Dispose of our resources if this StreamReader is closable.
276 // Note that Console.In should be left open.
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))
284 if (!LeaveOpen && (stream != null)) {
292 base.Dispose(disposing);
297 public virtual Encoding CurrentEncoding {
298 get { return encoding; }
301 public virtual Stream BaseStream {
302 get { return stream; }
305 internal bool LeaveOpen {
306 get { return !_closable; }
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()
320 CheckAsyncTaskInProgress();
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();
334 public bool EndOfStream {
337 __Error.ReaderClosed();
340 CheckAsyncTaskInProgress();
343 if (charPos < charLen)
346 // This may block on pipes!
347 int numRead = ReadBuffer();
353 public override int Peek() {
355 __Error.ReaderClosed();
358 CheckAsyncTaskInProgress();
361 if (charPos == charLen)
363 if (_isBlocked || ReadBuffer() == 0) return -1;
365 return charBuffer[charPos];
370 // Used internally by our console, as it previously depended on Peek() being a
371 // routine that would not block.
373 internal bool DataAvailable ()
375 return charPos < charLen;
379 public override int Read() {
381 __Error.ReaderClosed();
384 CheckAsyncTaskInProgress();
387 if (charPos == charLen) {
388 if (ReadBuffer() == 0) return -1;
390 int result = charBuffer[charPos];
395 public override int Read([In, Out] char[] buffer, int index, int count)
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();
406 __Error.ReaderClosed();
409 CheckAsyncTaskInProgress();
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;
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);
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.
437 public override String ReadToEnd()
440 __Error.ReaderClosed();
443 CheckAsyncTaskInProgress();
446 // Call ReadBuffer, then pull data out of charBuffer.
447 StringBuilder sb = new StringBuilder(charLen - charPos);
449 sb.Append(charBuffer, charPos, charLen - charPos);
450 charPos = charLen; // Note we consumed these characters
452 } while (charLen > 0);
453 return sb.ToString();
456 public override int ReadBlock([In, Out] char[] buffer, int index, int count)
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();
467 __Error.ReaderClosed();
470 CheckAsyncTaskInProgress();
473 return base.ReadBlock(buffer, index, count);
476 // Trims n bytes from the front of the buffer.
477 private void CompressBuffer(int n)
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);
484 private void DetectEncoding()
488 _detectEncoding = false;
489 bool changedEncoding = false;
490 if (byteBuffer[0]==0xFE && byteBuffer[1]==0xFF) {
491 // Big Endian Unicode
493 encoding = new UnicodeEncoding(true, true);
495 changedEncoding = true;
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);
503 changedEncoding = true;
507 encoding = new UTF32Encoding(false, true);
509 changedEncoding = true;
514 else if (byteLen >= 3 && byteBuffer[0]==0xEF && byteBuffer[1]==0xBB && byteBuffer[2]==0xBF) {
516 encoding = Encoding.UTF8;
518 changedEncoding = true;
521 else if (byteLen >= 4 && byteBuffer[0] == 0 && byteBuffer[1] == 0 &&
522 byteBuffer[2] == 0xFE && byteBuffer[3] == 0xFF) {
524 encoding = new UTF32Encoding(true, true);
526 changedEncoding = true;
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.
534 if (changedEncoding) {
535 decoder = encoding.GetDecoder();
536 _maxCharsPerBuffer = encoding.GetMaxCharCount(byteBuffer.Length);
537 charBuffer = new char[_maxCharsPerBuffer];
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()
549 return _checkPreamble;
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);
554 for(int i=0; i<len; i++, bytePos++) {
555 if (byteBuffer[bytePos] != _preamble[bytePos]) {
557 _checkPreamble = false;
562 Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
564 if (_checkPreamble) {
565 if (bytePos == _preamble.Length) {
567 CompressBuffer(_preamble.Length);
569 _checkPreamble = false;
570 _detectEncoding = false;
574 return _checkPreamble;
577 internal virtual int ReadBuffer() {
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.");
590 // EOF but we might have buffered bytes from previous
591 // attempt to detect preamble that needs to be decoded now
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;
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.");
609 if (byteLen == 0) // We're at EOF
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);
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
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)
629 charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
630 } while (charLen == 0);
631 //Console.WriteLine("ReadBuffer called. chars: "+charLen);
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)
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;
667 Contract.Assert(charsRead == 0);
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.");
675 // EOF but we might have buffered bytes from previous
676 // attempt to detect preamble that needs to be decoded now
678 if (readToUserBuffer) {
679 charsRead = decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
680 charLen = 0; // StreamReader's buffer is empty.
683 charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
684 charLen += charsRead; // Number of chars in StreamReader's buffer.
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?");
696 byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
698 Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
700 if (byteLen == 0) // EOF
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);
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
717 // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
718 if (_detectEncoding && byteLen >= 2) {
720 // DetectEncoding changes some buffer state. Recompute this.
721 readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
725 if (readToUserBuffer) {
726 charsRead += decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
727 charLen = 0; // StreamReader's buffer is empty.
730 charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
731 charLen += charsRead; // Number of chars in StreamReader's buffer.
733 } while (charsRead == 0);
735 _isBlocked &= charsRead < desiredChars;
737 //Console.WriteLine("ReadBuffer: charsRead: "+charsRead+" readToUserBuffer: "+readToUserBuffer);
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.
748 public override String ReadLine()
751 __Error.ReaderClosed();
754 CheckAsyncTaskInProgress();
757 if (charPos == charLen)
759 if (ReadBuffer() == 0) return null;
762 StringBuilder sb = null;
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') {
772 sb.Append(charBuffer, charPos, i - charPos);
776 s = new String(charBuffer, charPos, i - charPos);
779 if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0)) {
780 if (charBuffer[charPos] == '\n') charPos++;
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();
794 #region Task based Async APIs
795 [HostProtection(ExternalThreading=true)]
797 public override Task<String> ReadLineAsync()
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();
807 __Error.ReaderClosed();
809 CheckAsyncTaskInProgress();
811 Task<String> task = ReadLineAsyncInternal();
812 _asyncReadTask = task;
817 private async Task<String> ReadLineAsyncInternal()
819 if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
822 StringBuilder sb = null;
826 char[] tmpCharBuffer = CharBuffer_Prop;
827 int tmpCharLen = CharLen_Prop;
828 int tmpCharPos = CharPos_Prop;
833 char ch = tmpCharBuffer[i];
835 // Note the following common line feed chars:
836 // \n - UNIX \r\n - DOS \r - Mac
837 if (ch == '\r' || ch == '\n')
843 sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
848 s = new String(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
851 CharPos_Prop = tmpCharPos = i + 1;
853 if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync().ConfigureAwait(false)) > 0))
855 tmpCharPos = CharPos_Prop;
856 if (CharBuffer_Prop[tmpCharPos] == '\n')
857 CharPos_Prop = ++tmpCharPos;
865 } while (i < tmpCharLen);
867 i = tmpCharLen - tmpCharPos;
868 if (sb == null) sb = new StringBuilder(i + 80);
869 sb.Append(tmpCharBuffer, tmpCharPos, i);
871 } while (await ReadBufferAsync().ConfigureAwait(false) > 0);
873 return sb.ToString();
876 [HostProtection(ExternalThreading=true)]
878 public override Task<String> ReadToEndAsync()
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();
888 __Error.ReaderClosed();
890 CheckAsyncTaskInProgress();
892 Task<String> task = ReadToEndAsyncInternal();
893 _asyncReadTask = task;
898 private async Task<String> ReadToEndAsyncInternal()
900 // Call ReadBuffer, then pull data out of charBuffer.
901 StringBuilder sb = new StringBuilder(CharLen_Prop - CharPos_Prop);
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);
910 return sb.ToString();
913 [HostProtection(ExternalThreading=true)]
915 public override Task<int> ReadAsync(char[] buffer, int index, int count)
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();
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);
933 __Error.ReaderClosed();
935 CheckAsyncTaskInProgress();
937 Task<int> task = ReadAsyncInternal(buffer, index, count);
938 _asyncReadTask = task;
943 internal override async Task<int> ReadAsyncInternal(char[] buffer, int index, int count)
945 if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
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;
954 Byte[] tmpByteBuffer = ByteBuffer_Prop;
955 Stream tmpStream = Stream_Prop;
959 // n is the cha----ters avaialbe in _charBuffer
960 int n = CharLen_Prop - CharPos_Prop;
962 // charBuffer is empty, let's read from the stream
968 if (!CheckPreamble_Prop)
971 readToUserBuffer = count >= MaxCharsPerBuffer_Prop;
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).
977 Contract.Assert(n == 0);
979 if (CheckPreamble_Prop)
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.");
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)
992 if (readToUserBuffer)
994 n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead);
995 CharLen_Prop = 0; // StreamReader's buffer is empty.
999 n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0);
1000 CharLen_Prop += n; // Number of chars in StreamReader's buffer.
1004 // How can part of the preamble yield any chars?
1005 Contract.Assert(n == 0);
1007 IsBlocked_Prop = true;
1012 ByteLen_Prop += len;
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?");
1019 ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false);
1021 Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
1023 if (ByteLen_Prop == 0) // EOF
1025 IsBlocked_Prop = true;
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);
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
1043 // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
1044 if (DetectEncoding_Prop && ByteLen_Prop >= 2)
1047 // DetectEncoding changes some buffer state. Recompute this.
1048 readToUserBuffer = count >= MaxCharsPerBuffer_Prop;
1051 Contract.Assert(n == 0);
1054 if (readToUserBuffer)
1056 n += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead);
1058 // Why did the bytes yield no chars?
1059 Contract.Assert(n > 0);
1061 CharLen_Prop = 0; // StreamReader's buffer is empty.
1065 n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0);
1067 // Why did the bytes yield no chars?
1068 Contract.Assert(n > 0);
1070 CharLen_Prop += n; // Number of chars in StreamReader's buffer.
1075 if (n == 0) break; // We're at EOF
1078 // Got more chars in charBuffer than the user requested
1082 if (!readToUserBuffer)
1084 Buffer.InternalBlockCopy(CharBuffer_Prop, CharPos_Prop * 2, buffer, (index + charsRead) * 2, n * 2);
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.
1096 } // while (count > 0)
1101 [HostProtection(ExternalThreading=true)]
1103 public override Task<int> ReadBlockAsync(char[] buffer, int index, int count)
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();
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);
1121 __Error.ReaderClosed();
1123 CheckAsyncTaskInProgress();
1125 Task<int> task = base.ReadBlockAsync(buffer, index, count);
1126 _asyncReadTask = task;
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.
1139 private Int32 CharLen_Prop {
1140 get { return charLen; }
1141 set { charLen = value; }
1144 private Int32 CharPos_Prop {
1145 get { return charPos; }
1146 set { charPos = value; }
1149 private Int32 ByteLen_Prop {
1150 get { return byteLen; }
1151 set { byteLen = value; }
1154 private Int32 BytePos_Prop {
1155 get { return bytePos; }
1156 set { bytePos = value; }
1159 private Byte[] Preamble_Prop {
1160 get { return _preamble; }
1163 private bool CheckPreamble_Prop {
1164 get { return _checkPreamble; }
1167 private Decoder Decoder_Prop {
1168 get { return decoder; }
1171 private bool DetectEncoding_Prop {
1172 get { return _detectEncoding; }
1175 private Char[] CharBuffer_Prop {
1176 get { return charBuffer; }
1179 private Byte[] ByteBuffer_Prop {
1180 get { return byteBuffer; }
1183 private bool IsBlocked_Prop {
1184 get { return _isBlocked; }
1185 set { _isBlocked = value; }
1188 private Stream Stream_Prop {
1189 get { return stream; }
1192 private Int32 MaxCharsPerBuffer_Prop {
1193 get { return _maxCharsPerBuffer; }
1195 #endregion Private properties for async method performance
1196 private async Task<int> ReadBufferAsync()
1200 Byte[] tmpByteBuffer = ByteBuffer_Prop;
1201 Stream tmpStream = Stream_Prop;
1203 if (!CheckPreamble_Prop)
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.");
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)
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;
1222 return CharLen_Prop;
1225 ByteLen_Prop += len;
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.");
1232 if (ByteLen_Prop == 0) // We're at EOF
1233 return CharLen_Prop;
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);
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
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)
1252 CharLen_Prop += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, CharLen_Prop);
1253 } while (CharLen_Prop == 0);
1255 return CharLen_Prop;
1258 #endif //FEATURE_ASYNC_IO
1261 // No data, class doesn't need to be serializable.
1262 // Note this class is threadsafe.
1263 private class NullStreamReader : StreamReader
1265 // Instantiating Encoding causes unnecessary perf hit.
1266 internal NullStreamReader() {
1270 public override Stream BaseStream {
1271 get { return Stream.Null; }
1274 public override Encoding CurrentEncoding {
1275 get { return Encoding.Unicode; }
1278 protected override void Dispose(bool disposing)
1280 // Do nothing - this is essentially unclosable.
1283 public override int Peek()
1288 public override int Read()
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) {
1298 public override String ReadLine() {
1302 public override String ReadToEnd()
1304 return String.Empty;
1307 internal override int ReadBuffer()