5f25a0cf1b49157939508044eb4eaa3f896db77b
[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 //\r
8 // (C) Ximian, Inc.  http://www.ximian.com\r
9 //\r
10 \r
11 //\r
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
13 //\r
14 // Permission is hereby granted, free of charge, to any person obtaining\r
15 // a copy of this software and associated documentation files (the\r
16 // "Software"), to deal in the Software without restriction, including\r
17 // without limitation the rights to use, copy, modify, merge, publish,\r
18 // distribute, sublicense, and/or sell copies of the Software, and to\r
19 // permit persons to whom the Software is furnished to do so, subject to\r
20 // the following conditions:\r
21 // \r
22 // The above copyright notice and this permission notice shall be\r
23 // included in all copies or substantial portions of the Software.\r
24 // \r
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
32 //\r
33 \r
34 using System.Text;\r
35 using System;\r
36 \r
37 using System.Runtime.InteropServices;\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 bool iflush;\r
50                 \r
51                 private const int DefaultBufferSize = 1024;\r
52                 private const int DefaultFileBufferSize = 4096;\r
53                 private const int MinimumBufferSize = 256;\r
54 \r
55                 private byte[] byte_buf;\r
56                 private int byte_pos;\r
57                 private char[] decode_buf;\r
58                 private int decode_pos;\r
59 \r
60                 private bool DisposedAlready;\r
61                 private bool preamble_done;\r
62 \r
63                 public new static readonly StreamWriter Null = new StreamWriter (Stream.Null, Encoding.UTF8Unmarked, 1);\r
64 \r
65                 public StreamWriter (Stream stream)\r
66                         : this (stream, Encoding.UTF8Unmarked, DefaultBufferSize) {}\r
67 \r
68                 public StreamWriter (Stream stream, Encoding encoding)\r
69                         : this (stream, encoding, DefaultBufferSize) {}\r
70 \r
71                 internal void Initialize(Encoding encoding, int bufferSize) {\r
72                         internalEncoding = encoding;\r
73                         decode_pos = byte_pos = 0;\r
74                         int BufferSize = Math.Max(bufferSize, MinimumBufferSize);\r
75                         decode_buf = new char [BufferSize];\r
76                         byte_buf = new byte [encoding.GetMaxByteCount (BufferSize)];\r
77 \r
78                         // Fixes bug http://bugzilla.ximian.com/show_bug.cgi?id=74513\r
79                         if (internalStream.CanSeek && internalStream.Position > 0)\r
80                                 preamble_done = true;\r
81                 }\r
82 \r
83                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize) {\r
84                         if (null == stream)\r
85                                 throw new ArgumentNullException("stream");\r
86                         if (null == encoding)\r
87                                 throw new ArgumentNullException("encoding");\r
88                         if (bufferSize <= 0)\r
89                                 throw new ArgumentOutOfRangeException("bufferSize");\r
90                         if (!stream.CanWrite)\r
91                                 throw new ArgumentException ("Can not write to stream");\r
92 \r
93                         internalStream = stream;\r
94 \r
95                         Initialize(encoding, bufferSize);\r
96                 }\r
97 \r
98                 public StreamWriter (string path)\r
99                         : this (path, false, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
100 \r
101                 public StreamWriter (string path, bool append)\r
102                         : this (path, append, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
103 \r
104                 public StreamWriter (string path, bool append, Encoding encoding)\r
105                         : this (path, append, encoding, DefaultFileBufferSize) {}\r
106                 \r
107                 public StreamWriter (string path, bool append, Encoding encoding, int bufferSize) {\r
108                         if (null == encoding)\r
109                                 throw new ArgumentNullException("encoding");\r
110                         if (bufferSize <= 0)\r
111                                 throw new ArgumentOutOfRangeException("bufferSize");\r
112 \r
113                         FileMode mode;\r
114 \r
115                         if (append)\r
116                                 mode = FileMode.Append;\r
117                         else\r
118                                 mode = FileMode.Create;\r
119                         \r
120                         internalStream = new FileStream (path, mode, FileAccess.Write, FileShare.Read);\r
121 \r
122                         if (append)\r
123                                 internalStream.Position = internalStream.Length;\r
124                         else\r
125                                 internalStream.SetLength (0);\r
126 \r
127                         Initialize(encoding, bufferSize);\r
128                 }\r
129 \r
130                 public virtual bool AutoFlush {\r
131                         get {\r
132                                 return iflush;\r
133                         }\r
134                         set {\r
135                                 iflush = value;\r
136                                 if (iflush)\r
137                                         Flush ();\r
138                         }\r
139                 }\r
140 \r
141                 public virtual Stream BaseStream {\r
142                         get {\r
143                                 return internalStream;\r
144                         }\r
145                 }\r
146 \r
147                 public override Encoding Encoding {\r
148                         get {\r
149                                 return internalEncoding;\r
150                         }\r
151                 }\r
152 \r
153                 protected override void Dispose (bool disposing) \r
154                 {\r
155                         Exception exc = null;\r
156                         if (!DisposedAlready && disposing && internalStream != null) {\r
157                                 try {\r
158                                         Flush();\r
159                                 } catch (Exception e) {\r
160                                         exc = e;\r
161                                 }\r
162                                 DisposedAlready = true;\r
163                                 try {\r
164                                         internalStream.Close ();\r
165                                 } catch (Exception e) {\r
166                                         if (exc == null)\r
167                                                 exc = e;\r
168                                 }\r
169                         }\r
170 \r
171                         internalStream = null;\r
172                         byte_buf = null;\r
173                         internalEncoding = null;\r
174                         decode_buf = null;\r
175                         if (exc != null)\r
176                                 throw exc;\r
177                 }\r
178 \r
179                 public override void Flush ()\r
180                 {\r
181                         if (DisposedAlready)\r
182                                 throw new ObjectDisposedException("StreamWriter");\r
183 \r
184                         Decode ();\r
185                         if (byte_pos > 0) {\r
186                                 FlushBytes ();\r
187                                 internalStream.Flush ();\r
188                         }\r
189                 }\r
190 \r
191                 // how the speedup works:\r
192                 // the Write () methods simply copy the characters in a buffer of chars (decode_buf)\r
193                 // Decode () is called when the buffer is full or we need to flash.\r
194                 // Decode () will use the encoding to get the bytes and but them inside\r
195                 // byte_buf. From byte_buf the data is finally outputted to the stream.\r
196                 void FlushBytes () \r
197                 {\r
198                         // write the encoding preamble only at the start of the stream\r
199                         if (!preamble_done && byte_pos > 0) {\r
200                                 byte[] preamble = internalEncoding.GetPreamble ();\r
201                                 if (preamble.Length > 0)\r
202                                         internalStream.Write (preamble, 0, preamble.Length);\r
203                                 preamble_done = true;\r
204                         }\r
205                         internalStream.Write (byte_buf, 0, byte_pos);\r
206                         byte_pos = 0;\r
207                 }\r
208                 \r
209                 void Decode () \r
210                 {\r
211                         if (byte_pos > 0)\r
212                                 FlushBytes ();\r
213                         if (decode_pos > 0) {\r
214                                 int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);\r
215                                 byte_pos += len;\r
216                                 decode_pos = 0;\r
217                         }\r
218                 }\r
219                 \r
220                 public override void Write (char[] buffer, int index, int count) \r
221                 {\r
222                         if (DisposedAlready)\r
223                                 throw new ObjectDisposedException("StreamWriter");\r
224                         if (buffer == null)\r
225                                 throw new ArgumentNullException ("buffer");\r
226                         if (index < 0)\r
227                                 throw new ArgumentOutOfRangeException ("index", "< 0");\r
228                         if (count < 0)\r
229                                 throw new ArgumentOutOfRangeException ("count", "< 0");\r
230                         // re-ordered to avoid possible integer overflow\r
231                         if (index > buffer.Length - count)\r
232                                 throw new ArgumentException ("index + count > buffer.Length");\r
233 \r
234                         LowLevelWrite (buffer, index, count);\r
235                         if (iflush)\r
236                                 Flush();\r
237                 }\r
238                 \r
239                 void LowLevelWrite (char[] buffer, int index, int count)\r
240                 {\r
241                         while (count > 0) {\r
242                                 int todo = decode_buf.Length - decode_pos;\r
243                                 if (todo == 0) {\r
244                                         Decode ();\r
245                                         todo = decode_buf.Length;\r
246                                 }\r
247                                 if (todo > count)\r
248                                         todo = count;\r
249                                 Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);\r
250                                 count -= todo;\r
251                                 index += todo;\r
252                                 decode_pos += todo;\r
253                         }\r
254                 }\r
255                 \r
256                 void LowLevelWrite (string s)\r
257                 {\r
258                         int count = s.Length;\r
259                         int index = 0;\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                                 \r
269                                 for (int i = 0; i < todo; i ++)\r
270                                         decode_buf [i + decode_pos] = s [i + index];\r
271                                 \r
272                                 count -= todo;\r
273                                 index += todo;\r
274                                 decode_pos += todo;\r
275                         }\r
276                 }\r
277 \r
278                 public override void Write (char value)\r
279                 {\r
280                         if (DisposedAlready)\r
281                                 throw new ObjectDisposedException("StreamWriter");\r
282 \r
283                         // the size of decode_buf is always > 0 and\r
284                         // we check for overflow right away\r
285                         if (decode_pos >= decode_buf.Length)\r
286                                 Decode ();\r
287                         decode_buf [decode_pos++] = value;\r
288                         if (iflush)\r
289                                 Flush ();\r
290                 }\r
291 \r
292                 public override void Write (char[] buffer)\r
293                 {\r
294                         if (DisposedAlready)\r
295                                 throw new ObjectDisposedException ("StreamWriter");\r
296 \r
297                         if (buffer != null)\r
298                                 LowLevelWrite (buffer, 0, buffer.Length);\r
299                         if (iflush)\r
300                                 Flush ();\r
301                 }\r
302 \r
303                 public override void Write (string value) \r
304                 {\r
305                         if (DisposedAlready)\r
306                                 throw new ObjectDisposedException("StreamWriter");\r
307 \r
308                         if (value != null)\r
309                                 LowLevelWrite (value);\r
310                         \r
311                         if (iflush)\r
312                                 Flush ();\r
313                 }\r
314 \r
315                 public override void Close()\r
316                 {\r
317                         Dispose (true);\r
318                 }\r
319 \r
320                 ~StreamWriter ()\r
321                 {\r
322                         Dispose(false);\r
323                 }\r
324         }\r
325 }\r