The Labs \ Source Viewer \ SSCLI \ System.IO \ NullStreamReader

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  StreamReader
  18. **
  19. **
  20. ** Purpose: For reading text from streams in a particular
  21. ** encoding.
  22. **
  23. **
  24. ===========================================================*/
  25. using System;
  26. using System.Text;
  27. using System.Runtime.InteropServices;
  28. using System.Runtime.Versioning;
  29. namespace System.IO
  30. {
  31.     // This class implements a TextReader for reading characters to a Stream.
  32.     // This is designed for character input in a particular Encoding,
  33.     // whereas the Stream class is designed for byte input and output.
  34.     //
  35.     [Serializable()]
  36.     [System.Runtime.InteropServices.ComVisible(true)]
  37.     public class StreamReader : TextReader
  38.     {
  39.         // StreamReader.Null is threadsafe.
  40.         public static new readonly StreamReader Null = new NullStreamReader();
  41.        
  42.         // Using a 1K byte buffer and a 4K FileStream buffer works out pretty well
  43.         // perf-wise. On even a 40 MB text file, any perf loss by using a 4K
  44.         // buffer is negated by the win of allocating a smaller byte[], which
  45.         // saves construction time. This does break adaptive buffering,
  46.         // but this is slightly faster.
  47.         internal const int DefaultBufferSize = 1024;
  48.         // Byte buffer size
  49.         private const int DefaultFileStreamBufferSize = 4096;
  50.         private const int MinBufferSize = 128;
  51.        
  52.         private bool _closable;
  53.        
  54.         private Stream stream;
  55.         private Encoding encoding;
  56.         private Decoder decoder;
  57.         private byte[] byteBuffer;
  58.         private char[] charBuffer;
  59.         private byte[] _preamble;
  60.         // Encoding's preamble, which identifies this encoding.
  61.         private int charPos;
  62.         private int charLen;
  63.         // Record the number of valid bytes in the byteBuffer, for a few checks.
  64.         private int byteLen;
  65.         // This is used only for preamble detection
  66.         private int bytePos;
  67.        
  68.         // This is the maximum number of chars we can get from one call to
  69.         // ReadBuffer. Used so ReadBuffer can tell when to copy data into
  70.         // a user's char[] directly, instead of our internal char[].
  71.         private int _maxCharsPerBuffer;
  72.        
  73.         // We will support looking for byte order marks in the stream and trying
  74.         // to decide what the encoding might be from the byte order marks, IF they
  75.         // exist. But that's all we'll do.
  76.         private bool _detectEncoding;
  77.        
  78.         // Whether we must still check for the encoding's given preamble at the
  79.         // beginning of this file.
  80.         private bool _checkPreamble;
  81.        
  82.         // Whether the stream is most likely not going to give us back as much
  83.         // data as we want the next time we call it. We must do the computation
  84.         // before we do any byte order mark handling and save the result. Note
  85.         // that we need this to allow users to handle streams used for an
  86.         // interactive protocol, where they block waiting for the remote end
  87.         // to send a response, like logging in on a Unix machine.
  88.         private bool _isBlocked;
  89.        
  90.         // StreamReader by default will ignore illegal UTF8 characters. We don't want to
  91.         // throw here because we want to be able to read ill-formed data without choking.
  92.         // The high level goal is to be tolerant of encoding errors when we read and very strict
  93.         // when we write. Hence, default StreamWriter encoding will throw on error.
  94.        
  95.         internal StreamReader()
  96.         {
  97.         }
  98.        
  99.         public StreamReader(Stream stream) : this(stream, true)
  100.         {
  101.         }
  102.        
  103.         public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks) : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize)
  104.         {
  105.         }
  106.        
  107.         public StreamReader(Stream stream, Encoding encoding) : this(stream, encoding, true, DefaultBufferSize)
  108.         {
  109.         }
  110.        
  111.         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks) : this(stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize)
  112.         {
  113.         }
  114.        
  115.         // Creates a new StreamReader for the given stream. The
  116.         // character encoding is set by encoding and the buffer size,
  117.         // in number of 16-bit characters, is set by bufferSize.
  118.         //
  119.         // Note that detectEncodingFromByteOrderMarks is a very
  120.         // loose attempt at detecting the encoding by looking at the first
  121.         // 3 bytes of the stream. It will recognize UTF-8, little endian
  122.         // unicode, and big endian unicode text, but that's it. If neither
  123.         // of those three match, it will use the Encoding you provided.
  124.         //
  125.         public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
  126.         {
  127.             if (stream == null || encoding == null)
  128.                 throw new ArgumentNullException((stream == null ? "stream" : "encoding"));
  129.             if (!stream.CanRead)
  130.                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
  131.             if (bufferSize <= 0)
  132.                 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
  133.            
  134.             Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize);
  135.         }
  136.        
  137.         // For non closable streams such as Console.In
  138.         internal StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool closable) : this(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize)
  139.         {
  140.             _closable = closable;
  141.         }
  142.        
  143.         [ResourceExposure(ResourceScope.Machine)]
  144.         [ResourceConsumption(ResourceScope.Machine)]
  145.         public StreamReader(string path) : this(path, true)
  146.         {
  147.         }
  148.        
  149.         [ResourceExposure(ResourceScope.Machine)]
  150.         [ResourceConsumption(ResourceScope.Machine)]
  151.         public StreamReader(string path, bool detectEncodingFromByteOrderMarks) : this(path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize)
  152.         {
  153.         }
  154.        
  155.         [ResourceExposure(ResourceScope.Machine)]
  156.         [ResourceConsumption(ResourceScope.Machine)]
  157.         public StreamReader(string path, Encoding encoding) : this(path, encoding, true, DefaultBufferSize)
  158.         {
  159.         }
  160.        
  161.         [ResourceExposure(ResourceScope.Machine)]
  162.         [ResourceConsumption(ResourceScope.Machine)]
  163.         public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks) : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize)
  164.         {
  165.         }
  166.        
  167.         [ResourceExposure(ResourceScope.Machine)]
  168.         [ResourceConsumption(ResourceScope.Machine)]
  169.         public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
  170.         {
  171.             // Don't open a Stream before checking for invalid arguments,
  172.             // or we'll create a FileStream on disk and we won't close it until
  173.             // the finalizer runs, causing problems for applications.
  174.             if (path == null || encoding == null)
  175.                 throw new ArgumentNullException((path == null ? "path" : "encoding"));
  176.             if (path.Length == 0)
  177.                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"));
  178.             if (bufferSize <= 0)
  179.                 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
  180.            
  181.             Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan);
  182.             Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize);
  183.         }
  184.        
  185.         private void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
  186.         {
  187.             this.stream = stream;
  188.             this.encoding = encoding;
  189.             decoder = encoding.GetDecoder();
  190.             if (bufferSize < MinBufferSize)
  191.                 bufferSize = MinBufferSize;
  192.             byteBuffer = new byte[bufferSize];
  193.             _maxCharsPerBuffer = encoding.GetMaxCharCount(bufferSize);
  194.             charBuffer = new char[_maxCharsPerBuffer];
  195.             byteLen = 0;
  196.             bytePos = 0;
  197.             _detectEncoding = detectEncodingFromByteOrderMarks;
  198.             _preamble = encoding.GetPreamble();
  199.             _checkPreamble = (_preamble.Length > 0);
  200.             _isBlocked = false;
  201.             _closable = true;
  202.             // ByDefault all streams are closable unless explicitly told otherwise
  203.         }
  204.        
  205.         public override void Close()
  206.         {
  207.             Dispose(true);
  208.         }
  209.        
  210.         protected override void Dispose(bool disposing)
  211.         {
  212.             // Dispose of our resources if this StreamReader is closable.
  213.             // Note that Console.In should not be closable.
  214.             try {
  215.                 // Note that Stream.Close() can potentially throw here. So we need to
  216.                 // ensure cleaning up internal resources, inside the finally block.
  217.                 if (Closable && disposing && (stream != null))
  218.                     stream.Close();
  219.             }
  220.             finally {
  221.                 if (Closable && (stream != null)) {
  222.                     stream = null;
  223.                     encoding = null;
  224.                     decoder = null;
  225.                     byteBuffer = null;
  226.                     charBuffer = null;
  227.                     charPos = 0;
  228.                     charLen = 0;
  229.                     base.Dispose(disposing);
  230.                 }
  231.             }
  232.         }
  233.        
  234.         public virtual Encoding CurrentEncoding {
  235.             get { return encoding; }
  236.         }
  237.        
  238.         public virtual Stream BaseStream {
  239.             get { return stream; }
  240.         }
  241.        
  242.         internal bool Closable {
  243.             get { return _closable; }
  244.         }
  245.         //set { _closable = value; }
  246.        
  247.         // DiscardBufferedData tells StreamReader to throw away its internal
  248.         // buffer contents. This is useful if the user needs to seek on the
  249.         // underlying stream to a known location then wants the StreamReader
  250.         // to start reading from this new point. This method should be called
  251.         // very sparingly, if ever, since it can lead to very poor performance.
  252.         // However, it may be the only way of handling some scenarios where
  253.         // users need to re-read the contents of a StreamReader a second time.
  254.         public void DiscardBufferedData()
  255.         {
  256.             byteLen = 0;
  257.             charLen = 0;
  258.             charPos = 0;
  259.             decoder = encoding.GetDecoder();
  260.             _isBlocked = false;
  261.         }
  262.        
  263.         public bool EndOfStream {
  264.             get {
  265.                 if (stream == null)
  266.                     __Error.ReaderClosed();
  267.                
  268.                 if (charPos < charLen)
  269.                     return false;
  270.                
  271.                 // This may block on pipes!
  272.                 int numRead = ReadBuffer();
  273.                 return numRead == 0;
  274.             }
  275.         }
  276.        
  277.         public override int Peek()
  278.         {
  279.             if (stream == null)
  280.                 __Error.ReaderClosed();
  281.            
  282.             if (charPos == charLen) {
  283.                 if (_isBlocked || ReadBuffer() == 0)
  284.                     return -1;
  285.             }
  286.             return charBuffer[charPos];
  287.         }
  288.        
  289.         public override int Read()
  290.         {
  291.             if (stream == null)
  292.                 __Error.ReaderClosed();
  293.            
  294.             if (charPos == charLen) {
  295.                 if (ReadBuffer() == 0)
  296.                     return -1;
  297.             }
  298.             int result = charBuffer[charPos];
  299.             charPos++;
  300.             return result;
  301.         }
  302.        
  303.         public override int Read(        [In(), Out()]
  304. char[] buffer, int index, int count)
  305.         {
  306.             if (stream == null)
  307.                 __Error.ReaderClosed();
  308.             if (buffer == null)
  309.                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
  310.             if (index < 0 || count < 0)
  311.                 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  312.             if (buffer.Length - index < count)
  313.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  314.            
  315.             int charsRead = 0;
  316.             // As a perf optimization, if we had exactly one buffer's worth of
  317.             // data read in, let's try writing directly to the user's buffer.
  318.             bool readToUserBuffer = false;
  319.             while (count > 0) {
  320.                 int n = charLen - charPos;
  321.                 if (n == 0)
  322.                     n = ReadBuffer(buffer, index + charsRead, count, out readToUserBuffer);
  323.                 if (n == 0)
  324.                     break;
  325.                 // We're at EOF
  326.                 if (n > count)
  327.                     n = count;
  328.                 if (!readToUserBuffer) {
  329.                     Buffer.InternalBlockCopy(charBuffer, charPos * 2, buffer, (index + charsRead) * 2, n * 2);
  330.                     charPos += n;
  331.                 }
  332.                 charsRead += n;
  333.                 count -= n;
  334.                 if (_isBlocked)
  335.                     break;
  336.             }
  337.             return charsRead;
  338.         }
  339.        
  340.         public override string ReadToEnd()
  341.         {
  342.             if (stream == null)
  343.                 __Error.ReaderClosed();
  344.            
  345.             // Call ReadBuffer, then pull data out of charBuffer.
  346.             StringBuilder sb = new StringBuilder(charLen - charPos);
  347.             do {
  348.                 sb.Append(charBuffer, charPos, charLen - charPos);
  349.                 charPos = charLen;
  350.                 // Note we consumed these characters
  351.                 ReadBuffer();
  352.             }
  353.             while (charLen > 0);
  354.             return sb.ToString();
  355.         }
  356.        
  357.         // Trims n bytes from the front of the buffer.
  358.         private void CompressBuffer(int n)
  359.         {
  360.             BCLDebug.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?");
  361.             Buffer.InternalBlockCopy(byteBuffer, n, byteBuffer, 0, byteLen - n);
  362.             byteLen -= n;
  363.         }
  364.        
  365.         private void DetectEncoding()
  366.         {
  367.             if (byteLen < 2)
  368.                 return;
  369.             _detectEncoding = false;
  370.             bool changedEncoding = false;
  371.             if (byteBuffer[0] == 254 && byteBuffer[1] == 255) {
  372.                 // Big Endian Unicode
  373.                
  374.                 encoding = new UnicodeEncoding(true, true);
  375.                 CompressBuffer(2);
  376.                 changedEncoding = true;
  377.             }
  378.             else if (byteBuffer[0] == 255 && byteBuffer[1] == 254) {
  379.                 // Little Endian Unicode, or possibly little endian UTF32
  380.                 if (byteLen >= 4 && byteBuffer[2] == 0 && byteBuffer[3] == 0) {
  381.                     encoding = new UTF32Encoding(false, true);
  382.                     CompressBuffer(4);
  383.                 }
  384.                 else {
  385.                     encoding = new UnicodeEncoding(false, true);
  386.                     CompressBuffer(2);
  387.                 }
  388.                 changedEncoding = true;
  389.             }
  390.             else if (byteLen >= 3 && byteBuffer[0] == 239 && byteBuffer[1] == 187 && byteBuffer[2] == 191) {
  391.                 // UTF-8
  392.                 encoding = Encoding.UTF8;
  393.                 CompressBuffer(3);
  394.                 changedEncoding = true;
  395.             }
  396.             else if (byteLen >= 4 && byteBuffer[0] == 0 && byteBuffer[1] == 0 && byteBuffer[2] == 254 && byteBuffer[3] == 255) {
  397.                 // Big Endian UTF32
  398.                 encoding = new UTF32Encoding(true, true);
  399.                 changedEncoding = true;
  400.             }
  401.             else if (byteLen == 2)
  402.                 _detectEncoding = true;
  403.             // Note: in the future, if we change this algorithm significantly,
  404.             // we can support checking for the preamble of the given encoding.
  405.            
  406.             if (changedEncoding) {
  407.                 decoder = encoding.GetDecoder();
  408.                 _maxCharsPerBuffer = encoding.GetMaxCharCount(byteBuffer.Length);
  409.                 charBuffer = new char[_maxCharsPerBuffer];
  410.             }
  411.         }
  412.        
  413.         // Trims the preamble bytes from the byteBuffer. This routine can be called multiple times
  414.         // and we will buffer the bytes read until the preamble is matched or we determine that
  415.         // there is no match. If there is no match, every byte read previously will be available
  416.         // for further consumption. If there is a match, we will compress the buffer for the
  417.         // leading preamble bytes
  418.         private bool IsPreamble()
  419.         {
  420.             if (!_checkPreamble)
  421.                 return _checkPreamble;
  422.            
  423.             BCLDebug.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?");
  424.             int len = (byteLen >= (_preamble.Length)) ? (_preamble.Length - bytePos) : (byteLen - bytePos);
  425.            
  426.             for (int i = 0; i < len; i++,bytePos++) {
  427.                 if (byteBuffer[bytePos] != _preamble[bytePos]) {
  428.                     bytePos = 0;
  429.                     _checkPreamble = false;
  430.                     break;
  431.                 }
  432.             }
  433.            
  434.             BCLDebug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
  435.            
  436.             if (_checkPreamble) {
  437.                 if (bytePos == _preamble.Length) {
  438.                     // We have a match
  439.                     CompressBuffer(_preamble.Length);
  440.                     bytePos = 0;
  441.                     _checkPreamble = false;
  442.                     _detectEncoding = false;
  443.                 }
  444.             }
  445.            
  446.             return _checkPreamble;
  447.         }
  448.        
  449.         private int ReadBuffer()
  450.         {
  451.             charLen = 0;
  452.             charPos = 0;
  453.            
  454.             if (!_checkPreamble)
  455.                 byteLen = 0;
  456.             do {
  457.                 if (_checkPreamble) {
  458.                     BCLDebug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
  459.                     int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
  460.                     BCLDebug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
  461.                    
  462.                     if (len == 0) {
  463.                         // EOF but we might have buffered bytes from previous
  464.                         // attempts to detecting preamble that needs to decoded now
  465.                         if (byteLen > 0)
  466.                             charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
  467.                        
  468.                         return charLen;
  469.                     }
  470.                    
  471.                     byteLen += len;
  472.                 }
  473.                 else {
  474.                     BCLDebug.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?");
  475.                     byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
  476.                     BCLDebug.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
  477.                    
  478.                     if (byteLen == 0)
  479.                         // We're at EOF
  480.                         return charLen;
  481.                 }
  482.                
  483.                 _isBlocked = (byteLen < byteBuffer.Length);
  484.                
  485.                 // Check for preamble before detect encoding. This is not to override the
  486.                 // user suppplied Encoding for the one we implicitly detect. The user could
  487.                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
  488.                 if (IsPreamble())
  489.                     continue;
  490.                
  491.                 // If we're supposed to detect the encoding and haven't done so yet,
  492.                 // do it. Note this may need to be called more than once.
  493.                 if (_detectEncoding && byteLen >= 2)
  494.                     DetectEncoding();
  495.                
  496.                 charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);
  497.             }
  498.             while (charLen == 0);
  499.             //Console.WriteLine("ReadBuffer called. chars: "+charLen);
  500.             return charLen;
  501.         }
  502.        
  503.        
  504.         // This version has a perf optimization to decode data DIRECTLY into the
  505.         // user's buffer, bypassing StreamWriter's own buffer.
  506.         // This gives a > 20% perf improvement for our encodings across the board,
  507.         // but only when asking for at least the number of characters that one
  508.         // buffer's worth of bytes could produce.
  509.         // This optimization, if run, will break SwitchEncoding, so we must not do
  510.         // this on the first call to ReadBuffer.
  511.         private int ReadBuffer(char[] userBuffer, int userOffset, int desiredChars, out bool readToUserBuffer)
  512.         {
  513.             charLen = 0;
  514.             charPos = 0;
  515.            
  516.             if (!_checkPreamble)
  517.                 byteLen = 0;
  518.            
  519.             int charsRead = 0;
  520.            
  521.             // As a perf optimization, we can decode characters DIRECTLY into a
  522.             // user's char[]. We absolutely must not write more characters
  523.             // into the user's buffer than they asked for. Calculating
  524.             // encoding.GetMaxCharCount(byteLen) each time is potentially very
  525.             // expensive - instead, cache the number of chars a full buffer's
  526.             // worth of data may produce. Yes, this makes the perf optimization
  527.             // less aggressive, in that all reads that asked for fewer than AND
  528.             // returned fewer than _maxCharsPerBuffer chars won't get the user
  529.             // buffer optimization. This affects reads where the end of the
  530.             // Stream comes in the middle somewhere, and when you ask for
  531.             // fewer chars than than your buffer could produce.
  532.             readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
  533.            
  534.             do {
  535.                 if (_checkPreamble) {
  536.                     BCLDebug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
  537.                     int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
  538.                     BCLDebug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
  539.                    
  540.                     if (len == 0) {
  541.                         // EOF but we might have buffered bytes from previous
  542.                         // attempts to detecting preamble that needs to decoded now
  543.                         if (byteLen > 0) {
  544.                             if (readToUserBuffer) {
  545.                                 charsRead += decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
  546.                                 charLen = 0;
  547.                                 // StreamReader's buffer is empty.
  548.                             }
  549.                             else {
  550.                                 charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
  551.                                 charLen += charsRead;
  552.                                 // Number of chars in StreamReader's buffer.
  553.                             }
  554.                         }
  555.                         return charsRead;
  556.                     }
  557.                    
  558.                     byteLen += len;
  559.                 }
  560.                 else {
  561.                     BCLDebug.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?");
  562.                     byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
  563.                     BCLDebug.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
  564.                    
  565.                     if (byteLen == 0)
  566.                         // EOF
  567.                         return charsRead;
  568.                 }
  569.                
  570.                 _isBlocked = (byteLen < byteBuffer.Length);
  571.                
  572.                 // Check for preamble before detect encoding. This is not to override the
  573.                 // user suppplied Encoding for the one we implicitly detect. The user could
  574.                 // customize the encoding which we will loose, such as ThrowOnError on UTF8
  575.                 // Note: we don't need to recompute readToUserBuffer optimization as IsPreamble
  576.                 // doesn't change the encoding or affect _maxCharsPerBuffer
  577.                 if (IsPreamble())
  578.                     continue;
  579.                
  580.                 // On the first call to ReadBuffer, if we're supposed to detect the encoding, do it.
  581.                 if (_detectEncoding && byteLen >= 2) {
  582.                     DetectEncoding();
  583.                     // DetectEncoding changes some buffer state. Recompute this.
  584.                     readToUserBuffer = desiredChars >= _maxCharsPerBuffer;
  585.                 }
  586.                
  587.                 charPos = 0;
  588.                 if (readToUserBuffer) {
  589.                     charsRead += decoder.GetChars(byteBuffer, 0, byteLen, userBuffer, userOffset + charsRead);
  590.                     charLen = 0;
  591.                     // StreamReader's buffer is empty.
  592.                 }
  593.                 else {
  594.                     charsRead = decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charsRead);
  595.                     charLen += charsRead;
  596.                     // Number of chars in StreamReader's buffer.
  597.                 }
  598.             }
  599.             while (charsRead == 0);
  600.            
  601.             _isBlocked &= charsRead < desiredChars;
  602.            
  603.             //Console.WriteLine("ReadBuffer: charsRead: "+charsRead+" readToUserBuffer: "+readToUserBuffer);
  604.             return charsRead;
  605.         }
  606.        
  607.        
  608.         // Reads a line. A line is defined as a sequence of characters followed by
  609.         // a carriage return ('\r'), a line feed ('\n'), or a carriage return
  610.         // immediately followed by a line feed. The resulting string does not
  611.         // contain the terminating carriage return and/or line feed. The returned
  612.         // value is null if the end of the input stream has been reached.
  613.         //
  614.         public override string ReadLine()
  615.         {
  616.             if (stream == null)
  617.                 __Error.ReaderClosed();
  618.            
  619.             if (charPos == charLen) {
  620.                 if (ReadBuffer() == 0)
  621.                     return null;
  622.             }
  623.             StringBuilder sb = null;
  624.             do {
  625.                 int i = charPos;
  626.                 do {
  627.                     char ch = charBuffer[i];
  628.                     // Note the following common line feed chars:
  629.                     // \n - UNIX \r\n - DOS \r - Mac
  630.                     if (ch == '\r' || ch == '\n') {
  631.                         string s;
  632.                         if (sb != null) {
  633.                             sb.Append(charBuffer, charPos, i - charPos);
  634.                             s = sb.ToString();
  635.                         }
  636.                         else {
  637.                             s = new string(charBuffer, charPos, i - charPos);
  638.                         }
  639.                         charPos = i + 1;
  640.                         if (ch == '\r' && (charPos < charLen || ReadBuffer() > 0)) {
  641.                             if (charBuffer[charPos] == '\n')
  642.                                 charPos++;
  643.                         }
  644.                         return s;
  645.                     }
  646.                     i++;
  647.                 }
  648.                 while (i < charLen);
  649.                 i = charLen - charPos;
  650.                 if (sb == null)
  651.                     sb = new StringBuilder(i + 80);
  652.                 sb.Append(charBuffer, charPos, i);
  653.             }
  654.             while (ReadBuffer() > 0);
  655.             return sb.ToString();
  656.         }
  657.        
  658.         // No data, class doesn't need to be serializable.
  659.         // Note this class is threadsafe.
  660.         private class NullStreamReader : StreamReader
  661.         {
  662.             internal NullStreamReader() : base(Stream.Null, Encoding.Unicode, false, 1)
  663.             {
  664.             }
  665.            
  666.             public override Stream BaseStream {
  667.                 get { return Stream.Null; }
  668.             }
  669.            
  670.             public override Encoding CurrentEncoding {
  671.                 get { return Encoding.Unicode; }
  672.             }
  673.            
  674.             protected override void Dispose(bool disposing)
  675.             {
  676.                 // Do nothing - this is essentially unclosable.
  677.             }
  678.            
  679.             public override int Peek()
  680.             {
  681.                 return -1;
  682.             }
  683.            
  684.             public override int Read()
  685.             {
  686.                 return -1;
  687.             }
  688.            
  689.             public override int Read(char[] buffer, int index, int count)
  690.             {
  691.                 return 0;
  692.             }
  693.            
  694.             public override string ReadLine()
  695.             {
  696.                 return null;
  697.             }
  698.            
  699.             public override string ReadToEnd()
  700.             {
  701.                 return String.Empty;
  702.             }
  703.         }
  704.     }
  705. }

Developer Fusion