Merge pull request #820 from brendanzagaeski/master
[mono.git] / mcs / class / corlib / System.IO / BinaryReader.cs
index bbcb1ed8fbaa194f7e4e16ba8c65adc90424666e..d7b92b38541e42a0d7888c6f06acf15fb94c85f1 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,23 +35,19 @@ using System;
 using System.Text;
 using System.Globalization;
 using Mono.Security;
-#if NET_2_0
 using System.Runtime.InteropServices;
-#endif
 
 namespace System.IO {
-#if NET_2_0
        [ComVisible (true)]
-#endif
        public class BinaryReader : IDisposable {
                Stream m_stream;
                Encoding m_encoding;
-               int m_encoding_max_byte;
 
                byte[] m_buffer;
 
                Decoder decoder;
-               char [] charBuffer;
+               char[] charBuffer;
+               byte[] charByteBuffer;
                
                //
                // 128 chars should cover most strings in one grab.
@@ -57,12 +55,28 @@ namespace System.IO {
                const int MaxBufferSize = 128;
 
                
-               private bool m_disposed = false;
-
-               public BinaryReader(Stream input) : this(input, Encoding.UTF8UnmarkedUnsafe) {
+               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)
@@ -70,8 +84,14 @@ namespace System.IO {
 
                        m_stream = input;
                        m_encoding = encoding;
+#if NET_4_5
+                       leave_open = leaveOpen;
+#endif
                        decoder = encoding.GetDecoder ();
-                       m_buffer = new byte [32];
+                       
+                       // 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 {
@@ -87,7 +107,7 @@ 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;
@@ -97,25 +117,29 @@ namespace System.IO {
                        charBuffer = null;
                }
 
+#if NET_4_0
+               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();
                                }
@@ -139,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;
@@ -221,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) 
+               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() {
@@ -342,26 +368,36 @@ namespace System.IO {
                        return((char)ch);
                }
 
-               public virtual char[] ReadChars(int count) {
+               public virtual char[] ReadChars (int count)
+               {
                        if (count < 0) {
                                throw new ArgumentOutOfRangeException("count is less than 0");
                        }
 
+                       if (m_stream == null) {
+                               if (m_disposed)
+                                       throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader.");
+
+                               throw new IOException("Stream is invalid");
+                       }
+
                        if (count == 0)
-                               return new char [0];
+                               return EmptyArray<char>.Value;
                                        
                        char[] full = new char[count];
-                       int chars = Read(full, 0, count);
+                       int bytes_read;
+                       int chars = ReadCharBytes (full, 0, count, out bytes_read);                     
                        
-                       if (chars == 0) {
+                       if (chars == 0)
                                throw new EndOfStreamException();
-                       } else if (chars != full.Length) {
-                               char[] ret = new char[chars];
-                               Array.Copy(full, 0, ret, 0, chars);
-                               return ret;
-                       } else {
-                               return full;
+
+                       if (chars != count) {
+                               var new_buffer = new char[chars];
+                               Buffer.BlockCopyInternal (full, 0, new_buffer, 0, 2 * chars);
+                               return new_buffer;
                        }
+
+                       return full;
                }
 
                unsafe public virtual decimal ReadDecimal() {
@@ -468,9 +504,10 @@ namespace System.IO {
                        if (len == 0)
                                return String.Empty;
                        
-                       
-                       if (charBuffer == null)
-                               charBuffer = new char [MaxBufferSize];
+                       if (charByteBuffer == null) {
+                               charBuffer = new char [m_encoding.GetMaxByteCount (MaxBufferSize)];
+                               charByteBuffer = new byte [MaxBufferSize];
+                       }
 
                        //
                        // We read the string here in small chunks. Also, we
@@ -478,13 +515,13 @@ namespace System.IO {
                        //
                        StringBuilder sb = null;
                        do {
-                               int readLen = (len > MaxBufferSize)
-                                               ? MaxBufferSize
-                                               : len;
+                               int readLen = Math.Min (MaxBufferSize, len);
                                
-                               FillBuffer (readLen);
+                               readLen = m_stream.Read (charByteBuffer, 0, readLen);
+                               if (readLen == 0)
+                                       throw new EndOfStreamException();
                                
-                               int cch = decoder.GetChars (m_buffer, 0, readLen, charBuffer, 0);
+                               int cch = decoder.GetChars (charByteBuffer, 0, readLen, 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);