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

  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: BinaryReader
  18. **
  19. **
  20. ** Purpose: Wraps a stream and provides convenient read functionality
  21. ** for strings and primitive types.
  22. **
  23. **
  24. ============================================================*/
  25. namespace System.IO
  26. {
  27.    
  28.     using System;
  29.     using System.Text;
  30.     using System.Globalization;
  31.    
  32.     [System.Runtime.InteropServices.ComVisible(true)]
  33.     public class BinaryReader : IDisposable
  34.     {
  35.         private const int MaxCharBytesSize = 128;
  36.        
  37.         private Stream m_stream;
  38.         private byte[] m_buffer;
  39.         private Decoder m_decoder;
  40.         private byte[] m_charBytes;
  41.         private char[] m_singleChar;
  42.         private char[] m_charBuffer;
  43.         private int m_maxCharsSize;
  44.         // From MaxCharBytesSize & Encoding
  45.         // Performance optimization for Read() w/ Unicode. Speeds us up by ~40%
  46.         private bool m_2BytesPerChar;
  47.         private bool m_isMemoryStream;
  48.         // "do we sit on MemoryStream?" for Read/ReadInt32 perf
  49.         public BinaryReader(Stream input) : this(input, new UTF8Encoding())
  50.         {
  51.         }
  52.        
  53.         public BinaryReader(Stream input, Encoding encoding)
  54.         {
  55.             if (input == null) {
  56.                 throw new ArgumentNullException("input");
  57.             }
  58.             if (encoding == null) {
  59.                 throw new ArgumentNullException("encoding");
  60.             }
  61.             if (!input.CanRead)
  62.                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
  63.             m_stream = input;
  64.             m_decoder = encoding.GetDecoder();
  65.             m_maxCharsSize = encoding.GetMaxCharCount(MaxCharBytesSize);
  66.             int minBufferSize = encoding.GetMaxByteCount(1);
  67.             // max bytes per one char
  68.             if (minBufferSize < 16)
  69.                 minBufferSize = 16;
  70.             m_buffer = new byte[minBufferSize];
  71.             m_charBuffer = null;
  72.             m_charBytes = null;
  73.            
  74.             // For Encodings that always use 2 bytes per char (or more),
  75.             // special case them here to make Read() & Peek() faster.
  76.             m_2BytesPerChar = encoding is UnicodeEncoding;
  77.             // check if BinaryReader is based on MemoryStream, and keep this for it's life
  78.             // we cannot use "as" operator, since derived classes are not allowed
  79.             m_isMemoryStream = (m_stream.GetType() == typeof(MemoryStream));
  80.            
  81.             BCLDebug.Assert(m_decoder != null, "[BinaryReader.ctor]m_decoder!=null");
  82.         }
  83.        
  84.         public virtual Stream BaseStream {
  85.             get { return m_stream; }
  86.         }
  87.        
  88.         public virtual void Close()
  89.         {
  90.             Dispose(true);
  91.         }
  92.        
  93.         protected virtual void Dispose(bool disposing)
  94.         {
  95.             if (disposing) {
  96.                 Stream copyOfStream = m_stream;
  97.                 m_stream = null;
  98.                 if (copyOfStream != null)
  99.                     copyOfStream.Close();
  100.             }
  101.             m_stream = null;
  102.             m_buffer = null;
  103.             m_decoder = null;
  104.             m_charBytes = null;
  105.             m_singleChar = null;
  106.             m_charBuffer = null;
  107.         }
  108.        
  109.         /// <internalonly/>
  110.         void IDisposable.Dispose()
  111.         {
  112.             Dispose(true);
  113.         }
  114.        
  115.         public virtual int PeekChar()
  116.         {
  117.             if (m_stream == null)
  118.                 __Error.FileNotOpen();
  119.            
  120.             if (!m_stream.CanSeek)
  121.                 return -1;
  122.             long origPos = m_stream.Position;
  123.             int ch = Read();
  124.             m_stream.Position = origPos;
  125.             return ch;
  126.         }
  127.        
  128.         public virtual int Read()
  129.         {
  130.             if (m_stream == null) {
  131.                 __Error.FileNotOpen();
  132.             }
  133.             return InternalReadOneChar();
  134.         }
  135.        
  136.         public virtual bool ReadBoolean()
  137.         {
  138.             FillBuffer(1);
  139.             return (m_buffer[0] != 0);
  140.         }
  141.        
  142.         public virtual byte ReadByte()
  143.         {
  144.             // Inlined to avoid some method call overhead with FillBuffer.
  145.             if (m_stream == null)
  146.                 __Error.FileNotOpen();
  147.            
  148.             int b = m_stream.ReadByte();
  149.             if (b == -1)
  150.                 __Error.EndOfFile();
  151.             return (byte)b;
  152.         }
  153.        
  154.         [CLSCompliant(false)]
  155.         public virtual sbyte ReadSByte()
  156.         {
  157.             FillBuffer(1);
  158.             return (sbyte)(m_buffer[0]);
  159.         }
  160.        
  161.         public virtual char ReadChar()
  162.         {
  163.             int value = Read();
  164.             if (value == -1) {
  165.                 __Error.EndOfFile();
  166.             }
  167.             return (char)value;
  168.         }
  169.        
  170.         public virtual short ReadInt16()
  171.         {
  172.             FillBuffer(2);
  173.             return (short)(m_buffer[0] | m_buffer[1] << 8);
  174.         }
  175.        
  176.         [CLSCompliant(false)]
  177.         public virtual ushort ReadUInt16()
  178.         {
  179.             FillBuffer(2);
  180.             return (ushort)(m_buffer[0] | m_buffer[1] << 8);
  181.         }
  182.        
  183.         public virtual int ReadInt32()
  184.         {
  185.             if (m_isMemoryStream) {
  186.                 // read directly from MemoryStream buffer
  187.                 MemoryStream mStream = m_stream as MemoryStream;
  188.                 BCLDebug.Assert(mStream != null, "m_stream as MemoryStream != null");
  189.                
  190.                 return mStream.InternalReadInt32();
  191.             }
  192.             else {
  193.                 FillBuffer(4);
  194.                 return (int)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
  195.             }
  196.         }
  197.        
  198.         [CLSCompliant(false)]
  199.         public virtual uint ReadUInt32()
  200.         {
  201.             FillBuffer(4);
  202.             return (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
  203.         }
  204.        
  205.         public virtual long ReadInt64()
  206.         {
  207.             FillBuffer(8);
  208.             uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
  209.             uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | m_buffer[6] << 16 | m_buffer[7] << 24);
  210.             return (long)((ulong)hi) << 32 | lo;
  211.         }
  212.        
  213.         [CLSCompliant(false)]
  214.         public virtual ulong ReadUInt64()
  215.         {
  216.             FillBuffer(8);
  217.             uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
  218.             uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | m_buffer[6] << 16 | m_buffer[7] << 24);
  219.             return ((ulong)hi) << 32 | lo;
  220.         }
  221.        
  222.         unsafe public virtual float ReadSingle()
  223.         {
  224.             FillBuffer(4);
  225.             uint tmpBuffer = (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
  226.             return *((float*)&tmpBuffer);
  227.         }
  228.        
  229.         unsafe public virtual double ReadDouble()
  230.         {
  231.             FillBuffer(8);
  232.             uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
  233.             uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | m_buffer[6] << 16 | m_buffer[7] << 24);
  234.            
  235.             ulong tmpBuffer = ((ulong)hi) << 32 | lo;
  236.             return *((double*)&tmpBuffer);
  237.         }
  238.        
  239.         public virtual decimal ReadDecimal()
  240.         {
  241.             FillBuffer(16);
  242.             return Decimal.ToDecimal(m_buffer);
  243.         }
  244.        
  245.         public virtual string ReadString()
  246.         {
  247.             int currPos = 0;
  248.             int n;
  249.             int stringLength;
  250.             int readLength;
  251.             int charsRead;
  252.            
  253.             if (m_stream == null)
  254.                 __Error.FileNotOpen();
  255.            
  256.             // Length of the string in bytes, not chars
  257.             stringLength = Read7BitEncodedInt();
  258.             if (stringLength < 0) {
  259.                 throw new IOException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.IO_InvalidStringLen_Len"), stringLength));
  260.             }
  261.            
  262.             if (stringLength == 0) {
  263.                 return String.Empty;
  264.             }
  265.            
  266.             if (m_charBytes == null) {
  267.                 m_charBytes = new byte[MaxCharBytesSize];
  268.             }
  269.            
  270.             if (m_charBuffer == null) {
  271.                 m_charBuffer = new char[m_maxCharsSize];
  272.             }
  273.            
  274.             StringBuilder sb = null;
  275.             do {
  276.                 readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos);
  277.                
  278.                 n = m_stream.Read(m_charBytes, 0, readLength);
  279.                 if (n == 0) {
  280.                     __Error.EndOfFile();
  281.                 }
  282.                
  283.                 charsRead = m_decoder.GetChars(m_charBytes, 0, n, m_charBuffer, 0);
  284.                
  285.                 if (currPos == 0 && n == stringLength)
  286.                     return new string(m_charBuffer, 0, charsRead);
  287.                
  288.                 if (sb == null)
  289.                     sb = new StringBuilder(stringLength);
  290.                 // Actual string length in chars may be smaller.
  291.                 sb.Append(m_charBuffer, 0, charsRead);
  292.                 currPos += n;
  293.                
  294.             }
  295.             while (currPos < stringLength);
  296.            
  297.             return sb.ToString();
  298.         }
  299.        
  300.         public virtual int Read(char[] buffer, int index, int count)
  301.         {
  302.             if (buffer == null) {
  303.                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
  304.             }
  305.             if (index < 0) {
  306.                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  307.             }
  308.             if (count < 0) {
  309.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  310.             }
  311.             if (buffer.Length - index < count) {
  312.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  313.             }
  314.            
  315.             if (m_stream == null)
  316.                 __Error.FileNotOpen();
  317.            
  318.             return InternalReadChars(buffer, index, count);
  319.         }
  320.        
  321.         private int InternalReadChars(char[] buffer, int index, int count)
  322.         {
  323.             int charsRead = 0;
  324.             int numBytes = 0;
  325.             int charsRemaining = count;
  326.            
  327.             if (m_charBytes == null) {
  328.                 m_charBytes = new byte[MaxCharBytesSize];
  329.             }
  330.            
  331.             while (charsRemaining > 0) {
  332.                 // We really want to know what the minimum number of bytes per char
  333.                 // is for our encoding. Otherwise for UnicodeEncoding we'd have to
  334.                 // do ~1+log(n) reads to read n characters.
  335.                 numBytes = charsRemaining;
  336.                 if (m_2BytesPerChar)
  337.                     numBytes <<= 1;
  338.                 if (numBytes > MaxCharBytesSize)
  339.                     numBytes = MaxCharBytesSize;
  340.                
  341.                 if (m_isMemoryStream) {
  342.                     MemoryStream mStream = m_stream as MemoryStream;
  343.                     BCLDebug.Assert(mStream != null, "m_stream as MemoryStream != null");
  344.                    
  345.                     int position = mStream.InternalGetPosition();
  346.                     numBytes = mStream.InternalEmulateRead(numBytes);
  347.                     if (numBytes == 0) {
  348.                         return (count - charsRemaining);
  349.                     }
  350.                     charsRead = m_decoder.GetChars(mStream.InternalGetBuffer(), position, numBytes, buffer, index);
  351.                 }
  352.                 else {
  353.                     numBytes = m_stream.Read(m_charBytes, 0, numBytes);
  354.                     if (numBytes == 0) {
  355.                         // Console.WriteLine("Found no bytes. We're outta here.");
  356.                         return (count - charsRemaining);
  357.                     }
  358.                     charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, buffer, index);
  359.                 }
  360.                
  361.                 charsRemaining -= charsRead;
  362.                 index += charsRead;
  363.                 // Console.WriteLine("That became: " + charsRead + " characters.");
  364.             }
  365.             BCLDebug.Assert(charsRemaining == 0, "We didn't read all the chars we thought we would.");
  366.             return count;
  367.         }
  368.        
  369.         private int InternalReadOneChar()
  370.         {
  371.             // I know having a separate InternalReadOneChar method seems a little
  372.             // redundant, but this makes a scenario like the security parser code
  373.             // 20% faster, in addition to the optimizations for UnicodeEncoding I
  374.             // put in InternalReadChars.
  375.             int charsRead = 0;
  376.             int numBytes = 0;
  377.             long posSav = posSav = 0;
  378.            
  379.             if (m_stream.CanSeek)
  380.                 posSav = m_stream.Position;
  381.            
  382.             if (m_charBytes == null) {
  383.                 m_charBytes = new byte[MaxCharBytesSize];
  384.             }
  385.             if (m_singleChar == null) {
  386.                 m_singleChar = new char[1];
  387.             }
  388.            
  389.             while (charsRead == 0) {
  390.                 // We really want to know what the minimum number of bytes per char
  391.                 // is for our encoding. Otherwise for UnicodeEncoding we'd have to
  392.                 // do ~1+log(n) reads to read n characters.
  393.                 // Assume 1 byte can be 1 char unless m_2BytesPerChar is true.
  394.                 numBytes = m_2BytesPerChar ? 2 : 1;
  395.                
  396.                 int r = m_stream.ReadByte();
  397.                 m_charBytes[0] = (byte)r;
  398.                 if (r == -1)
  399.                     numBytes = 0;
  400.                 if (numBytes == 2) {
  401.                     r = m_stream.ReadByte();
  402.                     m_charBytes[1] = (byte)r;
  403.                     if (r == -1)
  404.                         numBytes = 1;
  405.                 }
  406.                
  407.                 if (numBytes == 0) {
  408.                     // Console.WriteLine("Found no bytes. We're outta here.");
  409.                     return -1;
  410.                 }
  411.                
  412.                 BCLDebug.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only.");
  413.                
  414.                 try {
  415.                    
  416.                     charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, m_singleChar, 0);
  417.                 }
  418.                 catch {
  419.                     // Handle surrogate char
  420.                    
  421.                     if (m_stream.CanSeek)
  422.                         m_stream.Seek((posSav - m_stream.Position), SeekOrigin.Current);
  423.                     // else - we can't do much here
  424.                    
  425.                     throw;
  426.                 }
  427.                
  428.                 BCLDebug.Assert(charsRead < 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!");
  429.                 // Console.WriteLine("That became: " + charsRead + " characters.");
  430.             }
  431.             if (charsRead == 0)
  432.                 return -1;
  433.             return m_singleChar[0];
  434.         }
  435.        
  436.         public virtual char[] ReadChars(int count)
  437.         {
  438.             if (m_stream == null) {
  439.                 __Error.FileNotOpen();
  440.             }
  441.             if (count < 0) {
  442.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  443.             }
  444.             char[] chars = new char[count];
  445.             int n = InternalReadChars(chars, 0, count);
  446.             if (n != count) {
  447.                 char[] copy = new char[n];
  448.                 Buffer.InternalBlockCopy(chars, 0, copy, 0, 2 * n);
  449.                 // sizeof(char)
  450.                 chars = copy;
  451.             }
  452.            
  453.             return chars;
  454.         }
  455.        
  456.         public virtual int Read(byte[] buffer, int index, int count)
  457.         {
  458.             if (buffer == null)
  459.                 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
  460.             if (index < 0)
  461.                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  462.             if (count < 0)
  463.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  464.             if (buffer.Length - index < count)
  465.                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  466.            
  467.             if (m_stream == null)
  468.                 __Error.FileNotOpen();
  469.             return m_stream.Read(buffer, index, count);
  470.         }
  471.        
  472.         public virtual byte[] ReadBytes(int count)
  473.         {
  474.             if (count < 0)
  475.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  476.             if (m_stream == null)
  477.                 __Error.FileNotOpen();
  478.            
  479.             byte[] result = new byte[count];
  480.            
  481.             int numRead = 0;
  482.             do {
  483.                 int n = m_stream.Read(result, numRead, count);
  484.                 if (n == 0)
  485.                     break;
  486.                 numRead += n;
  487.                 count -= n;
  488.             }
  489.             while (count > 0);
  490.            
  491.             if (numRead != result.Length) {
  492.                 // Trim array. This should happen on EOF & possibly net streams.
  493.                 byte[] copy = new byte[numRead];
  494.                 Buffer.InternalBlockCopy(result, 0, copy, 0, numRead);
  495.                 result = copy;
  496.             }
  497.            
  498.             return result;
  499.         }
  500.        
  501.         protected virtual void FillBuffer(int numBytes)
  502.         {
  503.             BCLDebug.Assert(m_buffer == null || (numBytes > 0 && numBytes <= m_buffer.Length), "[FillBuffer]numBytes>0 && numBytes<=m_buffer.Length");
  504.             int bytesRead = 0;
  505.             int n = 0;
  506.            
  507.             if (m_stream == null)
  508.                 __Error.FileNotOpen();
  509.            
  510.             // Need to find a good threshold for calling ReadByte() repeatedly
  511.             // vs. calling Read(byte[], int, int) for both buffered & unbuffered
  512.             // streams.
  513.             if (numBytes == 1) {
  514.                 n = m_stream.ReadByte();
  515.                 if (n == -1)
  516.                     __Error.EndOfFile();
  517.                 m_buffer[0] = (byte)n;
  518.                 return;
  519.             }
  520.            
  521.             do {
  522.                 n = m_stream.Read(m_buffer, bytesRead, numBytes - bytesRead);
  523.                 if (n == 0) {
  524.                     __Error.EndOfFile();
  525.                 }
  526.                 bytesRead += n;
  527.             }
  528.             while (bytesRead < numBytes);
  529.         }
  530.        
  531.         protected internal int Read7BitEncodedInt()
  532.         {
  533.             // Read out an Int32 7 bits at a time. The high bit
  534.             // of the byte when on means to continue reading more bytes.
  535.             int count = 0;
  536.             int shift = 0;
  537.             byte b;
  538.             do {
  539.                 // Check for a corrupted stream. Read a max of 5 bytes.
  540.                 // In a future version, add a DataFormatException.
  541.                 if (shift == 5 * 7)
  542.                     // 5 bytes max per Int32, shift += 7
  543.                     throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32"));
  544.                
  545.                 // ReadByte handles end of stream cases for us.
  546.                 b = ReadByte();
  547.                 count |= (b & 127) << shift;
  548.                 shift += 7;
  549.             }
  550.             while ((b & 128) != 0);
  551.             return count;
  552.         }
  553.     }
  554. }

Developer Fusion