182cbde3b942041538a8a80fcc8eb02a57fb830b
[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 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 #if NET_4_5
36 using System.Threading.Tasks;
37 #endif\r
38 \r
39 namespace System.IO {\r
40         \r
41         [Serializable]\r
42         [ComVisible (true)]\r
43         public class StreamWriter : TextWriter {\r
44 \r
45                 private Encoding internalEncoding;\r
46 \r
47                 private Stream internalStream;\r
48 \r
49                 private const int DefaultBufferSize = 1024;\r
50                 private const int DefaultFileBufferSize = 4096;\r
51                 private const int MinimumBufferSize = 256;\r
52 \r
53                 private byte[] byte_buf;\r
54                 private char[] decode_buf;\r
55                 private int byte_pos;\r
56                 private int decode_pos;\r
57 \r
58                 private bool iflush;\r
59                 private bool DisposedAlready;\r
60                 private bool preamble_done;\r
61 \r
62 #if NET_4_5\r
63                 readonly bool leave_open;\r
64                 Task async_task;\r
65 #endif\r
66 \r
67                 public new static readonly StreamWriter Null = new StreamWriter (Stream.Null, Encoding.UTF8Unmarked, 1);\r
68 \r
69                 public StreamWriter (Stream stream)\r
70                         : this (stream, Encoding.UTF8Unmarked, DefaultBufferSize) {}\r
71 \r
72                 public StreamWriter (Stream stream, Encoding encoding)\r
73                         : this (stream, encoding, DefaultBufferSize) {}\r
74 \r
75                 internal void Initialize(Encoding encoding, int bufferSize) {\r
76                         internalEncoding = encoding;\r
77                         decode_pos = byte_pos = 0;\r
78                         int BufferSize = Math.Max(bufferSize, MinimumBufferSize);\r
79                         decode_buf = new char [BufferSize];\r
80                         byte_buf = new byte [encoding.GetMaxByteCount (BufferSize)];\r
81 \r
82                         // Fixes bug http://bugzilla.ximian.com/show_bug.cgi?id=74513\r
83                         if (internalStream.CanSeek && internalStream.Position > 0)\r
84                                 preamble_done = true;\r
85                 }\r
86 \r
87 #if NET_4_5\r
88                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize)\r
89                         : this (stream, encoding, bufferSize, false)\r
90                 {\r
91                 }\r
92                 \r
93                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize, bool leaveOpen)\r
94 #else\r
95                 const bool leave_open = false;\r
96 \r
97                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize)\r
98 #endif\r
99                 {\r
100                         if (null == stream)\r
101                                 throw new ArgumentNullException("stream");\r
102                         if (null == encoding)\r
103                                 throw new ArgumentNullException("encoding");\r
104                         if (bufferSize <= 0)\r
105                                 throw new ArgumentOutOfRangeException("bufferSize");\r
106                         if (!stream.CanWrite)\r
107                                 throw new ArgumentException ("Can not write to stream");\r
108 \r
109 #if NET_4_5\r
110                         leave_open = leaveOpen;\r
111 #endif\r
112                         internalStream = stream;\r
113 \r
114                         Initialize(encoding, bufferSize);\r
115                 }\r
116 \r
117                 public StreamWriter (string path)\r
118                         : this (path, false, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
119 \r
120                 public StreamWriter (string path, bool append)\r
121                         : this (path, append, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
122 \r
123                 public StreamWriter (string path, bool append, Encoding encoding)\r
124                         : this (path, append, encoding, DefaultFileBufferSize) {}\r
125 \r
126                 public StreamWriter (string path, bool append, Encoding encoding, int bufferSize)\r
127                 {\r
128                         if (null == encoding)\r
129                                 throw new ArgumentNullException("encoding");\r
130                         if (bufferSize <= 0)\r
131                                 throw new ArgumentOutOfRangeException("bufferSize");\r
132 \r
133                         FileMode mode;\r
134 \r
135                         if (append)\r
136                                 mode = FileMode.Append;\r
137                         else\r
138                                 mode = FileMode.Create;\r
139                         \r
140                         internalStream = new FileStream (path, mode, FileAccess.Write, FileShare.Read);\r
141 \r
142                         if (append)\r
143                                 internalStream.Position = internalStream.Length;\r
144                         else\r
145                                 internalStream.SetLength (0);\r
146 \r
147                         Initialize(encoding, bufferSize);\r
148                 }\r
149 \r
150                 public virtual bool AutoFlush {\r
151                         get {\r
152                                 return iflush;\r
153                         }\r
154                         set {\r
155                                 iflush = value;\r
156                                 if (iflush)\r
157                                         Flush ();\r
158                         }\r
159                 }\r
160 \r
161                 public virtual Stream BaseStream {\r
162                         get {\r
163                                 return internalStream;\r
164                         }\r
165                 }\r
166 \r
167                 public override Encoding Encoding {\r
168                         get {\r
169                                 return internalEncoding;\r
170                         }\r
171                 }\r
172 \r
173                 protected override void Dispose (bool disposing) \r
174                 {\r
175                         Exception exc = null;\r
176                         if (!DisposedAlready && disposing && internalStream != null && !leave_open) {\r
177                                 try {\r
178                                         Flush();\r
179                                 } catch (Exception e) {\r
180                                         exc = e;\r
181                                 }\r
182                                 DisposedAlready = true;\r
183                                 try {\r
184                                         internalStream.Close ();\r
185                                 } catch (Exception e) {\r
186                                         if (exc == null)\r
187                                                 exc = e;\r
188                                 }\r
189                         }\r
190 \r
191                         internalStream = null;\r
192                         byte_buf = null;\r
193                         internalEncoding = null;\r
194                         decode_buf = null;\r
195                         if (exc != null)\r
196                                 throw exc;\r
197                 }\r
198 \r
199                 public override void Flush ()\r
200                 {\r
201                         CheckState ();\r
202 \r
203                         Decode ();\r
204                         if (byte_pos > 0) {\r
205                                 FlushBytes ();\r
206                                 internalStream.Flush ();\r
207                         }\r
208                 }\r
209 \r
210                 // how the speedup works:\r
211                 // the Write () methods simply copy the characters in a buffer of chars (decode_buf)\r
212                 // Decode () is called when the buffer is full or we need to flash.\r
213                 // Decode () will use the encoding to get the bytes and but them inside\r
214                 // byte_buf. From byte_buf the data is finally outputted to the stream.\r
215                 void FlushBytes () \r
216                 {\r
217                         // write the encoding preamble only at the start of the stream\r
218                         if (!preamble_done && byte_pos > 0) {\r
219                                 byte[] preamble = internalEncoding.GetPreamble ();\r
220                                 if (preamble.Length > 0)\r
221                                         internalStream.Write (preamble, 0, preamble.Length);\r
222                                 preamble_done = true;\r
223                         }\r
224                         internalStream.Write (byte_buf, 0, byte_pos);\r
225                         byte_pos = 0;\r
226                 }\r
227                 \r
228                 void Decode () \r
229                 {\r
230                         if (byte_pos > 0)\r
231                                 FlushBytes ();\r
232                         if (decode_pos > 0) {\r
233                                 int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);\r
234                                 byte_pos += len;\r
235                                 decode_pos = 0;\r
236                         }\r
237                 }\r
238                 \r
239                 public override void Write (char[] buffer, int index, int count) \r
240                 {\r
241                         if (buffer == null)\r
242                                 throw new ArgumentNullException ("buffer");\r
243                         if (index < 0)\r
244                                 throw new ArgumentOutOfRangeException ("index", "< 0");\r
245                         if (count < 0)\r
246                                 throw new ArgumentOutOfRangeException ("count", "< 0");\r
247                         // re-ordered to avoid possible integer overflow\r
248                         if (index > buffer.Length - count)\r
249                                 throw new ArgumentException ("index + count > buffer.Length");\r
250 \r
251                         CheckState ();\r
252 \r
253                         LowLevelWrite (buffer, index, count);\r
254                         if (iflush)\r
255                                 Flush();\r
256                 }\r
257                 \r
258                 void LowLevelWrite (char[] buffer, int index, int count)\r
259                 {\r
260                         while (count > 0) {\r
261                                 int todo = decode_buf.Length - decode_pos;\r
262                                 if (todo == 0) {\r
263                                         Decode ();\r
264                                         todo = decode_buf.Length;\r
265                                 }\r
266                                 if (todo > count)\r
267                                         todo = count;\r
268                                 Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);\r
269                                 count -= todo;\r
270                                 index += todo;\r
271                                 decode_pos += todo;\r
272                         }\r
273                 }\r
274                 \r
275                 void LowLevelWrite (string s)\r
276                 {\r
277                         int count = s.Length;\r
278                         int index = 0;\r
279                         while (count > 0) {\r
280                                 int todo = decode_buf.Length - decode_pos;\r
281                                 if (todo == 0) {\r
282                                         Decode ();\r
283                                         todo = decode_buf.Length;\r
284                                 }\r
285                                 if (todo > count)\r
286                                         todo = count;\r
287                                 \r
288                                 for (int i = 0; i < todo; i ++)\r
289                                         decode_buf [i + decode_pos] = s [i + index];\r
290                                 \r
291                                 count -= todo;\r
292                                 index += todo;\r
293                                 decode_pos += todo;\r
294                         }\r
295                 }\r
296 \r
297                 public override void Write (char value)\r
298                 {\r
299                         CheckState ();\r
300 \r
301                         // the size of decode_buf is always > 0 and\r
302                         // we check for overflow right away\r
303                         if (decode_pos >= decode_buf.Length)\r
304                                 Decode ();\r
305                         decode_buf [decode_pos++] = value;\r
306                         if (iflush)\r
307                                 Flush ();\r
308                 }\r
309 \r
310                 public override void Write (char[] buffer)\r
311                 {\r
312                         CheckState ();\r
313 \r
314                         if (buffer != null)\r
315                                 LowLevelWrite (buffer, 0, buffer.Length);\r
316                         if (iflush)\r
317                                 Flush ();\r
318                 }\r
319 \r
320                 public override void Write (string value) \r
321                 {\r
322                         CheckState ();\r
323 \r
324                         if (value != null)\r
325                                 LowLevelWrite (value);\r
326                         \r
327                         if (iflush)\r
328                                 Flush ();\r
329                 }\r
330 \r
331                 public override void Close()\r
332                 {\r
333                         Dispose (true);\r
334                 }\r
335 \r
336                 ~StreamWriter ()\r
337                 {\r
338                         Dispose(false);\r
339                 }\r
340 \r
341                 void CheckState ()\r
342                 {\r
343                         if (DisposedAlready)\r
344                                 throw new ObjectDisposedException ("StreamWriter");\r
345 \r
346 #if NET_4_5\r
347                         if (async_task != null && async_task.IsCompleted)\r
348                                 throw new InvalidOperationException ();\r
349 #endif\r
350                 }\r
351 \r
352 #if NET_4_5\r
353                 public override Task FlushAsync ()\r
354                 {\r
355                         CheckState ();\r
356                         return async_task = base.FlushAsync ();\r
357                 }\r
358 \r
359                 public override Task WriteAsync (char value)\r
360                 {\r
361                         CheckState ();\r
362                         return async_task = base.WriteAsync (value);\r
363                 }\r
364 \r
365                 public override Task WriteAsync (char[] buffer, int index, int count)\r
366                 {\r
367                         CheckState ();\r
368                         return async_task = base.WriteAsync (buffer, index, count);\r
369                 }\r
370 \r
371                 public override Task WriteAsync (string value)\r
372                 {\r
373                         CheckState ();\r
374                         return async_task = base.WriteAsync (value);\r
375                 }\r
376 \r
377                 public override Task WriteLineAsync ()\r
378                 {\r
379                         CheckState ();\r
380                         return async_task = base.WriteLineAsync ();\r
381                 }\r
382 \r
383                 public override Task WriteLineAsync (char value)\r
384                 {\r
385                         CheckState ();\r
386                         return async_task = base.WriteLineAsync (value);\r
387                 }\r
388 \r
389                 public override Task WriteLineAsync (char[] buffer, int index, int count)\r
390                 {\r
391                         CheckState ();\r
392                         return async_task = base.WriteLineAsync (buffer, index, count);\r
393                 }\r
394 \r
395                 public override Task WriteLineAsync (string value)\r
396                 {\r
397                         CheckState ();\r
398                         return async_task = base.WriteLineAsync (value);\r
399                 }\r
400 #endif\r
401         }\r
402 }\r