2 // System.IO.MemoryStream
\r
4 // Author: Marcin Szczepanski (marcins@zipworld.com.au)
\r
5 // Patrik Torstensson
\r
7 // TODO: Clarify some of the lamespec issues
\r
10 namespace System.IO {
\r
12 public class MemoryStream : Stream {
\r
13 private bool canRead;
\r
14 private bool canSeek;
\r
15 private bool canWrite;
\r
17 private bool allowGetBuffer;
\r
19 private int capacity;
\r
21 private byte[] internalBuffer;
\r
23 private int initialLength;
\r
24 private bool expandable;
\r
26 private bool streamClosed = false;
\r
28 private long position = 0;
\r
30 public MemoryStream() {
\r
37 internalBuffer = new byte[0];
\r
39 allowGetBuffer = true;
\r
43 public MemoryStream( byte[] buffer ) {
\r
44 InternalConstructor( buffer, 0, buffer.Length, true, false );
\r
47 public MemoryStream( int capacity ) {
\r
53 this.capacity = capacity;
\r
55 internalBuffer = new byte [capacity];
\r
58 allowGetBuffer = true;
\r
61 public MemoryStream( byte[] buffer, bool writeable ) {
\r
62 if( buffer == null ) {
\r
63 throw new ArgumentNullException();
\r
66 InternalConstructor( buffer, 0, buffer.Length, writeable, true );
\r
70 public MemoryStream( byte[] buffer, int index, int count ) {
\r
71 if( buffer == null ) {
\r
72 throw new ArgumentNullException();
\r
75 InternalConstructor( buffer, index, count, true, false );
\r
78 public MemoryStream( byte[] buffer, int index, int count, bool writeable ) {
\r
80 if( buffer == null ) {
\r
81 throw new ArgumentNullException();
\r
84 InternalConstructor( buffer, index, count, writeable, true );
\r
87 public MemoryStream( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {
\r
88 InternalConstructor( buffer, index, count, writeable, publicallyVisible );
\r
91 private void InternalConstructor( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {
\r
93 if( buffer == null ) {
\r
94 throw new ArgumentNullException();
\r
95 } else if ( index < 0 || count < 0 ) {
\r
96 throw new ArgumentOutOfRangeException();
\r
97 } else if ( buffer.Length - index < count ) {
\r
98 throw new ArgumentException();
\r
101 // LAMESPEC: The spec says to throw an UnauthorisedAccessException if
\r
102 // publicallyVisibile is fale?! Doesn't that defy the point of having
\r
103 // it there in the first place. I'll leave it out for now.
\r
107 canWrite = writeable;
\r
109 initialLength = count;
\r
111 internalBuffer = new byte[ count ];
\r
114 Buffer.BlockCopyInternal (buffer, index, internalBuffer, 0, count);
\r
116 allowGetBuffer = publicallyVisible;
\r
117 expandable = false;
\r
120 public override bool CanRead {
\r
122 return this.canRead;
\r
126 public override bool CanSeek {
\r
128 return this.canSeek;
\r
132 public override bool CanWrite {
\r
134 return this.canWrite;
\r
138 public virtual int Capacity {
\r
140 return this.capacity;
\r
144 if( value < 0 || value < capacity ) {
\r
145 throw new ArgumentOutOfRangeException("value",
\r
146 "New capacity cannot be negative or less than the current capacity" );
\r
147 } else if( !expandable ) {
\r
148 throw new NotSupportedException( "Cannot expand this MemoryStream" );
\r
151 byte[] newBuffer = new byte[ value ];
\r
152 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, capacity);
\r
157 public override long Length {
\r
159 // LAMESPEC: The spec says to throw an IOException if the
\r
160 // stream is closed and an ObjectDisposedException if
\r
161 // "methods were called after the stream was closed". What
\r
162 // is the difference?
\r
164 if( streamClosed ) {
\r
165 throw new IOException( "MemoryStream is closed" );
\r
168 return internalBuffer.Length;
\r
172 public override long Position {
\r
174 if( streamClosed ) {
\r
175 throw new IOException( "MemoryStream is closed" );
\r
183 if( position < 0 ) {
\r
184 throw new ArgumentOutOfRangeException ("value", "Position cannot be negative" );
\r
185 } else if (position > Int32.MaxValue) {
\r
186 throw new ArgumentOutOfRangeException ("value",
\r
187 "Length must be non-negative and less than 2^31 - 1 - origin");
\r
188 } else if( streamClosed ) {
\r
189 throw new IOException( "MemoryStream is closed" );
\r
196 public override void Close() {
\r
197 if( streamClosed ) {
\r
201 streamClosed = true;
\r
202 internalBuffer = null;
\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( buffer.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 Buffer.BlockCopyInternal (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",
\r
292 "Offset cannot be greater than length of MemoryStream" );
\r
295 position = refPoint + offset;
\r
301 public override void SetLength( long value ) {
\r
302 if( streamClosed ) {
\r
303 throw new ObjectDisposedException( "MemoryStream" );
\r
304 } else if( !expandable && value > capacity ) {
\r
305 throw new NotSupportedException( "Expanding this MemoryStream is not supported" );
\r
306 } else if( !canWrite ) {
\r
307 throw new IOException( "Cannot write to this MemoryStream" );
\r
308 } else if( value < 0 ) {
\r
310 // LAMESPEC: AGAIN! It says to throw this exception if value is
\r
311 // greater than "the maximum length of the MemoryStream". I haven't
\r
312 // seen anywhere mention what the maximum length of a MemoryStream is and
\r
313 // since we're this far this memory stream is expandable.
\r
315 throw new ArgumentOutOfRangeException();
\r
319 newBuffer = new byte[ value ];
\r
321 if (value < internalBuffer.Length) {
\r
323 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, (int)value );
\r
326 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, internalBuffer.Length );
\r
328 internalBuffer = newBuffer;
\r
329 capacity = (int)value;
\r
334 public virtual byte[] ToArray() {
\r
335 byte[] outBuffer = new byte[capacity];
\r
337 Buffer.BlockCopyInternal (internalBuffer, 0, outBuffer, 0, capacity);
\r
341 public override void Write( byte[] buffer, int offset, int count ) {
\r
342 if( buffer == null ) {
\r
343 throw new ArgumentNullException();
\r
344 } else if( !canWrite ) {
\r
345 throw new NotSupportedException();
\r
346 } else if( buffer.Length - offset < count ) {
\r
347 throw new ArgumentException();
\r
348 } else if( offset < 0 || count < 0 ) {
\r
349 throw new ArgumentOutOfRangeException();
\r
350 } else if( streamClosed ) {
\r
351 throw new ObjectDisposedException( "MemoryStream" );
\r
354 if( position + count > capacity ) {
\r
356 // expand the buffer
\r
357 SetLength( position + count );
\r
359 // only write as many bytes as will fit
\r
360 count = (int)((long)capacity - position);
\r
364 // internal buffer may not be allocated all the way up to capacity
\r
365 // count will already be limited to capacity above if non-expandable
\r
366 if( position + count >= internalBuffer.Length )
\r
367 SetLength( position + count );
\r
369 Buffer.BlockCopyInternal (buffer, offset, internalBuffer, (int)position, count);
\r
374 public override void WriteByte( byte value ) {
\r
375 if ( streamClosed )
\r
376 throw new ObjectDisposedException( "MemoryStream" );
\r
377 else if( !canWrite || (position >= capacity && !expandable))
\r
378 throw new NotSupportedException();
\r
380 if( position >= internalBuffer.Length )
\r
381 SetLength ( position + 1 );
\r
383 internalBuffer[ position++ ] = value;
\r
387 public virtual void WriteTo( Stream stream ) {
\r
388 if( stream == null ) {
\r
389 throw new ArgumentNullException();
\r
392 stream.Write( internalBuffer, 0, internalBuffer.Length );
\r