2 // System.IO.MemoryStream
4 // Authors: Marcin Szczepanski (marcins@zipworld.com.au)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
12 using System.Runtime.InteropServices;
17 public class MemoryStream : Stream
23 byte [] internalBuffer;
29 public MemoryStream () : this (0)
33 public MemoryStream (int capacity)
36 throw new ArgumentOutOfRangeException ("capacity");
40 this.capacity = capacity;
41 internalBuffer = new byte [capacity];
44 allowGetBuffer = true;
47 public MemoryStream (byte [] buffer)
50 throw new ArgumentNullException ("buffer");
52 InternalConstructor (buffer, 0, buffer.Length, true, false);
55 public MemoryStream (byte [] buffer, bool writeable)
58 throw new ArgumentNullException ("buffer");
60 InternalConstructor (buffer, 0, buffer.Length, writeable, false);
63 public MemoryStream (byte [] buffer, int index, int count)
65 InternalConstructor (buffer, index, count, true, false);
68 public MemoryStream (byte [] buffer, int index, int count, bool writeable)
70 InternalConstructor (buffer, index, count, writeable, false);
73 public MemoryStream (byte [] buffer, int index, int count, bool writeable, bool publicallyVisible)
75 InternalConstructor (buffer, index, count, writeable, publicallyVisible);
78 void InternalConstructor (byte [] buffer, int index, int count, bool writeable, bool publicallyVisible)
81 throw new ArgumentNullException ("buffer");
83 if (index < 0 || count < 0)
84 throw new ArgumentOutOfRangeException ("index or count is less than 0.");
86 if (buffer.Length - index < count)
87 throw new ArgumentException ("index+count",
88 "The size of the buffer is less than index + count.");
92 internalBuffer = buffer;
93 capacity = count + index;
98 allowGetBuffer = publicallyVisible;
102 void CheckIfClosedThrowDisposed ()
105 throw new ObjectDisposedException ("MemoryStream");
108 void CheckIfClosedThrowIO ()
111 throw new IOException ("MemoryStream is closed");
114 public override bool CanRead {
115 get { return !streamClosed; }
118 public override bool CanSeek {
119 get { return !streamClosed; }
122 public override bool CanWrite {
123 get { return (!streamClosed && canWrite); }
126 public virtual int Capacity {
128 CheckIfClosedThrowDisposed ();
129 return capacity - initialIndex;
133 CheckIfClosedThrowDisposed ();
134 if (value == capacity)
135 return; // LAMENESS: see MemoryStreamTest.ConstructorFive
138 throw new NotSupportedException ("Cannot expand this MemoryStream");
140 if (value < 0 || value < length)
141 throw new ArgumentOutOfRangeException ("value",
142 "New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
144 byte [] newBuffer = null;
146 newBuffer = new byte [value];
147 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, length);
150 internalBuffer = newBuffer; // It's null when capacity is set to 0
155 public override long Length {
157 // LAMESPEC: The spec says to throw an IOException if the
158 // stream is closed and an ObjectDisposedException if
159 // "methods were called after the stream was closed". What
160 // is the difference?
162 CheckIfClosedThrowIO ();
164 // This is ok for MemoryStreamTest.ConstructorFive
165 return length - initialIndex;
169 public override long Position {
171 CheckIfClosedThrowIO ();
172 return position - initialIndex;
176 CheckIfClosedThrowIO ();
178 throw new ArgumentOutOfRangeException ("value",
179 "Position cannot be negative" );
181 if (value > Int32.MaxValue)
182 throw new ArgumentOutOfRangeException ("value",
183 "Position must be non-negative and less than 2^31 - 1 - origin");
185 position = initialIndex + (int) value;
189 public override void Close ()
195 public override void Flush ()
200 public virtual byte [] GetBuffer ()
203 throw new UnauthorizedAccessException ();
205 return internalBuffer;
208 public override int Read ([In,Out] byte [] buffer, int offset, int count)
210 CheckIfClosedThrowDisposed ();
213 throw new ArgumentNullException ("buffer");
215 if (offset < 0 || count < 0)
216 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
218 if (buffer.Length - offset < count )
219 throw new ArgumentException ("offset+count",
220 "The size of the buffer is less than offset + count.");
222 if (position >= length || count == 0)
225 if (position > length - count)
226 count = length - position;
228 Buffer.BlockCopyInternal (internalBuffer, position, buffer, offset, count);
233 public override int ReadByte ()
235 CheckIfClosedThrowDisposed ();
236 if (position >= length)
239 return internalBuffer [position++];
242 public override long Seek (long offset, SeekOrigin loc)
244 CheckIfClosedThrowDisposed ();
246 // It's funny that they don't throw this exception for < Int32.MinValue
247 if (offset > (long) Int32.MaxValue)
248 throw new ArgumentOutOfRangeException ("Offset out of range. " + offset);
252 case SeekOrigin.Begin:
254 throw new IOException ("Attempted to seek before start of MemoryStream.");
255 refPoint = initialIndex;
257 case SeekOrigin.Current:
264 throw new ArgumentException ("loc", "Invalid SeekOrigin");
267 // LAMESPEC: My goodness, how may LAMESPECs are there in this
268 // class! :) In the spec for the Position property it's stated
269 // "The position must not be more than one byte beyond the end of the stream."
270 // In the spec for seek it says "Seeking to any location beyond the length of the
271 // stream is supported." That's a contradiction i'd say.
272 // I guess seek can go anywhere but if you use position it may get moved back.
274 refPoint += (int) offset;
275 if (refPoint < initialIndex)
276 throw new IOException ("Attempted to seek before start of MemoryStream.");
282 int CalculateNewCapacity (int minimum)
285 minimum = 256; // See GetBufferTwo test
287 if (minimum < capacity * 2)
288 minimum = capacity * 2;
293 public override void SetLength (long value)
295 if (!expandable && value > capacity)
296 throw new NotSupportedException ("Expanding this MemoryStream is not supported");
298 CheckIfClosedThrowDisposed ();
301 throw new IOException ("Cannot write to this MemoryStream");
303 // LAMESPEC: AGAIN! It says to throw this exception if value is
304 // greater than "the maximum length of the MemoryStream". I haven't
305 // seen anywhere mention what the maximum length of a MemoryStream is and
306 // since we're this far this memory stream is expandable.
307 if (value < 0 || (value + initialIndex) > (long) Int32.MaxValue)
308 throw new ArgumentOutOfRangeException ();
310 int newSize = (int) value + initialIndex;
311 if (newSize > capacity) {
312 Capacity = CalculateNewCapacity (newSize);
313 } else if (newSize > length) {
314 for (int i = newSize; i < length; i++)
315 Buffer.SetByte (internalBuffer, i, 0);
319 if (position > length)
323 public virtual byte [] ToArray ()
325 int l = length - initialIndex;
326 byte[] outBuffer = new byte [l];
328 Buffer.BlockCopyInternal (internalBuffer, initialIndex, outBuffer, 0, l);
332 public override void Write (byte [] buffer, int offset, int count)
334 CheckIfClosedThrowDisposed ();
337 throw new NotSupportedException ("Cannot write to this stream.");
340 throw new ArgumentNullException ("buffer");
342 if (offset < 0 || count < 0)
343 throw new ArgumentOutOfRangeException ();
345 if (buffer.Length - offset < count)
346 throw new ArgumentException ("offset+count",
347 "The size of the buffer is less than offset + count.");
349 if (position + count > capacity)
350 Capacity = CalculateNewCapacity (position + count);
352 Buffer.BlockCopyInternal (buffer, offset, internalBuffer, position, count);
354 if (position >= length)
358 public override void WriteByte (byte value)
360 CheckIfClosedThrowDisposed ();
362 throw new NotSupportedException ("Cannot write to this stream.");
364 if (position >= capacity)
365 Capacity = CalculateNewCapacity (position + 1);
367 if (position >= length)
368 length = position + 1;
370 internalBuffer [position++] = value;
373 public virtual void WriteTo (Stream stream)
375 CheckIfClosedThrowDisposed ();
378 throw new ArgumentNullException ("stream");
380 stream.Write (internalBuffer, initialIndex, length - initialIndex);