MemoryStream.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. //
  2. // System.IO.MemoryStream
  3. //
  4. // Author: Marcin Szczepanski ([email protected])
  5. //
  6. // TODO: Clarify some of the lamespec issues
  7. //
  8. namespace System.IO {
  9. [Serializable]
  10. public class MemoryStream : Stream {
  11. private bool canRead;
  12. private bool canSeek;
  13. private bool canWrite;
  14. private bool allowGetBuffer;
  15. private int capacity;
  16. private byte[] internalBuffer;
  17. private int initialLength;
  18. private bool expandable;
  19. private bool streamClosed = false;
  20. private long position = 0;
  21. public MemoryStream() {
  22. canRead = true;
  23. canSeek = true;
  24. canWrite = true;
  25. capacity = 0;
  26. internalBuffer = new byte[0];
  27. allowGetBuffer = true;
  28. expandable = true;
  29. }
  30. public MemoryStream( byte[] buffer ) {
  31. InternalConstructor( buffer, 0, buffer.Length, true, false );
  32. }
  33. public MemoryStream( int capacity ) {
  34. canRead = true;
  35. canSeek = true;
  36. canWrite = true;
  37. this.capacity = capacity;
  38. initialLength = 0;
  39. internalBuffer = new byte[ 0 ];
  40. expandable = true;
  41. allowGetBuffer = true;
  42. }
  43. public MemoryStream( byte[] buffer, bool writeable ) {
  44. if( buffer == null ) {
  45. throw new ArgumentNullException();
  46. }
  47. InternalConstructor( buffer, 0, buffer.Length, writeable, true );
  48. }
  49. public MemoryStream( byte[] buffer, int index, int count ) {
  50. if( buffer == null ) {
  51. throw new ArgumentNullException();
  52. }
  53. InternalConstructor( buffer, index, count, true, false );
  54. }
  55. public MemoryStream( byte[] buffer, int index, int count, bool writeable ) {
  56. if( buffer == null ) {
  57. throw new ArgumentNullException();
  58. }
  59. InternalConstructor( buffer, index, count, writeable, true );
  60. }
  61. public MemoryStream( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {
  62. InternalConstructor( buffer, index, count, writeable, publicallyVisible );
  63. }
  64. private void InternalConstructor( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {
  65. if( buffer == null ) {
  66. throw new ArgumentNullException();
  67. } else if ( index < 0 || count < 0 ) {
  68. throw new ArgumentOutOfRangeException();
  69. } else if ( buffer.Length - index < count ) {
  70. throw new ArgumentException();
  71. }
  72. // LAMESPEC: The spec says to throw an UnauthorisedAccessException if
  73. // publicallyVisibile is fale?! Doesn't that defy the point of having
  74. // it there in the first place. I'll leave it out for now.
  75. canRead = true;
  76. canSeek = true;
  77. canWrite = writeable;
  78. initialLength = count;
  79. internalBuffer = new byte[ count ];
  80. capacity = count;
  81. Array.Copy( buffer, index, internalBuffer, 0, count );
  82. allowGetBuffer = publicallyVisible;
  83. expandable = false;
  84. }
  85. public override bool CanRead {
  86. get {
  87. return this.canRead;
  88. }
  89. }
  90. public override bool CanSeek {
  91. get {
  92. return this.canSeek;
  93. }
  94. }
  95. public override bool CanWrite {
  96. get {
  97. return this.canWrite;
  98. }
  99. }
  100. public virtual int Capacity {
  101. get {
  102. return this.capacity;
  103. }
  104. set {
  105. if( value < 0 || value < capacity ) {
  106. throw new ArgumentOutOfRangeException( "New capacity cannot be negative or less than the current capacity" );
  107. } else if( !expandable ) {
  108. throw new NotSupportedException( "Cannot expand this MemoryStream" );
  109. }
  110. byte[] newBuffer = new byte[ value ];
  111. Array.Copy( internalBuffer, 0, newBuffer, 0, capacity );
  112. capacity = value;
  113. }
  114. }
  115. public override long Length {
  116. get {
  117. // LAMESPEC: The spec says to throw an IOException if the
  118. // stream is closed and an ObjectDisposedException if
  119. // "methods were called after the stream was closed". What
  120. // is the difference?
  121. if( streamClosed ) {
  122. throw new IOException( "MemoryStream is closed" );
  123. }
  124. return internalBuffer.Length;
  125. }
  126. }
  127. public override long Position {
  128. get {
  129. if( streamClosed ) {
  130. throw new IOException( "MemoryStream is closed" );
  131. }
  132. return position;
  133. }
  134. set {
  135. if( position < 0 ) {
  136. throw new ArgumentOutOfRangeException( "Position cannot be negative" );
  137. } else if( streamClosed ) {
  138. throw new IOException( "MemoryStream is closed" );
  139. }
  140. position = value;
  141. if( position > internalBuffer.Length + 1 ) {
  142. position = internalBuffer.Length + 1;
  143. }
  144. }
  145. }
  146. public override void Close() {
  147. if( streamClosed ) {
  148. throw new IOException( "MemoryStream already closed" );
  149. }
  150. streamClosed = true;
  151. }
  152. public override void Flush() { }
  153. public virtual byte[] GetBuffer() {
  154. if( !allowGetBuffer ) {
  155. throw new UnauthorizedAccessException();
  156. }
  157. return internalBuffer;
  158. }
  159. public override int Read( byte[] buffer, int offset, int count ) {
  160. if( buffer == null ) {
  161. throw new ArgumentNullException();
  162. } else if( offset < 0 || count < 0 ) {
  163. throw new ArgumentOutOfRangeException();
  164. } else if( buffer.Length - offset < count ) {
  165. throw new ArgumentException();
  166. } else if ( streamClosed ) {
  167. throw new ObjectDisposedException( "MemoryStream" );
  168. }
  169. long ReadTo;
  170. if( position + count > internalBuffer.Length ) {
  171. ReadTo = internalBuffer.Length;
  172. } else {
  173. ReadTo = position + (long)count;
  174. }
  175. Array.Copy( internalBuffer, (int)position, buffer, offset, (int)(ReadTo - position) );
  176. int bytesRead = (int)(ReadTo - position);
  177. position = ReadTo;
  178. return bytesRead;
  179. }
  180. public override int ReadByte( ) {
  181. if( streamClosed ) {
  182. throw new ObjectDisposedException( "MemoryStream" );
  183. }
  184. // LAMESPEC: What happens if we're at the end of the stream? It's unspecified in the
  185. // docs but tests against the MS impl. show it returns -1
  186. //
  187. if( position >= internalBuffer.Length ) {
  188. return -1;
  189. } else {
  190. return internalBuffer[ position++ ];
  191. }
  192. }
  193. public override long Seek( long offset, SeekOrigin loc ) {
  194. long refPoint;
  195. if( streamClosed ) {
  196. throw new ObjectDisposedException( "MemoryStream" );
  197. }
  198. switch( loc ) {
  199. case SeekOrigin.Begin:
  200. refPoint = 0;
  201. break;
  202. case SeekOrigin.Current:
  203. refPoint = position;
  204. break;
  205. case SeekOrigin.End:
  206. refPoint = internalBuffer.Length;
  207. break;
  208. default:
  209. throw new ArgumentException( "Invalid SeekOrigin" );
  210. }
  211. // LAMESPEC: My goodness, how may LAMESPECs are there in this
  212. // class! :) In the spec for the Position property it's stated
  213. // "The position must not be more than one byte beyond the end of the stream."
  214. // In the spec for seek it says "Seeking to any location beyond the length of the
  215. // stream is supported." That's a contradiction i'd say.
  216. // I guess seek can go anywhere but if you use position it may get moved back.
  217. if( refPoint + offset < 0 ) {
  218. throw new IOException( "Attempted to seek before start of MemoryStream" );
  219. } else if( offset > internalBuffer.Length ) {
  220. throw new ArgumentOutOfRangeException( "Offset cannot be greater than length of MemoryStream" );
  221. }
  222. position = refPoint + offset;
  223. return position;
  224. }
  225. public override void SetLength( long value ) {
  226. if( streamClosed ) {
  227. throw new ObjectDisposedException( "MemoryStream" );
  228. } else if( !expandable && value > capacity ) {
  229. throw new NotSupportedException( "Expanding this MemoryStream is not supported" );
  230. } else if( !canWrite ) {
  231. throw new IOException( "Cannot write to this MemoryStream" );
  232. } else if( value < 0 ) {
  233. // LAMESPEC: AGAIN! It says to throw this exception if value is
  234. // greater than "the maximum length of the MemoryStream". I haven't
  235. // seen anywhere mention what the maximum length of a MemoryStream is and
  236. // since we're this far this memory stream is expandable.
  237. throw new ArgumentOutOfRangeException();
  238. }
  239. byte[] newBuffer;
  240. newBuffer = new byte[ value ];
  241. if (value < internalBuffer.Length) {
  242. // truncate
  243. Array.Copy( internalBuffer, 0, newBuffer, 0, (int)value );
  244. } else {
  245. // expand
  246. Array.Copy( internalBuffer, 0, newBuffer, 0, internalBuffer.Length );
  247. }
  248. internalBuffer = newBuffer;
  249. capacity = (int)value;
  250. }
  251. public virtual byte[] ToArray() {
  252. if( streamClosed ) {
  253. throw new ArgumentException( "The MemoryStream has been closed" );
  254. }
  255. byte[] outBuffer = new byte[capacity];
  256. Array.Copy( internalBuffer, 0, outBuffer, 0, capacity);
  257. return outBuffer;
  258. }
  259. // LAMESPEC: !! It says that "offset" is "offset in buffer at which
  260. // to begin writing", I presume this should be "offset in buffer at which
  261. // to begin reading"
  262. public override void Write( byte[] buffer, int offset, int count ) {
  263. if( buffer == null ) {
  264. throw new ArgumentNullException();
  265. } else if( !canWrite ) {
  266. throw new NotSupportedException();
  267. } else if( buffer.Length - offset < count ) {
  268. throw new ArgumentException();
  269. } else if( offset < 0 || count < 0 ) {
  270. throw new ArgumentOutOfRangeException();
  271. } else if( streamClosed ) {
  272. throw new ObjectDisposedException( "MemoryStream" );
  273. }
  274. if( position + count > capacity ) {
  275. if( expandable ) {
  276. // expand the buffer
  277. SetLength( position + count );
  278. } else {
  279. // only write as many bytes as will fit
  280. count = (int)((long)capacity - position);
  281. }
  282. }
  283. // internal buffer may not be allocated all the way up to capacity
  284. // count will already be limited to capacity above if non-expandable
  285. if( position + count >= internalBuffer.Length )
  286. SetLength( position + count );
  287. Array.Copy( buffer, offset, internalBuffer, (int)position, count );
  288. position += count;
  289. }
  290. public override void WriteByte( byte value ) {
  291. if ( streamClosed )
  292. throw new ObjectDisposedException( "MemoryStream" );
  293. else if( !canWrite || (position >= capacity && !expandable))
  294. throw new NotSupportedException();
  295. if ( position >= capacity )
  296. SetLength( capacity + 1 );
  297. if( position >= internalBuffer.Length )
  298. SetLength ( position + 1 );
  299. internalBuffer[ position++ ] = value;
  300. }
  301. public virtual void WriteTo( Stream stream ) {
  302. if( stream == null ) {
  303. throw new ArgumentNullException();
  304. }
  305. stream.Write( internalBuffer, 0, internalBuffer.Length );
  306. }
  307. }
  308. }