Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / corlib / System.IO / StreamReader.cs
1 //
2 // System.IO.StreamReader.cs
3 //
4 // Authors:
5 //   Dietmar Maurer (dietmar@ximian.com)
6 //   Miguel de Icaza (miguel@ximian.com) 
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 // Copyright (C) 2004 Novell (http://www.novell.com)
11 // Copyright 2011, 2013 Xamarin Inc.
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Text;
35 using System.Runtime.InteropServices;
36 #if NET_4_5
37 using System.Threading.Tasks;
38 #endif
39
40 namespace System.IO {
41         [Serializable]
42         [ComVisible (true)]
43         public class StreamReader : TextReader
44         {
45                 sealed class NullStreamReader : StreamReader
46                 {
47                         internal NullStreamReader ()
48                         {
49                                 base_stream = Stream.Null;
50                         }
51
52                         public override int Peek ()
53                         {
54                                 return -1;
55                         }
56
57                         public override int Read ()
58                         {
59                                 return -1;
60                         }
61
62                         public override int Read ([In, Out] char[] buffer, int index, int count)
63                         {
64                                 return 0;
65                         }
66
67                         public override string ReadLine ()
68                         {
69                                 return null;
70                         }
71
72                         public override string ReadToEnd ()
73                         {
74                                 return String.Empty;
75                         }
76
77                         public override Stream BaseStream {
78                                 get { return Stream.Null; }
79                         }
80
81                         public override Encoding CurrentEncoding {
82                                 get { return Encoding.Unicode; }
83                         }
84                 }
85
86                 const int DefaultBufferSize = 1024;
87                 const int DefaultFileBufferSize = 4096;
88                 const int MinimumBufferSize = 128;
89
90                 //
91                 // The input buffer
92                 //
93                 byte [] input_buffer;
94                 
95                 // Input buffer ready for recycling
96                 static byte [] input_buffer_recycle;
97                 static object input_buffer_recycle_lock = new object ();
98
99                 //
100                 // The decoded buffer from the above input buffer
101                 //
102                 char [] decoded_buffer;
103                 static char[] decoded_buffer_recycle;
104
105                 Encoding encoding;
106                 Decoder decoder;
107                 StringBuilder line_builder;
108                 Stream base_stream;
109
110                 //
111                 // Decoded bytes in decoded_buffer.
112                 //
113                 int decoded_count;
114
115                 //
116                 // Current position in the decoded_buffer
117                 //
118                 int pos;
119
120                 //
121                 // The buffer size that we are using
122                 //
123                 int buffer_size;
124
125                 int do_checks;
126                 
127                 bool mayBlock;
128
129 #if NET_4_5
130                 IDecoupledTask async_task;
131                 readonly bool leave_open;
132 #endif
133
134                 public new static readonly StreamReader Null =  new NullStreamReader ();
135                 
136                 private StreamReader() {}
137
138                 public StreamReader(Stream stream)
139                         : this (stream, Encoding.UTF8, true, DefaultBufferSize) { }
140
141                 public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
142                         : this (stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) { }
143
144                 public StreamReader(Stream stream, Encoding encoding)
145                         : this (stream, encoding, true, DefaultBufferSize) { }
146
147                 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks)
148                         : this (stream, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) { }
149
150 #if NET_4_5
151                 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
152                         : this (stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false)
153                 {
154                 }
155
156                 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen)
157 #else
158                 const bool leave_open = false;
159
160                 public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
161 #endif
162                 {
163 #if NET_4_5
164                         leave_open = leaveOpen;
165 #endif
166                         Initialize (stream, encoding, detectEncodingFromByteOrderMarks, bufferSize);
167                 }
168
169                 public StreamReader(string path)
170                         : this (path, Encoding.UTF8, true, DefaultFileBufferSize) { }
171
172                 public StreamReader(string path, bool detectEncodingFromByteOrderMarks)
173                         : this (path, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultFileBufferSize) { }
174
175                 public StreamReader(string path, Encoding encoding)
176                         : this (path, encoding, true, DefaultFileBufferSize) { }
177
178                 public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks)
179                         : this (path, encoding, detectEncodingFromByteOrderMarks, DefaultFileBufferSize) { }
180                 
181                 public StreamReader(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
182                 {
183                         if (null == path)
184                                 throw new ArgumentNullException("path");
185                         if (String.Empty == path)
186                                 throw new ArgumentException("Empty path not allowed");
187                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
188                                 throw new ArgumentException("path contains invalid characters");
189                         if (null == encoding)
190                                 throw new ArgumentNullException ("encoding");
191                         if (bufferSize <= 0)
192                                 throw new ArgumentOutOfRangeException ("bufferSize", "The minimum size of the buffer must be positive");
193
194                         Stream stream = (Stream) File.OpenRead (path);
195                         Initialize (stream, encoding, detectEncodingFromByteOrderMarks, bufferSize);
196                 }
197
198                 internal void Initialize (Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize)
199                 {
200                         if (null == stream)
201                                 throw new ArgumentNullException ("stream");
202                         if (null == encoding)
203                                 throw new ArgumentNullException ("encoding");
204                         if (!stream.CanRead)
205                                 throw new ArgumentException ("Cannot read stream");
206                         if (bufferSize <= 0)
207                                 throw new ArgumentOutOfRangeException ("bufferSize", "The minimum size of the buffer must be positive");
208
209                         if (bufferSize < MinimumBufferSize)
210                                 bufferSize = MinimumBufferSize;
211                         
212                         // since GetChars() might add flushed character, it 
213                         // should have additional char buffer for extra 1 
214                         // (probably 1 is ok, but might be insufficient. I'm not sure)
215                         var decoded_buffer_size = encoding.GetMaxCharCount (bufferSize) + 1;
216
217                         //
218                         // Instead of allocating a new default buffer use the
219                         // last one if there is any available
220                         //
221                         if (bufferSize <= DefaultBufferSize && input_buffer_recycle != null) {
222                                 lock (input_buffer_recycle_lock) {
223                                         if (input_buffer_recycle != null) {
224                                                 input_buffer = input_buffer_recycle;
225                                                 input_buffer_recycle = null;
226                                         }
227                                         
228                                         if (decoded_buffer_recycle != null && decoded_buffer_size <= decoded_buffer_recycle.Length) {
229                                                 decoded_buffer = decoded_buffer_recycle;
230                                                 decoded_buffer_recycle = null;
231                                         }
232                                 }
233                         }
234                         
235                         if (input_buffer == null)
236                                 input_buffer = new byte [bufferSize];
237                         else
238                                 Array.Clear (input_buffer, 0, bufferSize);
239                         
240                         if (decoded_buffer == null)
241                                 decoded_buffer = new char [decoded_buffer_size];
242                         else
243                                 Array.Clear (decoded_buffer, 0, decoded_buffer_size);
244
245                         base_stream = stream;           
246                         this.buffer_size = bufferSize;
247                         this.encoding = encoding;
248                         decoder = encoding.GetDecoder ();
249
250                         byte [] preamble = encoding.GetPreamble ();
251                         do_checks = detectEncodingFromByteOrderMarks ? 1 : 0;
252                         do_checks += (preamble.Length == 0) ? 0 : 2;
253                         
254                         decoded_count = 0;
255                         pos = 0;
256                 }
257
258                 public virtual Stream BaseStream {
259                         get {
260                                 return base_stream;
261                         }
262                 }
263
264                 public virtual Encoding CurrentEncoding {
265                         get {
266                                 return encoding;
267                         }
268                 }
269
270                 public bool EndOfStream {
271                         get { return Peek () < 0; }
272                 }
273
274                 public override void Close ()
275                 {
276                         Dispose (true);
277                 }
278
279                 protected override void Dispose (bool disposing)
280                 {
281                         if (disposing && base_stream != null && !leave_open)
282                                 base_stream.Close ();
283                         
284                         if (input_buffer != null && input_buffer.Length == DefaultBufferSize && input_buffer_recycle == null) {
285                                 lock (input_buffer_recycle_lock) {
286                                         if (input_buffer_recycle == null) {
287                                                 input_buffer_recycle = input_buffer;
288                                         }
289                                         
290                                         if (decoded_buffer_recycle == null) {
291                                                 decoded_buffer_recycle = decoded_buffer;
292                                         }
293                                 }
294                         }
295                         
296                         input_buffer = null;
297                         decoded_buffer = null;
298                         encoding = null;
299                         decoder = null;
300                         base_stream = null;
301                         base.Dispose (disposing);
302                 }
303
304                 //
305                 // Provides auto-detection of the encoding, as well as skipping over
306                 // byte marks at the beginning of a stream.
307                 //
308                 int DoChecks (int count)
309                 {
310                         if ((do_checks & 2) == 2){
311                                 byte [] preamble = encoding.GetPreamble ();
312                                 int c = preamble.Length;
313                                 if (count >= c){
314                                         int i;
315                                         
316                                         for (i = 0; i < c; i++)
317                                                 if (input_buffer [i] != preamble [i])
318                                                         break;
319
320                                         if (i == c)
321                                                 return i;
322                                 }
323                         }
324
325                         if ((do_checks & 1) == 1){
326                                 if (count < 2)
327                                         return 0;
328
329                                 if (input_buffer [0] == 0xfe && input_buffer [1] == 0xff){
330                                         this.encoding = Encoding.BigEndianUnicode;
331                                         return 2;
332                                 }
333                                 if (input_buffer [0] == 0xff && input_buffer [1] == 0xfe && count < 4) {
334                                         // If we don't have enough bytes we can't check for UTF32, so use Unicode
335                                         this.encoding = Encoding.Unicode;
336                                         return 2;
337                                 }
338
339                                 if (count < 3)
340                                         return 0;
341
342                                 if (input_buffer [0] == 0xef && input_buffer [1] == 0xbb && input_buffer [2] == 0xbf){
343                                         this.encoding = Encoding.UTF8Unmarked;
344                                         return 3;
345                                 }
346
347                                 if (count < 4) {
348                                         if (input_buffer [0] == 0xff && input_buffer [1] == 0xfe && input_buffer [2] != 0) {
349                                                 this.encoding = Encoding.Unicode;
350                                                 return 2;
351                                         }
352                                         return 0;
353                                 }
354
355                                 if (input_buffer [0] == 0 && input_buffer [1] == 0
356                                         && input_buffer [2] == 0xfe && input_buffer [3] == 0xff)
357                                 {
358                                         this.encoding = Encoding.BigEndianUTF32;
359                                         return 4;
360                                 }
361
362                                 if (input_buffer [0] == 0xff && input_buffer [1] == 0xfe) {
363                                         if (input_buffer [2] == 0 && input_buffer[3] == 0) {
364                                                 this.encoding = Encoding.UTF32;
365                                                 return 4;
366                                         }
367
368                                         this.encoding = Encoding.Unicode;
369                                         return 2;
370                                 }
371                         }
372
373                         return 0;
374                 }
375
376                 public void DiscardBufferedData ()
377                 {
378                         CheckState ();
379
380                         pos = decoded_count = 0;
381                         mayBlock = false;
382                         // Discard internal state of the decoder too.
383                         decoder = encoding.GetDecoder ();
384                 }
385                 
386                 // the buffer is empty, fill it again
387                 // Keep in sync with ReadBufferAsync
388                 int ReadBuffer ()
389                 {
390                         pos = 0;
391
392                         // keep looping until the decoder gives us some chars
393                         decoded_count = 0;
394                         do {
395                                 var cbEncoded = base_stream.Read (input_buffer, 0, buffer_size);
396                                 if (cbEncoded <= 0)
397                                         return 0;
398
399                                 decoded_count = ReadBufferCore (cbEncoded);
400                         } while (decoded_count == 0);
401
402                         return decoded_count;
403                 }
404
405                 int ReadBufferCore (int cbEncoded)
406                 {
407                         int parse_start;
408
409                         mayBlock = cbEncoded < buffer_size;
410                         if (do_checks > 0){
411                                 Encoding old = encoding;
412                                 parse_start = DoChecks (cbEncoded);
413                                 if (old != encoding){
414                                         int old_decoded_size = old.GetMaxCharCount (buffer_size) + 1;
415                                         int new_decoded_size = encoding.GetMaxCharCount (buffer_size) + 1;
416                                         if (old_decoded_size != new_decoded_size)
417                                                 decoded_buffer = new char [new_decoded_size];
418                                         decoder = encoding.GetDecoder ();
419                                 }
420                                 do_checks = 0;
421                                 cbEncoded -= parse_start;
422                         } else {
423                                 parse_start = 0;
424                         }
425                                 
426                         return decoder.GetChars (input_buffer, parse_start, cbEncoded, decoded_buffer, 0);
427                 }
428
429                 //
430                 // Peek can block:
431                 // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=96484
432                 //
433                 public override int Peek ()
434                 {
435                         CheckState ();
436
437                         if (pos >= decoded_count && ReadBuffer () == 0)
438                                 return -1;
439
440                         return decoded_buffer [pos];
441                 }
442
443                 //
444                 // Used internally by our console, as it previously depended on Peek() being a
445                 // routine that would not block.
446                 //
447                 internal bool DataAvailable ()
448                 {
449                         return pos < decoded_count;
450                 }
451                 
452                 public override int Read ()
453                 {
454                         CheckState ();
455
456                         if (pos >= decoded_count && ReadBuffer () == 0)
457                                 return -1;
458
459                         return decoded_buffer [pos++];
460                 }
461
462                 // Keep in sync with ReadAsync
463                 public override int Read ([In, Out] char[] buffer, int index, int count)
464                 {
465                         if (buffer == null)
466                                 throw new ArgumentNullException ("buffer");
467                         if (index < 0)
468                                 throw new ArgumentOutOfRangeException ("index", "< 0");
469                         if (count < 0)
470                                 throw new ArgumentOutOfRangeException ("count", "< 0");
471                         // re-ordered to avoid possible integer overflow
472                         if (index > buffer.Length - count)
473                                 throw new ArgumentException ("index + count > buffer.Length");
474
475                         CheckState ();
476
477                         int chars_read = 0;
478                         while (count > 0) {
479                                 if (pos >= decoded_count && ReadBuffer () == 0)
480                                         return chars_read > 0 ? chars_read : 0;
481
482                                 int cch = Math.Min (decoded_count - pos, count);
483                                 Array.Copy (decoded_buffer, pos, buffer, index, cch);
484                                 pos += cch;
485                                 index += cch;
486                                 count -= cch;
487                                 chars_read += cch;
488                                 if (mayBlock)
489                                         break;
490                         }
491                         return chars_read;
492                 }
493
494                 bool foundCR;
495                 int FindNextEOL ()
496                 {
497                         char c = '\0';
498                         for (; pos < decoded_count; pos++) {
499                                 c = decoded_buffer [pos];
500                                 if (c == '\n') {
501                                         pos++;
502                                         int res = (foundCR) ? (pos - 2) : (pos - 1);
503                                         if (res < 0)
504                                                 res = 0; // if a new buffer starts with a \n and there was a \r at
505                                                         // the end of the previous one, we get here.
506                                         foundCR = false;
507                                         return res;
508                                 } else if (foundCR) {
509                                         foundCR = false;
510                                         if (pos == 0)
511                                                 return -2; // Need to flush the current buffered line.
512                                                            // This is a \r at the end of the previous decoded buffer that
513                                                            // is not followed by a \n in the current decoded buffer.
514                                         return pos - 1;
515                                 }
516
517                                 foundCR = (c == '\r');
518                         }
519
520                         return -1;
521                 }
522
523                 // Keep in sync with ReadLineAsync
524                 public override string ReadLine()
525                 {
526                         CheckState ();
527
528                         if (pos >= decoded_count && ReadBuffer () == 0)
529                                 return null;
530
531                         int begin = pos;
532                         int end = FindNextEOL ();
533                         if (end < decoded_count && end >= begin)
534                                 return new string (decoded_buffer, begin, end - begin);
535                         if (end == -2)
536                                 return line_builder.ToString (0, line_builder.Length);
537
538                         if (line_builder == null)
539                                 line_builder = new StringBuilder ();
540                         else
541                                 line_builder.Length = 0;
542
543                         while (true) {
544                                 if (foundCR) // don't include the trailing CR if present
545                                         decoded_count--;
546
547                                 line_builder.Append (decoded_buffer, begin, decoded_count - begin);
548                                 if (ReadBuffer () == 0) {
549                                         if (line_builder.Capacity > 32768) {
550                                                 StringBuilder sb = line_builder;
551                                                 line_builder = null;
552                                                 return sb.ToString (0, sb.Length);
553                                         }
554                                         return line_builder.ToString (0, line_builder.Length);
555                                 }
556
557                                 begin = pos;
558                                 end = FindNextEOL ();
559                                 if (end < decoded_count && end >= begin) {
560                                         line_builder.Append (decoded_buffer, begin, end - begin);
561                                         if (line_builder.Capacity > 32768) {
562                                                 StringBuilder sb = line_builder;
563                                                 line_builder = null;
564                                                 return sb.ToString (0, sb.Length);
565                                         }
566                                         return line_builder.ToString (0, line_builder.Length);
567                                 }
568
569                                 if (end == -2)
570                                         return line_builder.ToString (0, line_builder.Length);
571                         }
572                 }
573
574                 // Keep in sync with ReadToEndAsync
575                 public override string ReadToEnd()
576                 {
577                         CheckState ();
578
579                         StringBuilder text = new StringBuilder ();
580
581                         do {
582                                 text.Append (decoded_buffer, pos, decoded_count - pos);
583                         } while (ReadBuffer () != 0);
584
585                         return text.ToString ();
586                 }
587
588                 void CheckState ()
589                 {
590                         if (base_stream == null)
591                                 throw new ObjectDisposedException ("StreamReader", "Cannot read from a closed StreamReader");
592
593 #if NET_4_5
594                         if (async_task != null && !async_task.IsCompleted)
595                                 throw new InvalidOperationException ();
596 #endif
597                 }
598
599 #if NET_4_5
600                 public override int ReadBlock ([In, Out] char[] buffer, int index, int count)
601                 {
602                         if (buffer == null)
603                                 throw new ArgumentNullException ("buffer");
604                         if (index < 0)
605                                 throw new ArgumentOutOfRangeException ("index", "< 0");
606                         if (count < 0)
607                                 throw new ArgumentOutOfRangeException ("count", "< 0");
608                         // re-ordered to avoid possible integer overflow
609                         if (index > buffer.Length - count)
610                                 throw new ArgumentException ("index + count > buffer.Length");
611
612                         CheckState ();
613
614                         return base.ReadBlock (buffer, index, count);
615                 }
616
617                 public override Task<int> ReadAsync (char[] buffer, int index, int count)
618                 {
619                         if (buffer == null)
620                                 throw new ArgumentNullException ("buffer");
621                         if (index < 0)
622                                 throw new ArgumentOutOfRangeException ("index", "< 0");
623                         if (count < 0)
624                                 throw new ArgumentOutOfRangeException ("count", "< 0");
625                         // re-ordered to avoid possible integer overflow
626                         if (index > buffer.Length - count)
627                                 throw new ArgumentException ("index + count > buffer.Length");
628
629                         CheckState ();
630
631                         DecoupledTask<int> res;
632                         async_task = res = new DecoupledTask<int> (ReadAsyncCore (buffer, index, count));
633                         return res.Task;
634                 }
635
636                 async Task<int> ReadAsyncCore (char[] buffer, int index, int count)
637                 {
638                         int chars_read = 0;
639
640                         while (count > 0) {
641                                 if (pos >= decoded_count && await ReadBufferAsync ().ConfigureAwait (false) == 0)
642                                         return chars_read > 0 ? chars_read : 0;
643
644                                 int cch = Math.Min (decoded_count - pos, count);
645                                 Array.Copy (decoded_buffer, pos, buffer, index, cch);
646                                 pos += cch;
647                                 index += cch;
648                                 count -= cch;
649                                 chars_read += cch;
650                                 if (mayBlock)
651                                         break;
652                         }
653
654                         return chars_read;
655                 }
656
657                 public override Task<int> ReadBlockAsync (char[] buffer, int index, int count)
658                 {
659                         if (buffer == null)
660                                 throw new ArgumentNullException ("buffer");
661                         if (index < 0)
662                                 throw new ArgumentOutOfRangeException ("index", "< 0");
663                         if (count < 0)
664                                 throw new ArgumentOutOfRangeException ("count", "< 0");
665                         // re-ordered to avoid possible integer overflow
666                         if (index > buffer.Length - count)
667                                 throw new ArgumentException ("index + count > buffer.Length");
668
669                         CheckState ();
670
671                         DecoupledTask<int> res;
672                         async_task = res = new DecoupledTask<int> (ReadAsyncCore (buffer, index, count));
673                         return res.Task;
674                 }
675
676                 public override Task<string> ReadLineAsync ()
677                 {
678                         CheckState ();
679
680                         DecoupledTask<string> res;
681                         async_task = res = new DecoupledTask<string> (ReadLineAsyncCore ());
682                         return res.Task;
683                 }
684
685                 async Task<string> ReadLineAsyncCore ()
686                 {
687                         if (pos >= decoded_count && await ReadBufferAsync ().ConfigureAwait (false) == 0)
688                                 return null;
689
690                         int begin = pos;
691                         int end = FindNextEOL ();
692                         if (end < decoded_count && end >= begin)
693                                 return new string (decoded_buffer, begin, end - begin);
694                         if (end == -2)
695                                 return line_builder.ToString (0, line_builder.Length);
696
697                         if (line_builder == null)
698                                 line_builder = new StringBuilder ();
699                         else
700                                 line_builder.Length = 0;
701
702                         while (true) {
703                                 if (foundCR) // don't include the trailing CR if present
704                                         decoded_count--;
705
706                                 line_builder.Append (decoded_buffer, begin, decoded_count - begin);
707                                 if (await ReadBufferAsync ().ConfigureAwait (false) == 0) {
708                                         if (line_builder.Capacity > 32768) {
709                                                 StringBuilder sb = line_builder;
710                                                 line_builder = null;
711                                                 return sb.ToString (0, sb.Length);
712                                         }
713                                         return line_builder.ToString (0, line_builder.Length);
714                                 }
715
716                                 begin = pos;
717                                 end = FindNextEOL ();
718                                 if (end < decoded_count && end >= begin) {
719                                         line_builder.Append (decoded_buffer, begin, end - begin);
720                                         if (line_builder.Capacity > 32768) {
721                                                 StringBuilder sb = line_builder;
722                                                 line_builder = null;
723                                                 return sb.ToString (0, sb.Length);
724                                         }
725                                         return line_builder.ToString (0, line_builder.Length);
726                                 }
727
728                                 if (end == -2)
729                                         return line_builder.ToString (0, line_builder.Length);
730                         }
731                 }
732
733                 public override Task<string> ReadToEndAsync ()
734                 {
735                         CheckState ();
736
737                         DecoupledTask<string> res;
738                         async_task = res = new DecoupledTask<string> (ReadToEndAsyncCore ());
739                         return res.Task;
740                 }
741
742                 async Task<string> ReadToEndAsyncCore ()
743                 {
744                         StringBuilder text = new StringBuilder ();
745
746                         do {
747                                 text.Append (decoded_buffer, pos, decoded_count - pos);
748                         } while (await ReadBufferAsync () != 0);
749
750                         return text.ToString ();
751                 }
752
753                 async Task<int> ReadBufferAsync ()
754                 {
755                         pos = 0;
756
757                         // keep looping until the decoder gives us some chars
758                         decoded_count = 0;
759                         do {
760                                 var cbEncoded = await base_stream.ReadAsync (input_buffer, 0, buffer_size).ConfigureAwait (false);
761                                 if (cbEncoded <= 0)
762                                         return 0;
763
764                                 decoded_count = ReadBufferCore (cbEncoded);
765                         } while (decoded_count == 0);
766
767                         return decoded_count;
768                 }
769 #endif
770         }
771 }