StreamWriter.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //
  2. // System.IO.StreamWriter.cs
  3. //
  4. // Authors:
  5. // Dietmar Maurer ([email protected])
  6. // Paolo Molaro ([email protected])
  7. //
  8. // (C) Ximian, Inc. http://www.ximian.com
  9. //
  10. //
  11. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  12. //
  13. // Permission is hereby granted, free of charge, to any person obtaining
  14. // a copy of this software and associated documentation files (the
  15. // "Software"), to deal in the Software without restriction, including
  16. // without limitation the rights to use, copy, modify, merge, publish,
  17. // distribute, sublicense, and/or sell copies of the Software, and to
  18. // permit persons to whom the Software is furnished to do so, subject to
  19. // the following conditions:
  20. //
  21. // The above copyright notice and this permission notice shall be
  22. // included in all copies or substantial portions of the Software.
  23. //
  24. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  28. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  29. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  30. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  31. //
  32. using System.Text;
  33. using System;
  34. namespace System.IO {
  35. [Serializable]
  36. public class StreamWriter : TextWriter {
  37. private Encoding internalEncoding;
  38. private Stream internalStream;
  39. private bool closed = false;
  40. private bool iflush;
  41. private const int DefaultBufferSize = 1024;
  42. private const int DefaultFileBufferSize = 4096;
  43. private const int MinimumBufferSize = 256;
  44. private byte[] byte_buf;
  45. private int byte_pos;
  46. private char[] decode_buf;
  47. private int decode_pos;
  48. private bool DisposedAlready = false;
  49. private bool preamble_done = false;
  50. public new static readonly StreamWriter Null = new StreamWriter (Stream.Null, Encoding.UTF8Unmarked, 0);
  51. public StreamWriter (Stream stream)
  52. : this (stream, Encoding.UTF8Unmarked, DefaultBufferSize) {}
  53. public StreamWriter (Stream stream, Encoding encoding)
  54. : this (stream, encoding, DefaultBufferSize) {}
  55. internal void Initialize(Encoding encoding, int bufferSize) {
  56. internalEncoding = encoding;
  57. decode_pos = byte_pos = 0;
  58. int BufferSize = Math.Max(bufferSize, MinimumBufferSize);
  59. decode_buf = new char [BufferSize];
  60. byte_buf = new byte [encoding.GetMaxByteCount (BufferSize)];
  61. }
  62. //[MonoTODO("Nothing is done with bufferSize")]
  63. public StreamWriter (Stream stream, Encoding encoding, int bufferSize) {
  64. if (null == stream)
  65. throw new ArgumentNullException("stream");
  66. if (null == encoding)
  67. throw new ArgumentNullException("encoding");
  68. if (bufferSize < 0)
  69. throw new ArgumentOutOfRangeException("bufferSize");
  70. if (!stream.CanWrite)
  71. throw new ArgumentException("Can not write to stream", "stream");
  72. internalStream = stream;
  73. Initialize(encoding, bufferSize);
  74. }
  75. public StreamWriter (string path)
  76. : this (path, false, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}
  77. public StreamWriter (string path, bool append)
  78. : this (path, append, Encoding.UTF8Unmarked, DefaultFileBufferSize) {}
  79. public StreamWriter (string path, bool append, Encoding encoding)
  80. : this (path, append, encoding, DefaultFileBufferSize) {}
  81. public StreamWriter (string path, bool append, Encoding encoding, int bufferSize) {
  82. if (null == path)
  83. throw new ArgumentNullException("path");
  84. if (String.Empty == path)
  85. throw new ArgumentException("path cannot be empty string");
  86. if (path.IndexOfAny (Path.InvalidPathChars) != -1)
  87. throw new ArgumentException("path contains invalid characters");
  88. if (null == encoding)
  89. throw new ArgumentNullException("encoding");
  90. if (bufferSize < 0)
  91. throw new ArgumentOutOfRangeException("bufferSize");
  92. string DirName = Path.GetDirectoryName(path);
  93. if (DirName != String.Empty && !Directory.Exists(DirName))
  94. throw new DirectoryNotFoundException();
  95. FileMode mode;
  96. if (append)
  97. mode = FileMode.Append;
  98. else
  99. mode = FileMode.Create;
  100. internalStream = new FileStream (path, mode, FileAccess.Write);
  101. if (append)
  102. internalStream.Position = internalStream.Length;
  103. else
  104. internalStream.SetLength (0);
  105. Initialize(encoding, bufferSize);
  106. }
  107. public virtual bool AutoFlush {
  108. get {
  109. return iflush;
  110. }
  111. set {
  112. if (DisposedAlready)
  113. throw new ObjectDisposedException("StreamWriter");
  114. iflush = value;
  115. }
  116. }
  117. public virtual Stream BaseStream {
  118. get {
  119. return internalStream;
  120. }
  121. }
  122. public override Encoding Encoding {
  123. get {
  124. return internalEncoding;
  125. }
  126. }
  127. protected override void Dispose (bool disposing)
  128. {
  129. if (!DisposedAlready && disposing && internalStream != null) {
  130. Flush();
  131. DisposedAlready = true;
  132. internalStream.Close ();
  133. }
  134. internalStream = null;
  135. byte_buf = null;
  136. internalEncoding = null;
  137. decode_buf = null;
  138. }
  139. public override void Flush ()
  140. {
  141. if (DisposedAlready)
  142. throw new ObjectDisposedException("StreamWriter");
  143. Decode ();
  144. if (byte_pos > 0) {
  145. FlushBytes ();
  146. internalStream.Flush ();
  147. }
  148. }
  149. // how the speedup works:
  150. // the Write () methods simply copy the characters in a buffer of chars (decode_buf)
  151. // Decode () is called when the buffer is full or we need to flash.
  152. // Decode () will use the encoding to get the bytes and but them inside
  153. // byte_buf. From byte_buf the data is finally outputted to the stream.
  154. void FlushBytes ()
  155. {
  156. // write the encoding preamble only at the start of the stream
  157. if (!preamble_done && byte_pos > 0) {
  158. byte[] preamble = internalEncoding.GetPreamble ();
  159. if (preamble.Length > 0)
  160. internalStream.Write (preamble, 0, preamble.Length);
  161. preamble_done = true;
  162. }
  163. internalStream.Write (byte_buf, 0, byte_pos);
  164. byte_pos = 0;
  165. }
  166. void Decode ()
  167. {
  168. if (byte_pos > 0)
  169. FlushBytes ();
  170. if (decode_pos > 0) {
  171. int len = internalEncoding.GetBytes (decode_buf, 0, decode_pos, byte_buf, byte_pos);
  172. byte_pos += len;
  173. decode_pos = 0;
  174. }
  175. }
  176. public override void Write (char[] buffer, int index, int count)
  177. {
  178. if (DisposedAlready)
  179. throw new ObjectDisposedException("StreamWriter");
  180. if (buffer == null)
  181. throw new ArgumentNullException ("buffer");
  182. if (index < 0)
  183. throw new ArgumentOutOfRangeException ("index", "< 0");
  184. if (count < 0)
  185. throw new ArgumentOutOfRangeException ("count", "< 0");
  186. // re-ordered to avoid possible integer overflow
  187. if (index > buffer.Length - count)
  188. throw new ArgumentException ("index + count > buffer.Length");
  189. LowLevelWrite (buffer, index, count);
  190. if (iflush)
  191. Flush();
  192. }
  193. void LowLevelWrite (char[] buffer, int index, int count)
  194. {
  195. while (count > 0) {
  196. int todo = decode_buf.Length - decode_pos;
  197. if (todo == 0) {
  198. Decode ();
  199. todo = decode_buf.Length;
  200. }
  201. if (todo > count)
  202. todo = count;
  203. Buffer.BlockCopy (buffer, index * 2, decode_buf, decode_pos * 2, todo * 2);
  204. count -= todo;
  205. index += todo;
  206. decode_pos += todo;
  207. }
  208. }
  209. public override void Write (char value)
  210. {
  211. if (DisposedAlready)
  212. throw new ObjectDisposedException("StreamWriter");
  213. // the size of decode_buf is always > 0 and
  214. // we check for overflow right away
  215. if (decode_pos >= decode_buf.Length)
  216. Decode ();
  217. decode_buf [decode_pos++] = value;
  218. if (iflush)
  219. Flush ();
  220. }
  221. public override void Write (char[] value)
  222. {
  223. if (DisposedAlready)
  224. throw new ObjectDisposedException("StreamWriter");
  225. if (value != null)
  226. LowLevelWrite (value, 0, value.Length);
  227. if (iflush)
  228. Flush ();
  229. }
  230. public override void Write (string value)
  231. {
  232. if (DisposedAlready)
  233. throw new ObjectDisposedException("StreamWriter");
  234. if (value != null)
  235. LowLevelWrite (value.ToCharArray (), 0, value.Length);
  236. if (iflush)
  237. Flush ();
  238. }
  239. public override void Close()
  240. {
  241. closed = true;
  242. Dispose (true);
  243. }
  244. ~StreamWriter ()
  245. {
  246. Dispose(false);
  247. }
  248. }
  249. }