Merge pull request #1135 from kitsilanosoftware/rpm-spec-dead-entries
[mono.git] / mcs / class / corlib / System.IO / StreamReader.cs
index 538c538a482485e3570c011294ecb1b85f9ad0bb..8a0a9e9bb10110da7b4903c2bed329c02679494c 100644 (file)
@@ -8,7 +8,7 @@
 //
 // (C) Ximian, Inc.  http://www.ximian.com
 // Copyright (C) 2004 Novell (http://www.novell.com)
-// Copyright 2011 Xamarin Inc.
+// Copyright 2011, 2013 Xamarin Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -44,6 +44,11 @@ namespace System.IO {
        {
                sealed class NullStreamReader : StreamReader
                {
+                       internal NullStreamReader ()
+                       {
+                               base_stream = Stream.Null;
+                       }
+
                        public override int Peek ()
                        {
                                return -1;
@@ -122,7 +127,7 @@ namespace System.IO {
                bool mayBlock;
 
 #if NET_4_5
-               Task async_task;
+               IDecoupledTask async_task;
                readonly bool leave_open;
 #endif
 
@@ -131,10 +136,10 @@ namespace System.IO {
                private StreamReader() {}
 
                public StreamReader(Stream stream)
-                       : this (stream, Encoding.UTF8Unmarked, true, DefaultBufferSize) { }
+                       : this (stream, Encoding.UTF8, true, DefaultBufferSize) { }
 
                public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
-                       : this (stream, Encoding.UTF8Unmarked, detectEncodingFromByteOrderMarks, DefaultBufferSize) { }
+                       : this (stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) { }
 
                public StreamReader(Stream stream, Encoding encoding)
                        : this (stream, encoding, true, DefaultBufferSize) { }
@@ -162,10 +167,10 @@ namespace System.IO {
                }
 
                public StreamReader(string path)
-                       : this (path, Encoding.UTF8Unmarked, true, DefaultFileBufferSize) { }
+                       : this (path, Encoding.UTF8, true, DefaultFileBufferSize) { }
 
                public StreamReader(string path, bool detectEncodingFromByteOrderMarks)
-                       : this (path, Encoding.UTF8Unmarked, detectEncodingFromByteOrderMarks, DefaultFileBufferSize) { }
+                       : this (path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultFileBufferSize) { }
 
                public StreamReader(string path, Encoding encoding)
                        : this (path, encoding, true, DefaultFileBufferSize) { }
@@ -258,8 +263,6 @@ namespace System.IO {
 
                public virtual Encoding CurrentEncoding {
                        get {
-                               if (encoding == null)
-                                       throw new Exception ();
                                return encoding;
                        }
                }
@@ -381,43 +384,48 @@ namespace System.IO {
                }
                
                // the buffer is empty, fill it again
-               private int ReadBuffer ()
+               // Keep in sync with ReadBufferAsync
+               int ReadBuffer ()
                {
                        pos = 0;
-                       int cbEncoded = 0;
 
                        // keep looping until the decoder gives us some chars
                        decoded_count = 0;
-                       int parse_start = 0;
-                       do      
-                       {
-                               cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
-                               
+                       do {
+                               var cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
                                if (cbEncoded <= 0)
                                        return 0;
 
-                               mayBlock = (cbEncoded < buffer_size);
-                               if (do_checks > 0){
-                                       Encoding old = encoding;
-                                       parse_start = DoChecks (cbEncoded);
-                                       if (old != encoding){
-                                               int old_decoded_size = old.GetMaxCharCount (buffer_size) + 1;
-                                               int new_decoded_size = encoding.GetMaxCharCount (buffer_size) + 1;
-                                               if (old_decoded_size != new_decoded_size)
-                                                       decoded_buffer = new char [new_decoded_size];
-                                               decoder = encoding.GetDecoder ();
-                                       }
-                                       do_checks = 0;
-                                       cbEncoded -= parse_start;
-                               }
-                               
-                               decoded_count += decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
-                               parse_start = 0;
+                               decoded_count = ReadBufferCore (cbEncoded);
                        } while (decoded_count == 0);
 
                        return decoded_count;
                }
 
+               int ReadBufferCore (int cbEncoded)
+               {
+                       int parse_start;
+
+                       mayBlock = cbEncoded < buffer_size;
+                       if (do_checks > 0){
+                               Encoding old = encoding;
+                               parse_start = DoChecks (cbEncoded);
+                               if (old != encoding){
+                                       int old_decoded_size = old.GetMaxCharCount (buffer_size) + 1;
+                                       int new_decoded_size = encoding.GetMaxCharCount (buffer_size) + 1;
+                                       if (old_decoded_size != new_decoded_size)
+                                               decoded_buffer = new char [new_decoded_size];
+                                       decoder = encoding.GetDecoder ();
+                               }
+                               do_checks = 0;
+                               cbEncoded -= parse_start;
+                       } else {
+                               parse_start = 0;
+                       }
+                               
+                       return decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
+               }
+
                //
                // Peek can block:
                // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=96484
@@ -451,6 +459,7 @@ namespace System.IO {
                        return decoded_buffer [pos++];
                }
 
+               // Keep in sync with ReadAsync
                public override int Read ([In, Out] char[] buffer, int index, int count)
                {
                        if (buffer == null)
@@ -466,8 +475,7 @@ namespace System.IO {
                        CheckState ();
 
                        int chars_read = 0;
-                       while (count > 0)
-                       {
+                       while (count > 0) {
                                if (pos >= decoded_count && ReadBuffer () == 0)
                                        return chars_read > 0 ? chars_read : 0;
 
@@ -512,6 +520,7 @@ namespace System.IO {
                        return -1;
                }
 
+               // Keep in sync with ReadLineAsync
                public override string ReadLine()
                {
                        CheckState ();
@@ -523,7 +532,7 @@ namespace System.IO {
                        int end = FindNextEOL ();
                        if (end < decoded_count && end >= begin)
                                return new string (decoded_buffer, begin, end - begin);
-                       else if (end == -2)
+                       if (end == -2)
                                return line_builder.ToString (0, line_builder.Length);
 
                        if (line_builder == null)
@@ -555,23 +564,23 @@ namespace System.IO {
                                                return sb.ToString (0, sb.Length);
                                        }
                                        return line_builder.ToString (0, line_builder.Length);
-                               } else if (end == -2)
+                               }
+
+                               if (end == -2)
                                        return line_builder.ToString (0, line_builder.Length);
                        }
                }
 
+               // Keep in sync with ReadToEndAsync
                public override string ReadToEnd()
                {
                        CheckState ();
 
                        StringBuilder text = new StringBuilder ();
 
-                       int size = decoded_buffer.Length;
-                       char [] buffer = new char [size];
-                       int len;
-                       
-                       while ((len = Read (buffer, 0, size)) > 0)
-                               text.Append (buffer, 0, len);
+                       do {
+                               text.Append (decoded_buffer, pos, decoded_count - pos);
+                       } while (ReadBuffer () != 0);
 
                        return text.ToString ();
                }
@@ -582,7 +591,7 @@ namespace System.IO {
                                throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
 
 #if NET_4_5
-                       if (async_task != null && async_task.IsCompleted)
+                       if (async_task != null && !async_task.IsCompleted)
                                throw new InvalidOperationException ();
 #endif
                }
@@ -607,40 +616,156 @@ namespace System.IO {
 
                public override Task<int> ReadAsync (char[] buffer, int index, int count)
                {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+                       if (index < 0)
+                               throw new ArgumentOutOfRangeException ("index", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (index > buffer.Length - count)
+                               throw new ArgumentException ("index + count > buffer.Length");
+
                        CheckState ();
 
-                       Task<int> res;
-                       async_task = res = base.ReadAsync (buffer, index, count);
-                       return res;
+                       DecoupledTask<int> res;
+                       async_task = res = new DecoupledTask<int> (ReadAsyncCore (buffer, index, count));
+                       return res.Task;
+               }
+
+               async Task<int> ReadAsyncCore (char[] buffer, int index, int count)
+               {
+                       int chars_read = 0;
+
+                       while (count > 0) {
+                               if (pos >= decoded_count && await ReadBufferAsync ().ConfigureAwait (false) == 0)
+                                       return chars_read > 0 ? chars_read : 0;
+
+                               int cch = Math.Min (decoded_count - pos, count);
+                               Array.Copy (decoded_buffer, pos, buffer, index, cch);
+                               pos += cch;
+                               index += cch;
+                               count -= cch;
+                               chars_read += cch;
+                               if (mayBlock)
+                                       break;
+                       }
+
+                       return chars_read;
                }
 
                public override Task<int> ReadBlockAsync (char[] buffer, int index, int count)
                {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+                       if (index < 0)
+                               throw new ArgumentOutOfRangeException ("index", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
+                       // re-ordered to avoid possible integer overflow
+                       if (index > buffer.Length - count)
+                               throw new ArgumentException ("index + count > buffer.Length");
+
                        CheckState ();
 
-                       Task<int> res;
-                       async_task = res = base.ReadBlockAsync (buffer, index, count);
-                       return res;
+                       DecoupledTask<int> res;
+                       async_task = res = new DecoupledTask<int> (ReadAsyncCore (buffer, index, count));
+                       return res.Task;
                }
 
                public override Task<string> ReadLineAsync ()
                {
                        CheckState ();
 
-                       Task<string> res;
-                       async_task = res = base.ReadLineAsync ();
-                       return res;
+                       DecoupledTask<string> res;
+                       async_task = res = new DecoupledTask<string> (ReadLineAsyncCore ());
+                       return res.Task;
+               }
+
+               async Task<string> ReadLineAsyncCore ()
+               {
+                       if (pos >= decoded_count && await ReadBufferAsync ().ConfigureAwait (false) == 0)
+                               return null;
+
+                       int begin = pos;
+                       int end = FindNextEOL ();
+                       if (end < decoded_count && end >= begin)
+                               return new string (decoded_buffer, begin, end - begin);
+                       if (end == -2)
+                               return line_builder.ToString (0, line_builder.Length);
+
+                       if (line_builder == null)
+                               line_builder = new StringBuilder ();
+                       else
+                               line_builder.Length = 0;
+
+                       while (true) {
+                               if (foundCR) // don't include the trailing CR if present
+                                       decoded_count--;
+
+                               line_builder.Append (decoded_buffer, begin, decoded_count - begin);
+                               if (await ReadBufferAsync ().ConfigureAwait (false) == 0) {
+                                       if (line_builder.Capacity > 32768) {
+                                               StringBuilder sb = line_builder;
+                                               line_builder = null;
+                                               return sb.ToString (0, sb.Length);
+                                       }
+                                       return line_builder.ToString (0, line_builder.Length);
+                               }
+
+                               begin = pos;
+                               end = FindNextEOL ();
+                               if (end < decoded_count && end >= begin) {
+                                       line_builder.Append (decoded_buffer, begin, end - begin);
+                                       if (line_builder.Capacity > 32768) {
+                                               StringBuilder sb = line_builder;
+                                               line_builder = null;
+                                               return sb.ToString (0, sb.Length);
+                                       }
+                                       return line_builder.ToString (0, line_builder.Length);
+                               }
+
+                               if (end == -2)
+                                       return line_builder.ToString (0, line_builder.Length);
+                       }
                }
 
                public override Task<string> ReadToEndAsync ()
                {
                        CheckState ();
 
-                       Task<string> res;
-                       async_task = res = base.ReadToEndAsync ();
-                       return res;
+                       DecoupledTask<string> res;
+                       async_task = res = new DecoupledTask<string> (ReadToEndAsyncCore ());
+                       return res.Task;
+               }
+
+               async Task<string> ReadToEndAsyncCore ()
+               {
+                       StringBuilder text = new StringBuilder ();
+
+                       do {
+                               text.Append (decoded_buffer, pos, decoded_count - pos);
+                       } while (await ReadBufferAsync () != 0);
+
+                       return text.ToString ();
                }
 
+               async Task<int> ReadBufferAsync ()
+               {
+                       pos = 0;
+
+                       // keep looping until the decoder gives us some chars
+                       decoded_count = 0;
+                       do {
+                               var cbEncoded = await base_stream.ReadAsync (input_buffer, 0, buffer_size).ConfigureAwait (false);
+                               if (cbEncoded <= 0)
+                                       return 0;
+
+                               decoded_count = ReadBufferCore (cbEncoded);
+                       } while (decoded_count == 0);
+
+                       return decoded_count;
+               }
 #endif
        }
 }