Reorder fields to improve object layout since the runtime can't do it for corlib...
[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 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                 public new static readonly StreamWriter Null = new StreamWriter (Stream.Null, Encoding.UTF8Unmarked, 1);\r
63 \r
64                 public StreamWriter (Stream stream)\r
65                         : this (stream, Encoding.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                         if (null == stream)\r
84                                 throw new ArgumentNullException("stream");\r
85                         if (null == encoding)\r
86                                 throw new ArgumentNullException("encoding");\r
87                         if (bufferSize <= 0)\r
88                                 throw new ArgumentOutOfRangeException("bufferSize");\r
89                         if (!stream.CanWrite)\r
90                                 throw new ArgumentException ("Can not write to stream");\r
91 \r
92                         internalStream = stream;\r
93 \r
94                         Initialize(encoding, bufferSize);\r
95                 }\r
96 \r
97                 public StreamWriter (string path)\r
98                         : this (path, false, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
99 \r
100                 public StreamWriter (string path, bool append)\r
101                         : this (path, append, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
102 \r
103                 public StreamWriter (string path, bool append, Encoding encoding)\r
104                         : this (path, append, encoding, DefaultFileBufferSize) {}\r
105                 \r
106                 public StreamWriter (string path, bool append, Encoding encoding, int bufferSize) {\r
107                         if (null == encoding)\r
108                                 throw new ArgumentNullException("encoding");\r
109                         if (bufferSize <= 0)\r
110                                 throw new ArgumentOutOfRangeException("bufferSize");\r
111 \r
112                         FileMode mode;\r
113 \r
114                         if (append)\r
115                                 mode = FileMode.Append;\r
116                         else\r
117                                 mode = FileMode.Create;\r
118                         \r
119                         internalStream = new FileStream (path, mode, FileAccess.Write, FileShare.Read);\r
120 \r
121                         if (append)\r
122                                 internalStream.Position = internalStream.Length;\r
123                         else\r
124                                 internalStream.SetLength (0);\r
125 \r
126                         Initialize(encoding, bufferSize);\r
127                 }\r
128 \r
129                 public virtual bool AutoFlush {\r
130                         get {\r
131                                 return iflush;\r
132                         }\r
133                         set {\r
134                                 iflush = value;\r
135                                 if (iflush)\r
136                                         Flush ();\r
137                         }\r
138                 }\r
139 \r
140                 public virtual Stream BaseStream {\r
141                         get {\r
142                                 return internalStream;\r
143                         }\r
144                 }\r
145 \r
146                 public override Encoding Encoding {\r
147                         get {\r
148                                 return internalEncoding;\r
149                         }\r
150                 }\r
151 \r
152                 protected override void Dispose (bool disposing) \r
153                 {\r
154                         Exception exc = null;\r
155                         if (!DisposedAlready && disposing && internalStream != null) {\r
156                                 try {\r
157                                         Flush();\r
158                                 } catch (Exception e) {\r
159                                         exc = e;\r
160                                 }\r
161                                 DisposedAlready = true;\r
162                                 try {\r
163                                         internalStream.Close ();\r
164                                 } catch (Exception e) {\r
165                                         if (exc == null)\r
166                                                 exc = e;\r
167                                 }\r
168                         }\r
169 \r
170                         internalStream = null;\r
171                         byte_buf = null;\r
172                         internalEncoding = null;\r
173                         decode_buf = null;\r
174                         if (exc != null)\r
175                                 throw exc;\r
176                 }\r
177 \r
178                 public override void Flush ()\r
179                 {\r
180                         if (DisposedAlready)\r
181                                 throw new ObjectDisposedException("StreamWriter");\r
182 \r
183                         Decode ();\r
184                         if (byte_pos > 0) {\r
185                                 FlushBytes ();\r
186                                 internalStream.Flush ();\r
187                         }\r
188                 }\r
189 \r
190                 // how the speedup works:\r
191                 // the Write () methods simply copy the characters in a buffer of chars (decode_buf)\r
192                 // Decode () is called when the buffer is full or we need to flash.\r
193                 // Decode () will use the encoding to get the bytes and but them inside\r
194                 // byte_buf. From byte_buf the data is finally outputted to the stream.\r
195                 void FlushBytes () \r
196                 {\r
197                         // write the encoding preamble only at the start of the stream\r
198                         if (!preamble_done && byte_pos > 0) {\r
199                                 byte[] preamble = internalEncoding.GetPreamble ();\r
200                                 if (preamble.Length > 0)\r
201                                         internalStream.Write (preamble, 0, preamble.Length);\r
202                                 preamble_done = true;\r
203                         }\r
204                         internalStream.Write (byte_buf, 0, byte_pos);\r
205                         byte_pos = 0;\r
206                 }\r
207                 \r
208                 void Decode () \r
209                 {\r
210                         if (byte_pos > 0)\r
211                                 FlushBytes ();\r
212                         if (decode_pos > 0) {\r
213                                 int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);\r
214                                 byte_pos += len;\r
215                                 decode_pos = 0;\r
216                         }\r
217                 }\r
218                 \r
219                 public override void Write (char[] buffer, int index, int count) \r
220                 {\r
221                         if (DisposedAlready)\r
222                                 throw new ObjectDisposedException("StreamWriter");\r
223                         if (buffer == null)\r
224                                 throw new ArgumentNullException ("buffer");\r
225                         if (index < 0)\r
226                                 throw new ArgumentOutOfRangeException ("index", "< 0");\r
227                         if (count < 0)\r
228                                 throw new ArgumentOutOfRangeException ("count", "< 0");\r
229                         // re-ordered to avoid possible integer overflow\r
230                         if (index > buffer.Length - count)\r
231                                 throw new ArgumentException ("index + count > buffer.Length");\r
232 \r
233                         LowLevelWrite (buffer, index, count);\r
234                         if (iflush)\r
235                                 Flush();\r
236                 }\r
237                 \r
238                 void LowLevelWrite (char[] buffer, int index, int count)\r
239                 {\r
240                         while (count > 0) {\r
241                                 int todo = decode_buf.Length - decode_pos;\r
242                                 if (todo == 0) {\r
243                                         Decode ();\r
244                                         todo = decode_buf.Length;\r
245                                 }\r
246                                 if (todo > count)\r
247                                         todo = count;\r
248                                 Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);\r
249                                 count -= todo;\r
250                                 index += todo;\r
251                                 decode_pos += todo;\r
252                         }\r
253                 }\r
254                 \r
255                 void LowLevelWrite (string s)\r
256                 {\r
257                         int count = s.Length;\r
258                         int index = 0;\r
259                         while (count > 0) {\r
260                                 int todo = decode_buf.Length - decode_pos;\r
261                                 if (todo == 0) {\r
262                                         Decode ();\r
263                                         todo = decode_buf.Length;\r
264                                 }\r
265                                 if (todo > count)\r
266                                         todo = count;\r
267                                 \r
268                                 for (int i = 0; i < todo; i ++)\r
269                                         decode_buf [i + decode_pos] = s [i + index];\r
270                                 \r
271                                 count -= todo;\r
272                                 index += todo;\r
273                                 decode_pos += todo;\r
274                         }\r
275                 }\r
276 \r
277                 public override void Write (char value)\r
278                 {\r
279                         if (DisposedAlready)\r
280                                 throw new ObjectDisposedException("StreamWriter");\r
281 \r
282                         // the size of decode_buf is always > 0 and\r
283                         // we check for overflow right away\r
284                         if (decode_pos >= decode_buf.Length)\r
285                                 Decode ();\r
286                         decode_buf [decode_pos++] = value;\r
287                         if (iflush)\r
288                                 Flush ();\r
289                 }\r
290 \r
291                 public override void Write (char[] buffer)\r
292                 {\r
293                         if (DisposedAlready)\r
294                                 throw new ObjectDisposedException ("StreamWriter");\r
295 \r
296                         if (buffer != null)\r
297                                 LowLevelWrite (buffer, 0, buffer.Length);\r
298                         if (iflush)\r
299                                 Flush ();\r
300                 }\r
301 \r
302                 public override void Write (string value) \r
303                 {\r
304                         if (DisposedAlready)\r
305                                 throw new ObjectDisposedException("StreamWriter");\r
306 \r
307                         if (value != null)\r
308                                 LowLevelWrite (value);\r
309                         \r
310                         if (iflush)\r
311                                 Flush ();\r
312                 }\r
313 \r
314                 public override void Close()\r
315                 {\r
316                         Dispose (true);\r
317                 }\r
318 \r
319                 ~StreamWriter ()\r
320                 {\r
321                         Dispose(false);\r
322                 }\r
323         }\r
324 }\r