Merge pull request #309 from i59/patch-1
[mono.git] / mcs / class / corlib / System.IO / BinaryReader.cs
index 7d4546a2a53cb7573313f402be22183e67c754e2..a85dfe6ebba1883c0a5533684be7a414b5af7ffc 100644 (file)
@@ -4,10 +4,12 @@
 // Author:
 //   Matt Kimball (matt@kimball.net)
 //   Dick Porter (dick@ximian.com)
+//   Marek Safar (marek.safar@gmail.com)
 //
 
 //
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright 2011 Xamarin Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -33,21 +35,48 @@ using System;
 using System.Text;
 using System.Globalization;
 using Mono.Security;
+using System.Runtime.InteropServices;
 
 namespace System.IO {
+       [ComVisible (true)]
        public class BinaryReader : IDisposable {
                Stream m_stream;
                Encoding m_encoding;
-               int m_encoding_max_byte;
 
                byte[] m_buffer;
+
+               Decoder decoder;
+               char[] charBuffer;
+               byte[] charByteBuffer;
                
-               private bool m_disposed = false;
+               //
+               // 128 chars should cover most strings in one grab.
+               //
+               const int MaxBufferSize = 128;
 
-               public BinaryReader(Stream input) : this(input, Encoding.UTF8Unmarked) {
+               
+               private bool m_disposed;
+               
+               public BinaryReader(Stream input) 
+                       : this(input, Encoding.UTF8UnmarkedUnsafe)
+               {
                }
-
-               public BinaryReader(Stream input, Encoding encoding) {
+               
+#if NET_4_5
+               readonly bool leave_open;
+               
+               public BinaryReader(Stream input, Encoding encoding)
+                       : this (input, encoding, false)
+               {
+               }
+               
+               public BinaryReader(Stream input, Encoding encoding, bool leaveOpen)
+#else
+               const bool leave_open = false;
+               
+               public BinaryReader(Stream input, Encoding encoding)
+#endif
+               {
                        if (input == null || encoding == null) 
                                throw new ArgumentNullException(Locale.GetText ("Input or Encoding is a null reference."));
                        if (!input.CanRead)
@@ -55,8 +84,14 @@ namespace System.IO {
 
                        m_stream = input;
                        m_encoding = encoding;
-                       m_encoding_max_byte = m_encoding.GetMaxByteCount(1);
-                       m_buffer = new byte [32];
+#if NET_4_5
+                       leave_open = leaveOpen;
+#endif
+                       decoder = encoding.GetDecoder ();
+                       
+                       // internal buffer size is documented to be between 16 and the value
+                       // returned by GetMaxByteCount for the specified encoding
+                       m_buffer = new byte [Math.Max (16, encoding.GetMaxByteCount (1))];
                }
 
                public virtual Stream BaseStream {
@@ -72,34 +107,39 @@ namespace System.IO {
                
                protected virtual void Dispose (bool disposing)
                {
-                       if (disposing && m_stream != null)
+                       if (disposing && m_stream != null && !leave_open)
                                m_stream.Close ();
 
                        m_disposed = true;
                        m_buffer = null;
                        m_encoding = null;
                        m_stream = null;
+                       charBuffer = null;
                }
 
+#if NET_4_0 || NET_2_1
+               public void Dispose ()
+#else
                void IDisposable.Dispose() 
+#endif
                {
                        Dispose (true);
                }
 
-               protected virtual void FillBuffer (int bytes)
+               protected virtual void FillBuffer (int numBytes)
                {
+                       if (numBytes > m_buffer.Length)
+                               throw new ArgumentOutOfRangeException ("numBytes");
                        if (m_disposed)
                                throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader.");
                        if (m_stream==null)
                                throw new IOException("Stream is invalid");
                        
-                       CheckBuffer(bytes);
-
                        /* Cope with partial reads */
                        int pos=0;
 
-                       while(pos<bytes) {
-                               int n=m_stream.Read(m_buffer, pos, bytes-pos);
+                       while(pos<numBytes) {
+                               int n=m_stream.Read(m_buffer, pos, numBytes-pos);
                                if(n==0) {
                                        throw new EndOfStreamException();
                                }
@@ -123,10 +163,9 @@ namespace System.IO {
                        }
 
                        char[] result = new char[1];
-                       byte[] bytes;
                        int bcount;
 
-                       int ccount = ReadCharBytes (result, 0, 1, out bytes, out bcount);
+                       int ccount = ReadCharBytes (result, 0, 1, out bcount);
 
                        // Reposition the stream
                        m_stream.Position -= bcount;
@@ -142,15 +181,16 @@ namespace System.IO {
                }
 
                public virtual int Read() {
-                       char[] decode = new char[1];
+                       if (charBuffer == null)
+                               charBuffer = new char [MaxBufferSize];
 
-                       int count=Read(decode, 0, 1);
-                       if(count==0) {
+                       int count = Read (charBuffer, 0, 1);
+                       if(count == 0) {
                                /* No chars available */
-                               return(-1);
+                               return -1;
                        }
                        
-                       return decode[0];
+                       return charBuffer [0];
                }
 
                public virtual int Read(byte[] buffer, int index, int count) {
@@ -204,54 +244,57 @@ namespace System.IO {
                        }
 
                        int bytes_read;
-                       byte[] bytes;
-                       return ReadCharBytes (buffer, index, count, out bytes, out bytes_read);
+                       return ReadCharBytes (buffer, index, count, out bytes_read);
                }
 
-               private int ReadCharBytes(char[] buffer, int index, int count, out byte[] bytes, out int bytes_read) \r
+               private int ReadCharBytes (char[] buffer, int index, int count, out int bytes_read) 
                {
-                       int chars_read=0;
-                       bytes_read=0;
+                       int chars_read = 0;
+                       bytes_read = 0;
                        
-                       while(chars_read < count) 
-                       {
-                               CheckBuffer(bytes_read + 1);
+                       while (chars_read < count) {
+                               int pos = 0;
+                               while (true) {
+                                       CheckBuffer (pos + 1);
 
-                               int read_byte = m_stream.ReadByte();
+                                       int read_byte = m_stream.ReadByte ();
 
-                               if(read_byte==-1) 
-                               {
-                                       /* EOF */
-                                       bytes = m_buffer;
-                                       return(chars_read);
-                               }
+                                       if (read_byte == -1)
+                                               /* EOF */
+                                               return chars_read;
 
-                               m_buffer[bytes_read]=(byte)read_byte;
-                               bytes_read++;
+                                       m_buffer [pos ++] = (byte)read_byte;
+                                       bytes_read ++;
 
-                               chars_read=m_encoding.GetChars(m_buffer, 0,
-                                                                                bytes_read,
-                                                                                buffer, index);
-                               
+                                       int n = m_encoding.GetChars (m_buffer, 0, pos, buffer, index + chars_read);
+                                       if (n > 0)
+                                               break;
+                               }
+                               chars_read ++;
                        }
 
-                       bytes = m_buffer;
-                       return(chars_read);
+                       return chars_read;
                }
 
                protected int Read7BitEncodedInt() {
                        int ret = 0;
                        int shift = 0;
+                       int len;
                        byte b;
 
-                       do {
+                       for (len = 0; len < 5; ++len) {
                                b = ReadByte();
                                
                                ret = ret | ((b & 0x7f) << shift);
                                shift += 7;
-                       } while ((b & 0x80) == 0x80);
+                               if ((b & 0x80) == 0)
+                                       break;
+                       }
 
-                       return ret;
+                       if (len < 5)
+                               return ret;
+                       else
+                               throw new FormatException ("Too many bytes in what should have been a 7 bit encoded Int32.");
                }
 
                public virtual bool ReadBoolean() {
@@ -295,7 +338,7 @@ namespace System.IO {
                        byte[] buf = new byte[count];
                        int pos=0;
                        
-                       while(pos < count) \r
+                       while(pos < count) 
                        {
                                int n=m_stream.Read(buf, pos, count-pos);
                                if(n==0) {
@@ -308,7 +351,7 @@ namespace System.IO {
                                
                        if (pos!=count) {
                                byte[] new_buffer=new byte[pos];
-                               Array.Copy(buf, new_buffer, pos);
+                               Buffer.BlockCopyInternal (buf, 0, new_buffer, 0, pos);
                                return(new_buffer);
                        }
                        
@@ -330,6 +373,9 @@ namespace System.IO {
                                throw new ArgumentOutOfRangeException("count is less than 0");
                        }
 
+                       if (count == 0)
+                               return new char [0];
+                                       
                        char[] full = new char[count];
                        int chars = Read(full, 0, count);
                        
@@ -441,12 +487,45 @@ namespace System.IO {
                         * not chars
                         */
                        int len = Read7BitEncodedInt();
+                       
+                       if (len < 0)
+                               throw new IOException ("Invalid binary file (string len < 0)");
 
-                       FillBuffer(len);
+                       if (len == 0)
+                               return String.Empty;
                        
-                       char[] str = m_encoding.GetChars(m_buffer, 0, len);
+                       if (charByteBuffer == null) {
+                               charBuffer = new char [m_encoding.GetMaxByteCount (MaxBufferSize)];
+                               charByteBuffer = new byte [MaxBufferSize];
+                       }
+
+                       //
+                       // We read the string here in small chunks. Also, we
+                       // Attempt to optimize the common case of short strings.
+                       //
+                       StringBuilder sb = null;
+                       do {
+                               int readLen = Math.Min (MaxBufferSize, len);
+                               
+                               int n = m_stream.Read (charByteBuffer, 0, readLen);
+                               if (n == 0)
+                                       throw new EndOfStreamException();
+                               
+                               int cch = decoder.GetChars (charByteBuffer, 0, n, charBuffer, 0);
+
+                               if (sb == null && readLen == len) // ok, we got out the easy way, dont bother with the sb
+                                       return new String (charBuffer, 0, cch);
+
+                               if (sb == null)
+                                       // Len is a fairly good estimate of the number of chars in a string
+                                       // Most of the time 1 byte == 1 char
+                                       sb = new StringBuilder (len);
+                               
+                               sb.Append (charBuffer, 0, cch);
+                               len -= readLen;
+                       } while (len > 0);
 
-                       return(new String(str));
+                       return sb.ToString();
                }
 
                public virtual float ReadSingle() {
@@ -497,8 +576,7 @@ namespace System.IO {
                {
                        if(m_buffer.Length <= length) {
                                byte[] new_buffer=new byte[length];
-                               Array.Copy(m_buffer, new_buffer,
-                                          m_buffer.Length);
+                               Buffer.BlockCopyInternal (m_buffer, 0, new_buffer, 0, m_buffer.Length);
                                m_buffer=new_buffer;
                        }
                }