BufferedStream.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. //
  2. // System.IO.BufferedStream
  3. //
  4. // Author:
  5. // Matt Kimball ([email protected])
  6. // Ville Palo <[email protected]>
  7. //
  8. // Copyright (C) 2004 Novell (http://www.novell.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.Globalization;
  33. using System.Runtime.InteropServices;
  34. namespace System.IO {
  35. [ComVisible (true)]
  36. public sealed class BufferedStream : Stream {
  37. Stream m_stream;
  38. byte[] m_buffer;
  39. int m_buffer_pos;
  40. int m_buffer_read_ahead;
  41. bool m_buffer_reading;
  42. private bool disposed = false;
  43. public BufferedStream (Stream stream) : this (stream, 4096)
  44. {
  45. }
  46. public BufferedStream (Stream stream, int bufferSize)
  47. {
  48. if (stream == null)
  49. throw new ArgumentNullException ("stream");
  50. // LAMESPEC: documented as < 0
  51. if (bufferSize <= 0)
  52. throw new ArgumentOutOfRangeException ("bufferSize", "<= 0");
  53. if (!stream.CanRead && !stream.CanWrite) {
  54. throw new ObjectDisposedException (
  55. Locale.GetText ("Cannot access a closed Stream."));
  56. }
  57. m_stream = stream;
  58. m_buffer = new byte [bufferSize];
  59. }
  60. public override bool CanRead {
  61. get {
  62. return m_stream.CanRead;
  63. }
  64. }
  65. public override bool CanWrite {
  66. get {
  67. return m_stream.CanWrite;
  68. }
  69. }
  70. public override bool CanSeek {
  71. get {
  72. return m_stream.CanSeek;
  73. }
  74. }
  75. public override long Length {
  76. get {
  77. Flush ();
  78. return m_stream.Length;
  79. }
  80. }
  81. public override long Position {
  82. get {
  83. CheckObjectDisposedException ();
  84. return m_stream.Position - m_buffer_read_ahead + m_buffer_pos;
  85. }
  86. set {
  87. if (value < Position && (Position - value <= m_buffer_pos) && m_buffer_reading) {
  88. m_buffer_pos -= (int) (Position - value);
  89. }
  90. else if (value > Position && (value - Position < m_buffer_read_ahead - m_buffer_pos) && m_buffer_reading) {
  91. m_buffer_pos += (int) (value - Position);
  92. }
  93. else {
  94. Flush();
  95. m_stream.Position = value;
  96. }
  97. }
  98. }
  99. protected override void Dispose (bool disposing)
  100. {
  101. if (disposed)
  102. return;
  103. if (m_buffer != null)
  104. Flush();
  105. m_stream.Close();
  106. m_buffer = null;
  107. disposed = true;
  108. }
  109. public override void Flush ()
  110. {
  111. CheckObjectDisposedException ();
  112. if (m_buffer_reading) {
  113. if (CanSeek)
  114. m_stream.Position = Position;
  115. } else if (m_buffer_pos > 0) {
  116. m_stream.Write(m_buffer, 0, m_buffer_pos);
  117. }
  118. m_buffer_read_ahead = 0;
  119. m_buffer_pos = 0;
  120. }
  121. public override long Seek (long offset, SeekOrigin origin)
  122. {
  123. CheckObjectDisposedException ();
  124. if (!CanSeek) {
  125. throw new NotSupportedException (
  126. Locale.GetText ("Non seekable stream."));
  127. }
  128. Flush ();
  129. return m_stream.Seek (offset, origin);
  130. }
  131. public override void SetLength (long value)
  132. {
  133. CheckObjectDisposedException ();
  134. if (value < 0)
  135. throw new ArgumentOutOfRangeException ("value must be positive");
  136. if (!m_stream.CanWrite && !m_stream.CanSeek)
  137. throw new NotSupportedException ("the stream cannot seek nor write.");
  138. if ((m_stream == null) || (!m_stream.CanRead && !m_stream.CanWrite))
  139. throw new IOException ("the stream is not open");
  140. m_stream.SetLength(value);
  141. if (Position > value)
  142. Position = value;
  143. }
  144. public override int ReadByte ()
  145. {
  146. CheckObjectDisposedException ();
  147. byte[] b = new byte[1];
  148. if (Read(b, 0, 1) == 1) {
  149. return b[0];
  150. } else {
  151. return -1;
  152. }
  153. }
  154. public override void WriteByte (byte value)
  155. {
  156. CheckObjectDisposedException ();
  157. byte[] b = new byte[1];
  158. b[0] = value;
  159. Write(b, 0, 1);
  160. }
  161. public override int Read ([In,Out] byte[] array, int offset, int count)
  162. {
  163. if (array == null)
  164. throw new ArgumentNullException ("array");
  165. CheckObjectDisposedException ();
  166. if (!m_stream.CanRead) {
  167. throw new NotSupportedException (
  168. Locale.GetText ("Cannot read from stream"));
  169. }
  170. if (offset < 0)
  171. throw new ArgumentOutOfRangeException ("offset", "< 0");
  172. if (count < 0)
  173. throw new ArgumentOutOfRangeException ("count", "< 0");
  174. // re-ordered to avoid possible integer overflow
  175. if (array.Length - offset < count)
  176. throw new ArgumentException ("array.Length - offset < count");
  177. if (!m_buffer_reading) {
  178. Flush();
  179. m_buffer_reading = true;
  180. }
  181. if (count <= m_buffer_read_ahead - m_buffer_pos) {
  182. Buffer.BlockCopyInternal (m_buffer, m_buffer_pos, array, offset, count);
  183. m_buffer_pos += count;
  184. if (m_buffer_pos == m_buffer_read_ahead) {
  185. m_buffer_pos = 0;
  186. m_buffer_read_ahead = 0;
  187. }
  188. return count;
  189. }
  190. int ret = m_buffer_read_ahead - m_buffer_pos;
  191. Buffer.BlockCopyInternal (m_buffer, m_buffer_pos, array, offset, ret);
  192. m_buffer_pos = 0;
  193. m_buffer_read_ahead = 0;
  194. offset += ret;
  195. count -= ret;
  196. if (count >= m_buffer.Length) {
  197. ret += m_stream.Read(array, offset, count);
  198. } else {
  199. m_buffer_read_ahead = m_stream.Read(m_buffer, 0, m_buffer.Length);
  200. if (count < m_buffer_read_ahead) {
  201. Buffer.BlockCopyInternal (m_buffer, 0, array, offset, count);
  202. m_buffer_pos = count;
  203. ret += count;
  204. } else {
  205. Buffer.BlockCopyInternal (m_buffer, 0, array, offset, m_buffer_read_ahead);
  206. ret += m_buffer_read_ahead;
  207. m_buffer_read_ahead = 0;
  208. }
  209. }
  210. return ret;
  211. }
  212. public override void Write (byte[] array, int offset, int count)
  213. {
  214. if (array == null)
  215. throw new ArgumentNullException ("array");
  216. CheckObjectDisposedException ();
  217. if (!m_stream.CanWrite) {
  218. throw new NotSupportedException (
  219. Locale.GetText ("Cannot write to stream"));
  220. }
  221. if (offset < 0)
  222. throw new ArgumentOutOfRangeException ("offset", "< 0");
  223. if (count < 0)
  224. throw new ArgumentOutOfRangeException ("count", "< 0");
  225. // avoid possible integer overflow
  226. if (array.Length - offset < count)
  227. throw new ArgumentException ("array.Length - offset < count");
  228. if (m_buffer_reading) {
  229. Flush();
  230. m_buffer_reading = false;
  231. }
  232. // reordered to avoid possible integer overflow
  233. if (m_buffer_pos >= m_buffer.Length - count) {
  234. Flush ();
  235. m_stream.Write (array, offset, count);
  236. }
  237. else {
  238. Buffer.BlockCopyInternal (array, offset, m_buffer, m_buffer_pos, count);
  239. m_buffer_pos += count;
  240. }
  241. }
  242. private void CheckObjectDisposedException ()
  243. {
  244. if (disposed) {
  245. throw new ObjectDisposedException ("BufferedStream",
  246. Locale.GetText ("Stream is closed"));
  247. }
  248. }
  249. }
  250. }