lalala
[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 using System.Text;\r
12 using System;\r
13 \r
14 namespace System.IO {\r
15         \r
16         [Serializable]\r
17         public class StreamWriter : TextWriter {\r
18 \r
19                 private Encoding internalEncoding;\r
20 \r
21                 private Stream internalStream;\r
22                 private bool closed = false;\r
23 \r
24                 private bool iflush;\r
25                 \r
26                 private const int DefaultBufferSize = 1024;\r
27                 private const int DefaultFileBufferSize = 4096;\r
28                 private const int MinimumBufferSize = 256;\r
29 \r
30                 private byte[] byte_buf;\r
31                 private int byte_pos;\r
32                 private char[] decode_buf;\r
33                 private int decode_pos;\r
34 \r
35                 private bool DisposedAlready = false;\r
36                 private bool preamble_done = false;\r
37 \r
38                 public new static readonly StreamWriter Null = new StreamWriter (Stream.Null, Encoding.UTF8Unmarked, 0);\r
39 \r
40                 public StreamWriter (Stream stream)\r
41                         : this (stream, Encoding.UTF8Unmarked, DefaultBufferSize) {}\r
42 \r
43                 public StreamWriter (Stream stream, Encoding encoding)\r
44                         : this (stream, encoding, DefaultBufferSize) {}\r
45 \r
46                 internal void Initialize(Encoding encoding, int bufferSize) {\r
47                         internalEncoding = encoding;\r
48                         decode_pos = byte_pos = 0;\r
49                         int BufferSize = Math.Max(bufferSize, MinimumBufferSize);\r
50                         decode_buf = new char [BufferSize];\r
51                         byte_buf = new byte [encoding.GetMaxByteCount (BufferSize)];\r
52                 }\r
53 \r
54                 //[MonoTODO("Nothing is done with bufferSize")]\r
55                 public StreamWriter (Stream stream, Encoding encoding, int bufferSize) {\r
56                         if (null == stream)\r
57                                 throw new ArgumentNullException("stream");\r
58                         if (null == encoding)\r
59                                 throw new ArgumentNullException("encoding");\r
60                         if (bufferSize < 0)\r
61                                 throw new ArgumentOutOfRangeException("bufferSize");\r
62                         if (!stream.CanWrite)\r
63                                 throw new ArgumentException("Can not write to stream", "stream");\r
64 \r
65                         internalStream = stream;\r
66 \r
67                         Initialize(encoding, bufferSize);\r
68                 }\r
69 \r
70                 public StreamWriter (string path)\r
71                         : this (path, false, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
72 \r
73                 public StreamWriter (string path, bool append)\r
74                         : this (path, append, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}\r
75 \r
76                 public StreamWriter (string path, bool append, Encoding encoding)\r
77                         : this (path, append, encoding, DefaultFileBufferSize) {}\r
78                 \r
79                 public StreamWriter (string path, bool append, Encoding encoding, int bufferSize) {\r
80                         if (null == path)\r
81                                 throw new ArgumentNullException("path");\r
82                         if (String.Empty == path)\r
83                                 throw new ArgumentException("path cannot be empty string");\r
84                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)\r
85                                 throw new ArgumentException("path contains invalid characters");\r
86 \r
87                         if (null == encoding)\r
88                                 throw new ArgumentNullException("encoding");\r
89                         if (bufferSize < 0)\r
90                                 throw new ArgumentOutOfRangeException("bufferSize");\r
91 \r
92                         string DirName = Path.GetDirectoryName(path);\r
93                         if (DirName != String.Empty && !Directory.Exists(DirName))\r
94                                 throw new DirectoryNotFoundException();\r
95 \r
96                         FileMode mode;\r
97 \r
98                         if (append)\r
99                                 mode = FileMode.Append;\r
100                         else\r
101                                 mode = FileMode.Create;\r
102                         \r
103                         internalStream = new FileStream (path, mode, FileAccess.Write);\r
104 \r
105                         if (append)\r
106                                 internalStream.Position = internalStream.Length;\r
107                         else\r
108                                 internalStream.SetLength (0);\r
109 \r
110                         Initialize(encoding, bufferSize);\r
111                 }\r
112 \r
113                 public virtual bool AutoFlush {\r
114                         get {\r
115                                 return iflush;\r
116                         }\r
117                         set {\r
118                                 iflush = value;\r
119                         }\r
120                 }\r
121 \r
122                 public virtual Stream BaseStream {\r
123                         get {\r
124                                 return internalStream;\r
125                         }\r
126                 }\r
127 \r
128                 public override Encoding Encoding {\r
129                         get {\r
130                                 return internalEncoding;\r
131                         }\r
132                 }\r
133 \r
134                 protected override void Dispose (bool disposing) {\r
135                         if (!DisposedAlready && disposing && internalStream != null) {\r
136                                 Flush();\r
137                                 DisposedAlready = true;\r
138                                 internalStream.Close ();\r
139                         }\r
140 \r
141                         internalStream = null;\r
142                         byte_buf = null;\r
143                         internalEncoding = null;\r
144                         decode_buf = null;\r
145                 }\r
146 \r
147                 public override void Flush () {\r
148                         if (DisposedAlready)\r
149                                 throw new ObjectDisposedException("StreamWriter");\r
150 \r
151                         Decode ();\r
152                         if (byte_pos > 0) {\r
153                                 FlushBytes ();\r
154                                 internalStream.Flush ();\r
155                         }\r
156                 }\r
157 \r
158                 // how the speedup works:\r
159                 // the Write () methods simply copy the characters in a buffer of chars (decode_buf)\r
160                 // Decode () is called when the buffer is full or we need to flash.\r
161                 // Decode () will use the encoding to get the bytes and but them inside\r
162                 // byte_buf. From byte_buf the data is finally outputted to the stream.\r
163                 void FlushBytes () {\r
164                         // write the encoding preamble only at the start of the stream\r
165                         if (!preamble_done && byte_pos > 0) {\r
166                                 byte[] preamble = internalEncoding.GetPreamble ();\r
167                                 if (preamble.Length > 0)\r
168                                         internalStream.Write (preamble, 0, preamble.Length);\r
169                                 preamble_done = true;\r
170                         }\r
171                         internalStream.Write (byte_buf, 0, byte_pos);\r
172                         byte_pos = 0;\r
173                 }\r
174                 \r
175                 void Decode () {\r
176                         if (byte_pos > 0)\r
177                                 FlushBytes ();\r
178                         if (decode_pos > 0) {\r
179                                 int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);\r
180                                 byte_pos += len;\r
181                                 decode_pos = 0;\r
182                         }\r
183                 }\r
184                 \r
185                 public override void Write (char[] buffer, int index, int count) {\r
186                         if (DisposedAlready)\r
187                                 throw new ObjectDisposedException("StreamWriter");\r
188 \r
189                         if (buffer == null)\r
190                                 throw new ArgumentNullException ("buffer");\r
191                         if (index < 0 || index > buffer.Length)\r
192                                 throw new ArgumentOutOfRangeException ("index");\r
193                         if (count < 0 || (index + count) > buffer.Length)\r
194                                 throw new ArgumentOutOfRangeException ("count");\r
195 \r
196                         LowLevelWrite (buffer, index, count);\r
197                         if (iflush)\r
198                                 Flush();\r
199                 }\r
200                 \r
201                 void LowLevelWrite (char[] buffer, int index, int count) {\r
202                         while (count > 0) {\r
203                                 int todo = decode_buf.Length - decode_pos;\r
204                                 if (todo == 0) {\r
205                                         Decode ();\r
206                                         todo = decode_buf.Length;\r
207                                 }\r
208                                 if (todo > count)\r
209                                         todo = count;\r
210                                 Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);\r
211                                 count -= todo;\r
212                                 index += todo;\r
213                                 decode_pos += todo;\r
214                         }\r
215                 }\r
216 \r
217                 public override void Write (char value)\r
218                 {\r
219                         // the size of decode_buf is always > 0 and\r
220                         // we check for overflow right away\r
221                         if (decode_pos >= decode_buf.Length)\r
222                                 Decode ();\r
223                         decode_buf [decode_pos++] = value;\r
224                         if (iflush)\r
225                                 Flush ();\r
226                 }\r
227 \r
228                 public override void Write (char [] value)\r
229                 {\r
230                         LowLevelWrite (value, 0, value.Length);\r
231                         if (iflush)\r
232                                 Flush ();\r
233                 }\r
234 \r
235                 public override void Write (string value) {\r
236                         if (DisposedAlready)\r
237                                 throw new ObjectDisposedException("StreamWriter");\r
238 \r
239                         if (value != null)\r
240                                 LowLevelWrite (value.ToCharArray (), 0, value.Length);\r
241                         if (iflush)\r
242                                 Flush ();\r
243                 }\r
244 \r
245 \r
246                 public override void Close()\r
247                 {\r
248                         Dispose (true);\r
249                 }\r
250 \r
251                 ~StreamWriter() {\r
252                         Dispose(false);\r
253                 }\r
254         }\r
255 }\r