StreamWriter.cs 8.3 KB

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