Merge pull request #1542 from ninjarobot/UriTemplateMatchException
[mono.git] / mcs / class / corlib / System.IO / StreamWriter.cs
1 //\r
2 // System.IO.StreamWriter.cs\r
3 //\r
4 // Authors:\r
5 //   Dietmar Maurer (dietmar@ximian.com)\r
6 //   Paolo Molaro (lupus@ximian.com)\r
7 //   Marek Safar (marek.safar@gmail.com)\r
8 //\r
9 // (C) Ximian, Inc.  http://www.ximian.com\r
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
11 // Copyright 2011, 2013 Xamarin Inc.\r
12 //\r
13 // Permission is hereby granted, free of charge, to any person obtaining\r
14 // a copy of this software and associated documentation files (the\r
15 // "Software"), to deal in the Software without restriction, including\r
16 // without limitation the rights to use, copy, modify, merge, publish,\r
17 // distribute, sublicense, and/or sell copies of the Software, and to\r
18 // permit persons to whom the Software is furnished to do so, subject to\r
19 // the following conditions:\r
20 // \r
21 // The above copyright notice and this permission notice shall be\r
22 // included in all copies or substantial portions of the Software.\r
23 // \r
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
31 //\r
32 \r
33 using System.Text;\r
34 using System.Runtime.InteropServices;\r
35 using System.Threading.Tasks;\r
36 \r
37 namespace System.IO {\r
38         \r
39         [Serializable]\r
40         [ComVisible (true)]\r
41         public class StreamWriter : TextWriter {\r
42 \r
43                 private Encoding internalEncoding;\r
44 \r
45                 private Stream internalStream;\r
46 \r
47                 private const int DefaultBufferSize = 1024;\r
48                 private const int DefaultFileBufferSize = 4096;\r
49                 private const int MinimumBufferSize = 256;\r
50 \r
51                 private byte[] byte_buf;\r
52                 private char[] decode_buf;\r
53                 private int byte_pos;\r
54                 private int decode_pos;\r
55 \r
56                 private bool iflush;\r
57                 private bool preamble_done;\r
58 \r
59                 readonly bool leave_open;\r
60                 IDecoupledTask async_task;\r
61 \r
62                 public new static readonly StreamWriter Null = new StreamWriter (Stream.Null, EncodingHelper.UTF8Unmarked, 1);\r
63 \r
64                 public StreamWriter (Stream stream)\r
65                         : this (stream, EncodingHelper.UTF8Unmarked, DefaultBufferSize) {}\r
66 \r
67                 public StreamWriter (Stream stream, Encoding encoding)\r
68                         : this (stream, encoding, DefaultBufferSize) {}\r
69 \r
70                 internal void Initialize(Encoding encoding, int bufferSize) {\r
71                         internalEncoding = encoding;\r
72                         decode_pos = byte_pos = 0;\r
73                         int BufferSize = Math.Max(bufferSize, MinimumBufferSize);\r
74                         decode_buf = new char [BufferSize];\r
75                         byte_buf = new byte [encoding.GetMaxByteCount (BufferSize)];\r
76 \r
77                         // Fixes bug http://bugzilla.ximian.com/show_bug.cgi?id=74513\r
78                         if (internalStream.CanSeek && internalStream.Position > 0)\r
79                                 preamble_done = true;\r
80                 }\r
81 \r
82                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize)\r
83                         : this (stream, encoding, bufferSize, false)\r
84                 {\r
85                 }\r
86                 \r
87                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)\r
88                 {\r
89                         if (null == stream)\r
90                                 throw new ArgumentNullException("stream");\r
91                         if (null == encoding)\r
92                                 throw new ArgumentNullException("encoding");\r
93                         if (bufferSize <= 0)\r
94                                 throw new ArgumentOutOfRangeException("bufferSize");\r
95                         if (!stream.CanWrite)\r
96                                 throw new ArgumentException ("Can not write to stream");\r
97 \r
98                         leave_open = leaveOpen;\r
99                         internalStream = stream;\r
100 \r
101                         Initialize(encoding, bufferSize);\r
102                 }\r
103 \r
104                 public StreamWriter (string path)\r
105                         : this (path, false, EncodingHelper.UTF8Unmarked, DefaultFileBufferSize) {}\r
106 \r
107                 public StreamWriter (string path, bool append)\r
108                         : this (path, append, EncodingHelper.UTF8Unmarked, DefaultFileBufferSize) {}\r
109 \r
110                 public StreamWriter (string path, bool append, Encoding encoding)\r
111                         : this (path, append, encoding, DefaultFileBufferSize) {}\r
112 \r
113                 public StreamWriter (string path, bool append, Encoding encoding, int bufferSize)\r
114                 {\r
115                         if (null == encoding)\r
116                                 throw new ArgumentNullException("encoding");\r
117                         if (bufferSize <= 0)\r
118                                 throw new ArgumentOutOfRangeException("bufferSize");\r
119 \r
120                         FileMode mode;\r
121 \r
122                         if (append)\r
123                                 mode = FileMode.Append;\r
124                         else\r
125                                 mode = FileMode.Create;\r
126                         \r
127                         internalStream = new FileStream (path, mode, FileAccess.Write, FileShare.Read);\r
128 \r
129                         if (append)\r
130                                 internalStream.Position = internalStream.Length;\r
131                         else\r
132                                 internalStream.SetLength (0);\r
133 \r
134                         Initialize(encoding, bufferSize);\r
135                 }\r
136 \r
137                 public virtual bool AutoFlush {\r
138                         get {\r
139                                 return iflush;\r
140                         }\r
141                         set {\r
142                                 iflush = value;\r
143                                 if (iflush)\r
144                                         Flush ();\r
145                         }\r
146                 }\r
147 \r
148                 public virtual Stream BaseStream {\r
149                         get {\r
150                                 return internalStream;\r
151                         }\r
152                 }\r
153 \r
154                 public override Encoding Encoding {\r
155                         get {\r
156                                 return internalEncoding;\r
157                         }\r
158                 }\r
159 \r
160                 protected override void Dispose (bool disposing) \r
161                 {\r
162                         if (byte_buf == null || !disposing)\r
163                                 return;\r
164 \r
165                         try {\r
166                                 Flush ();\r
167                         } finally {\r
168                                 byte_buf = null;\r
169                                 internalEncoding = null;\r
170                                 decode_buf = null;\r
171 \r
172                                 if (!leave_open) {\r
173                                         internalStream.Close ();\r
174                                 }\r
175 \r
176                                 internalStream = null;\r
177                         }\r
178                 }\r
179 \r
180                 public override void Flush ()\r
181                 {\r
182                         CheckState ();\r
183                         FlushCore ();\r
184                 }\r
185 \r
186                 // Keep in sync with FlushCoreAsync\r
187                 void FlushCore ()\r
188                 {\r
189                         Decode ();\r
190                         if (byte_pos > 0) {\r
191                                 FlushBytes ();\r
192                                 internalStream.Flush ();\r
193                         }\r
194                 }\r
195 \r
196                 // how the speedup works:\r
197                 // the Write () methods simply copy the characters in a buffer of chars (decode_buf)\r
198                 // Decode () is called when the buffer is full or we need to flash.\r
199                 // Decode () will use the encoding to get the bytes and but them inside\r
200                 // byte_buf. From byte_buf the data is finally outputted to the stream.\r
201                 void FlushBytes () \r
202                 {\r
203                         // write the encoding preamble only at the start of the stream\r
204                         if (!preamble_done && byte_pos > 0) {\r
205                                 byte[] preamble = internalEncoding.GetPreamble ();\r
206                                 if (preamble.Length > 0)\r
207                                         internalStream.Write (preamble, 0, preamble.Length);\r
208                                 preamble_done = true;\r
209                         }\r
210                         internalStream.Write (byte_buf, 0, byte_pos);\r
211                         byte_pos = 0;\r
212                 }\r
213 \r
214                 void Decode () \r
215                 {\r
216                         if (byte_pos > 0)\r
217                                 FlushBytes ();\r
218                         if (decode_pos > 0) {\r
219                                 int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);\r
220                                 byte_pos += len;\r
221                                 decode_pos = 0;\r
222                         }\r
223                 }\r
224 \r
225                 void LowLevelWrite (char[] buffer, int index, int count)\r
226                 {\r
227                         while (count > 0) {\r
228                                 int todo = decode_buf.Length - decode_pos;\r
229                                 if (todo == 0) {\r
230                                         Decode ();\r
231                                         todo = decode_buf.Length;\r
232                                 }\r
233                                 if (todo > count)\r
234                                         todo = count;\r
235                                 Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);\r
236                                 count -= todo;\r
237                                 index += todo;\r
238                                 decode_pos += todo;\r
239                         }\r
240                 }\r
241                 \r
242                 void LowLevelWrite (string s)\r
243                 {\r
244                         int count = s.Length;\r
245                         int index = 0;\r
246                         while (count > 0) {\r
247                                 int todo = decode_buf.Length - decode_pos;\r
248                                 if (todo == 0) {\r
249                                         Decode ();\r
250                                         todo = decode_buf.Length;\r
251                                 }\r
252                                 if (todo > count)\r
253                                         todo = count;\r
254                                 \r
255                                 for (int i = 0; i < todo; i ++)\r
256                                         decode_buf [i + decode_pos] = s [i + index];\r
257                                 \r
258                                 count -= todo;\r
259                                 index += todo;\r
260                                 decode_pos += todo;\r
261                         }\r
262                 }               \r
263 \r
264                 async Task FlushCoreAsync ()\r
265                 {\r
266                         await DecodeAsync ().ConfigureAwait (false);\r
267                         if (byte_pos > 0) {\r
268                                 await FlushBytesAsync ().ConfigureAwait (false);\r
269                                 await internalStream.FlushAsync ().ConfigureAwait (false);\r
270                         }\r
271                 }\r
272 \r
273                 async Task FlushBytesAsync ()\r
274                 {\r
275                         // write the encoding preamble only at the start of the stream\r
276                         if (!preamble_done && byte_pos > 0) {\r
277                                 byte[] preamble = internalEncoding.GetPreamble ();\r
278                                 if (preamble.Length > 0)\r
279                                         await internalStream.WriteAsync (preamble, 0, preamble.Length).ConfigureAwait (false);\r
280                                 preamble_done = true;\r
281                         }\r
282 \r
283                         await internalStream.WriteAsync (byte_buf, 0, byte_pos).ConfigureAwait (false);\r
284                         byte_pos = 0;\r
285                 }\r
286 \r
287                 async Task DecodeAsync () \r
288                 {\r
289                         if (byte_pos > 0)\r
290                                 await FlushBytesAsync ().ConfigureAwait (false);\r
291                         if (decode_pos > 0) {\r
292                                 int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);\r
293                                 byte_pos += len;\r
294                                 decode_pos = 0;\r
295                         }\r
296                 }               \r
297 \r
298                 async Task LowLevelWriteAsync (char[] buffer, int index, int count)\r
299                 {\r
300                         while (count > 0) {\r
301                                 int todo = decode_buf.Length - decode_pos;\r
302                                 if (todo == 0) {\r
303                                         await DecodeAsync ().ConfigureAwait (false);\r
304                                         todo = decode_buf.Length;\r
305                                 }\r
306                                 if (todo > count)\r
307                                         todo = count;\r
308                                 Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);\r
309                                 count -= todo;\r
310                                 index += todo;\r
311                                 decode_pos += todo;\r
312                         }\r
313                 }\r
314                 \r
315                 async Task LowLevelWriteAsync (string s)\r
316                 {\r
317                         int count = s.Length;\r
318                         int index = 0;\r
319                         while (count > 0) {\r
320                                 int todo = decode_buf.Length - decode_pos;\r
321                                 if (todo == 0) {\r
322                                         await DecodeAsync ().ConfigureAwait (false);\r
323                                         todo = decode_buf.Length;\r
324                                 }\r
325                                 if (todo > count)\r
326                                         todo = count;\r
327                                 \r
328                                 for (int i = 0; i < todo; i ++)\r
329                                         decode_buf [i + decode_pos] = s [i + index];\r
330                                 \r
331                                 count -= todo;\r
332                                 index += todo;\r
333                                 decode_pos += todo;\r
334                         }\r
335                 }       \r
336 \r
337                 public override void Write (char[] buffer, int index, int count) \r
338                 {\r
339                         if (buffer == null)\r
340                                 throw new ArgumentNullException ("buffer");\r
341                         if (index < 0)\r
342                                 throw new ArgumentOutOfRangeException ("index", "< 0");\r
343                         if (count < 0)\r
344                                 throw new ArgumentOutOfRangeException ("count", "< 0");\r
345                         // re-ordered to avoid possible integer overflow\r
346                         if (index > buffer.Length - count)\r
347                                 throw new ArgumentException ("index + count > buffer.Length");\r
348 \r
349                         CheckState ();\r
350 \r
351                         LowLevelWrite (buffer, index, count);\r
352                         if (iflush)\r
353                                 FlushCore ();\r
354                 }\r
355                 \r
356                 public override void Write (char value)\r
357                 {\r
358                         CheckState ();\r
359 \r
360                         // the size of decode_buf is always > 0 and\r
361                         // we check for overflow right away\r
362                         if (decode_pos >= decode_buf.Length)\r
363                                 Decode ();\r
364                         decode_buf [decode_pos++] = value;\r
365                         if (iflush)\r
366                                 FlushCore ();\r
367                 }\r
368 \r
369                 public override void Write (char[] buffer)\r
370                 {\r
371                         CheckState ();\r
372 \r
373                         if (buffer != null)\r
374                                 LowLevelWrite (buffer, 0, buffer.Length);\r
375                         if (iflush)\r
376                                 FlushCore ();\r
377                 }\r
378 \r
379                 public override void Write (string value) \r
380                 {\r
381                         CheckState ();\r
382 \r
383                         if (value == null)\r
384                                 return;\r
385                         \r
386                         LowLevelWrite (value);\r
387                         \r
388                         if (iflush)\r
389                                 FlushCore ();\r
390                 }\r
391 \r
392                 public override void Close()\r
393                 {\r
394                         Dispose (true);\r
395                 }\r
396 \r
397                 void CheckState ()\r
398                 {\r
399                         if (byte_buf == null)\r
400                                 throw new ObjectDisposedException ("StreamWriter");\r
401 \r
402                         if (async_task != null && !async_task.IsCompleted)\r
403                                 throw new InvalidOperationException ();\r
404                 }\r
405 \r
406                 public override Task FlushAsync ()\r
407                 {\r
408                         CheckState ();\r
409                         DecoupledTask res;\r
410                         async_task = res = new DecoupledTask (FlushCoreAsync ());\r
411                         return res.Task;\r
412                 }\r
413 \r
414                 public override Task WriteAsync (char value)\r
415                 {\r
416                         CheckState ();\r
417 \r
418                         DecoupledTask res;\r
419                         async_task = res = new DecoupledTask (WriteAsyncCore (value));\r
420                         return res.Task;\r
421                 }\r
422 \r
423                 async Task WriteAsyncCore (char value)\r
424                 {\r
425                         // the size of decode_buf is always > 0 and\r
426                         // we check for overflow right away\r
427                         if (decode_pos >= decode_buf.Length)\r
428                                 await DecodeAsync ().ConfigureAwait (false);\r
429                         decode_buf [decode_pos++] = value;\r
430 \r
431                         if (iflush)\r
432                                 await FlushCoreAsync ().ConfigureAwait (false);\r
433                 }\r
434 \r
435                 public override Task WriteAsync (char[] buffer, int index, int count)\r
436                 {\r
437                         CheckState ();\r
438                         if (buffer == null)\r
439                                 return TaskConstants.Finished;\r
440 \r
441                         DecoupledTask res;\r
442                         async_task = res = new DecoupledTask (WriteAsyncCore (buffer, index, count));\r
443                         return res.Task;\r
444                 }\r
445 \r
446                 async Task WriteAsyncCore (char[] buffer, int index, int count)\r
447                 {\r
448                         // Debug.Assert (buffer == null);\r
449 \r
450                         await LowLevelWriteAsync (buffer, index, count).ConfigureAwait (false);\r
451 \r
452                         if (iflush)\r
453                                 await FlushCoreAsync ().ConfigureAwait (false);\r
454                 }\r
455 \r
456                 public override Task WriteAsync (string value)\r
457                 {\r
458                         CheckState ();\r
459 \r
460                         if (value == null)\r
461                                 return TaskConstants.Finished;\r
462 \r
463                         DecoupledTask res;                      \r
464                         async_task = res = new DecoupledTask (WriteAsyncCore (value, false));\r
465                         return res.Task;\r
466                 }\r
467 \r
468                 async Task WriteAsyncCore (string value, bool appendNewLine)\r
469                 {\r
470                         // Debug.Assert (value == null);\r
471 \r
472                         await LowLevelWriteAsync (value).ConfigureAwait (false);\r
473                         if (appendNewLine)\r
474                                 await LowLevelWriteAsync (CoreNewLine, 0, CoreNewLine.Length).ConfigureAwait (false);\r
475                         \r
476                         if (iflush)\r
477                                 await FlushCoreAsync ().ConfigureAwait (false);\r
478                 }               \r
479 \r
480                 public override Task WriteLineAsync ()\r
481                 {\r
482                         CheckState ();\r
483 \r
484                         DecoupledTask res;\r
485                         async_task = res = new DecoupledTask (WriteAsyncCore (CoreNewLine, 0, CoreNewLine.Length));\r
486                         return res.Task;\r
487                 }\r
488 \r
489                 public override Task WriteLineAsync (char value)\r
490                 {\r
491                         CheckState ();\r
492                         DecoupledTask res;\r
493                         async_task = res = new DecoupledTask (WriteLineAsyncCore (value));\r
494                         return res.Task;\r
495                 }\r
496 \r
497                 async Task WriteLineAsyncCore (char value)\r
498                 {\r
499                         await WriteAsyncCore (value).ConfigureAwait (false);\r
500                         await LowLevelWriteAsync (CoreNewLine, 0, CoreNewLine.Length).ConfigureAwait (false);\r
501                         \r
502                         if (iflush)\r
503                                 await FlushCoreAsync ().ConfigureAwait (false);\r
504                 }               \r
505 \r
506                 public override Task WriteLineAsync (char[] buffer, int index, int count)\r
507                 {\r
508                         if (buffer == null)\r
509                                 throw new ArgumentNullException ("buffer");\r
510                         if (index < 0)\r
511                                 throw new ArgumentOutOfRangeException ("index", "< 0");\r
512                         if (count < 0)\r
513                                 throw new ArgumentOutOfRangeException ("count", "< 0");\r
514                         // re-ordered to avoid possible integer overflow\r
515                         if (index > buffer.Length - count)\r
516                                 throw new ArgumentException ("index + count > buffer.Length");\r
517 \r
518                         CheckState ();\r
519                         DecoupledTask res;\r
520                         async_task = res = new DecoupledTask (WriteLineAsyncCore (buffer, index, count));\r
521                         return res.Task;\r
522                 }\r
523 \r
524                 async Task WriteLineAsyncCore (char[] buffer, int index, int count)\r
525                 {\r
526                         // Debug.Assert (buffer == null);\r
527 \r
528                         await LowLevelWriteAsync (buffer, index, count).ConfigureAwait (false);\r
529                         await LowLevelWriteAsync (CoreNewLine, 0, CoreNewLine.Length).ConfigureAwait (false);\r
530                         \r
531                         if (iflush)\r
532                                 await FlushCoreAsync ().ConfigureAwait (false);\r
533                 }               \r
534 \r
535                 public override Task WriteLineAsync (string value)\r
536                 {\r
537                         if (value == null)\r
538                                 return WriteLineAsync ();\r
539 \r
540                         CheckState ();\r
541                         DecoupledTask res;                      \r
542                         async_task = res = new DecoupledTask (WriteAsyncCore (value, true));\r
543                         return res.Task;\r
544                 }\r
545         }\r
546 }\r