2 // System.IO.MemoryStream
\r
4 // Author: Marcin Szczepanski (marcins@zipworld.com.au)
\r
6 // TODO: Clarify some of the lamespec issues
\r
9 namespace System.IO {
\r
10 public class MemoryStream : Stream {
\r
11 private bool canRead;
\r
12 private bool canSeek;
\r
13 private bool canWrite;
\r
15 private bool allowGetBuffer;
\r
17 private int capacity;
\r
19 private byte[] internalBuffer;
\r
21 private int initialLength;
\r
22 private bool expandable;
\r
24 private bool streamClosed = false;
\r
26 private long position = 0;
\r
28 public MemoryStream() {
\r
35 internalBuffer = new byte[0];
\r
37 allowGetBuffer = true;
\r
41 public MemoryStream( byte[] buffer ) {
\r
42 InternalConstructor( buffer, 0, buffer.Length, true, false );
\r
45 public MemoryStream( int capacity ) {
\r
51 this.capacity = capacity;
\r
52 initialLength = capacity;
\r
53 internalBuffer = new byte[ capacity ];
\r
56 allowGetBuffer = true;
\r
59 public MemoryStream( byte[] buffer, bool writeable ) {
\r
60 if( buffer == null ) {
\r
61 throw new ArgumentNullException();
\r
64 InternalConstructor( buffer, 0, buffer.Length, writeable, true );
\r
68 public MemoryStream( byte[] buffer, int index, int count ) {
\r
69 if( buffer == null ) {
\r
70 throw new ArgumentNullException();
\r
73 InternalConstructor( buffer, index, count, true, false );
\r
76 public MemoryStream( byte[] buffer, int index, int count, bool writeable ) {
\r
78 if( buffer == null ) {
\r
79 throw new ArgumentNullException();
\r
82 InternalConstructor( buffer, index, count, writeable, true );
\r
85 public MemoryStream( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {
\r
86 InternalConstructor( buffer, index, count, writeable, publicallyVisible );
\r
89 private void InternalConstructor( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {
\r
91 if( buffer == null ) {
\r
92 throw new ArgumentNullException();
\r
93 } else if ( index < 0 || count < 0 ) {
\r
94 throw new ArgumentOutOfRangeException();
\r
95 } else if ( buffer.Length - index < count ) {
\r
96 throw new ArgumentException();
\r
99 // LAMESPEC: The spec says to throw an UnauthorisedAccessException if
\r
100 // publicallyVisibile is fale?! Doesn't that defy the point of having
\r
101 // it there in the first place. I'll leave it out for now.
\r
105 canWrite = writeable;
\r
107 initialLength = count;
\r
109 internalBuffer = new byte[ count ];
\r
112 Array.Copy( buffer, index, internalBuffer, 0, count );
\r
114 allowGetBuffer = publicallyVisible;
\r
115 expandable = false;
\r
118 public override bool CanRead {
\r
120 return this.canRead;
\r
124 public override bool CanSeek {
\r
126 return this.canSeek;
\r
130 public override bool CanWrite {
\r
132 return this.canWrite;
\r
136 public virtual int Capacity {
\r
138 return this.capacity;
\r
142 if( value < 0 || value < capacity ) {
\r
143 throw new ArgumentOutOfRangeException( "New capacity cannot be negative or less than the current capacity" );
\r
144 } else if( !expandable ) {
\r
145 throw new NotSupportedException( "Cannot expand this MemoryStream" );
\r
148 byte[] newBuffer = new byte[ value ];
\r
149 Array.Copy( internalBuffer, 0, newBuffer, 0, capacity );
\r
154 public override long Length {
\r
156 // LAMESPEC: The spec says to throw an IOException if the
\r
157 // stream is closed and an ObjectDisposedException if
\r
158 // "methods were called after the stream was closed". What
\r
159 // is the difference?
\r
161 if( streamClosed ) {
\r
162 throw new IOException( "MemoryStream is closed" );
\r
165 return internalBuffer.Length;
\r
169 public override long Position {
\r
171 if( streamClosed ) {
\r
172 throw new IOException( "MemoryStream is closed" );
\r
180 if( position < 0 ) {
\r
181 throw new ArgumentOutOfRangeException( "Position cannot be negative" );
\r
182 } else if( streamClosed ) {
\r
183 throw new IOException( "MemoryStream is closed" );
\r
188 if( position > internalBuffer.Length + 1 ) {
\r
189 position = internalBuffer.Length + 1;
\r
194 public override void Close() {
\r
195 if( streamClosed ) {
\r
196 throw new IOException( "MemoryStream already closed" );
\r
199 streamClosed = true;
\r
203 protected override void Dispose( bool disposing ) { }
\r
205 public override void Flush() { }
\r
207 public virtual byte[] GetBuffer() {
\r
208 if( !allowGetBuffer ) {
\r
209 throw new UnauthorizedAccessException();
\r
212 return internalBuffer;
\r
215 public override int Read( byte[] buffer, int offset, int count ) {
\r
216 if( buffer == null ) {
\r
217 throw new ArgumentNullException();
\r
218 } else if( offset < 0 || count < 0 ) {
\r
219 throw new ArgumentOutOfRangeException();
\r
220 } else if( internalBuffer.Length - offset < count ) {
\r
221 throw new ArgumentException();
\r
222 } else if ( streamClosed ) {
\r
223 throw new ObjectDisposedException( "MemoryStream" );
\r
228 if( position + count > internalBuffer.Length ) {
\r
229 ReadTo = internalBuffer.Length;
\r
231 ReadTo = position + (long)count;
\r
234 Array.Copy( internalBuffer, (int)position, buffer, offset, (int)(ReadTo - position) );
\r
236 int bytesRead = (int)(ReadTo - position);
\r
243 public override int ReadByte( ) {
\r
244 if( streamClosed ) {
\r
245 throw new ObjectDisposedException( "MemoryStream" );
\r
249 // LAMESPEC: What happens if we're at the end of the stream? It's unspecified in the
\r
250 // docs but tests against the MS impl. show it returns -1
\r
253 if( position >= internalBuffer.Length ) {
\r
256 return internalBuffer[ position++ ];
\r
260 public override long Seek( long offset, SeekOrigin loc ) {
\r
263 if( streamClosed ) {
\r
264 throw new ObjectDisposedException( "MemoryStream" );
\r
268 case SeekOrigin.Begin:
\r
271 case SeekOrigin.Current:
\r
272 refPoint = position;
\r
274 case SeekOrigin.End:
\r
275 refPoint = internalBuffer.Length;
\r
278 throw new ArgumentException( "Invalid SeekOrigin" );
\r
281 // LAMESPEC: My goodness, how may LAMESPECs are there in this
\r
282 // class! :) In the spec for the Position property it's stated
\r
283 // "The position must not be more than one byte beyond the end of the stream."
\r
284 // In the spec for seek it says "Seeking to any location beyond the length of the
\r
285 // stream is supported." That's a contradiction i'd say.
\r
286 // I guess seek can go anywhere but if you use position it may get moved back.
\r
288 if( refPoint + offset < 0 ) {
\r
289 throw new IOException( "Attempted to seek before start of MemoryStream" );
\r
290 } else if( offset > internalBuffer.Length ) {
\r
291 throw new ArgumentOutOfRangeException( "Offset cannot be greater than length of MemoryStream" );
\r
294 position = refPoint + offset;
\r
300 public override void SetLength( long value ) {
\r
301 if( streamClosed ) {
\r
302 throw new ObjectDisposedException( "MemoryStream" );
\r
303 } else if( !expandable && value > capacity ) {
\r
304 throw new NotSupportedException( "Expanding this MemoryStream is not supported" );
\r
305 } else if( !canWrite ) {
\r
306 throw new IOException( "Cannot write to this MemoryStream" );
\r
307 } else if( value < 0 ) {
\r
309 // LAMESPEC: AGAIN! It says to throw this exception if value is
\r
310 // greater than "the maximum length of the MemoryStream". I haven't
\r
311 // seen anywhere mention what the maximum length of a MemoryStream is and
\r
312 // since we're this far this memory stream is expandable.
\r
314 throw new ArgumentOutOfRangeException();
\r
318 newBuffer = new byte[ value ];
\r
320 if( value < capacity ) {
\r
322 Array.Copy( internalBuffer, 0, newBuffer, 0, (int)value );
\r
325 Array.Copy( internalBuffer, 0, newBuffer, 0, internalBuffer.Length );
\r
327 internalBuffer = newBuffer;
\r
328 capacity = (int)value;
\r
333 public virtual byte[] ToArray() {
\r
335 if( streamClosed ) {
\r
336 throw new ArgumentException( "The MemoryStream has been closed" );
\r
339 byte[] outBuffer = new byte[capacity];
\r
340 Array.Copy( internalBuffer, 0, outBuffer, 0, capacity);
\r
344 // LAMESPEC: !! It says that "offset" is "offset in buffer at which
\r
345 // to begin writing", I presume this should be "offset in buffer at which
\r
346 // to begin reading"
\r
348 public override void Write( byte[] buffer, int offset, int count ) {
\r
349 if( buffer == null ) {
\r
350 throw new ArgumentNullException();
\r
351 } else if( !canWrite ) {
\r
352 throw new NotSupportedException();
\r
353 } else if( buffer.Length - offset < count ) {
\r
354 throw new ArgumentException();
\r
355 } else if( offset < 0 || count < 0 ) {
\r
356 throw new ArgumentOutOfRangeException();
\r
357 } else if( streamClosed ) {
\r
358 throw new ObjectDisposedException( "MemoryStream" );
\r
361 if( position + count > capacity ) {
\r
363 // expand the buffer
\r
364 SetLength( position + count );
\r
366 // only write as many bytes as will fit
\r
367 count = (int)((long)capacity - position);
\r
371 Array.Copy( buffer, offset, internalBuffer, (int)position, count );
\r
377 public override void WriteByte( byte value ) {
\r
378 if( streamClosed ) {
\r
379 throw new ObjectDisposedException( "MemoryStream" );
\r
382 if( position >= capacity ) {
\r
383 SetLength( capacity + 1 );
\r
386 internalBuffer[ position++ ] = value;
\r
390 public virtual void WriteTo( Stream stream ) {
\r
391 if( stream == null ) {
\r
392 throw new ArgumentNullException();
\r
395 stream.Write( internalBuffer, 0, internalBuffer.Length );
\r