63de28dbe0545dc17b0626ad1ca17b0cb01cbef5
[mono.git] / mcs / class / corlib / System.IO / MemoryStream.cs
1 //\r
2 // System.IO.MemoryStream \r
3 //\r
4 // Author:      Marcin Szczepanski (marcins@zipworld.com.au)\r
5 //              Patrik Torstensson\r
6 //\r
7 // TODO: Clarify some of the lamespec issues\r
8 //\r
9 \r
10 namespace System.IO {\r
11         [Serializable]\r
12         public class MemoryStream : Stream {\r
13                 private bool canRead;\r
14                 private bool canSeek;\r
15                 private bool canWrite;\r
16                 \r
17                 private bool allowGetBuffer;\r
18 \r
19                 private int capacity;\r
20 \r
21                 private byte[] internalBuffer;\r
22 \r
23                 private int initialLength;\r
24                 private bool expandable;\r
25 \r
26                 private bool streamClosed = false;\r
27 \r
28                 private long position = 0;\r
29                 \r
30                 public MemoryStream() {\r
31                         canRead = true;\r
32                         canSeek = true;\r
33                         canWrite = true;\r
34 \r
35                         capacity = 0;\r
36 \r
37                         internalBuffer = new byte[0];   \r
38 \r
39                         allowGetBuffer = true;\r
40                         expandable = true;\r
41                 }\r
42 \r
43                 public MemoryStream( byte[] buffer ) {\r
44                         InternalConstructor( buffer, 0, buffer.Length, true, false );                        \r
45                 }\r
46 \r
47                 public MemoryStream( int capacity ) {\r
48                         \r
49                         canRead = true;\r
50                         canSeek = true;\r
51                         canWrite = true;\r
52                         \r
53                         this.capacity = capacity;\r
54                         initialLength = 0;\r
55                         internalBuffer = new byte [capacity];\r
56 \r
57                         expandable = true;\r
58                         allowGetBuffer = true;\r
59                 }\r
60 \r
61                 public MemoryStream( byte[] buffer, bool writeable ) {\r
62                         if( buffer == null ) {\r
63                                 throw new ArgumentNullException();\r
64                         }\r
65 \r
66                         InternalConstructor( buffer, 0, buffer.Length, writeable, true );\r
67 \r
68                 }\r
69  \r
70                 public MemoryStream( byte[] buffer, int index, int count ) { \r
71                         if( buffer == null ) {\r
72                                 throw new ArgumentNullException();\r
73                         }\r
74                         \r
75                         InternalConstructor( buffer, index, count, true, false );                                        \r
76                 }\r
77                 \r
78                 public MemoryStream( byte[] buffer, int index, int count, bool writeable ) { \r
79                         \r
80                         if( buffer == null ) {\r
81                                 throw new ArgumentNullException();\r
82                         }\r
83                         \r
84                         InternalConstructor( buffer, index, count, writeable, true );        \r
85                 }\r
86 \r
87                 public MemoryStream( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {\r
88                         InternalConstructor( buffer, index, count, writeable, publicallyVisible );\r
89                 }\r
90 \r
91                 private void InternalConstructor( byte[] buffer, int index, int count, bool writeable, bool publicallyVisible ) {\r
92                 \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
99                         }\r
100 \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
104                         \r
105                         canRead = true;\r
106                         canSeek = true;\r
107                         canWrite = writeable;\r
108 \r
109                         initialLength = count;\r
110 \r
111                         internalBuffer = new byte[ count ];\r
112                         capacity = count;\r
113 \r
114                         Buffer.BlockCopyInternal (buffer, index, internalBuffer, 0, count);\r
115 \r
116                         allowGetBuffer = publicallyVisible;\r
117                         expandable = false;                \r
118                  }\r
119 \r
120                  public override bool CanRead {\r
121                         get {\r
122                                 return this.canRead;\r
123                         }\r
124                 }\r
125 \r
126                 public override bool CanSeek {\r
127                         get {\r
128                                 return this.canSeek;\r
129                         }\r
130                 }\r
131 \r
132                 public override bool CanWrite {\r
133                         get {\r
134                                 return this.canWrite;\r
135                         }\r
136                 }\r
137 \r
138                 public virtual int Capacity {\r
139                         get {\r
140                                 return this.capacity;\r
141                         }\r
142 \r
143                         set {\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
149                                 }\r
150 \r
151                                 byte[] newBuffer = new byte[ value ];\r
152                                 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, capacity);\r
153                                 capacity = value;\r
154                         }\r
155                 }\r
156 \r
157                 public override long Length {\r
158                         get {\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
163 \r
164                                 if( streamClosed ) {\r
165                                         throw new IOException( "MemoryStream is closed" );\r
166                                 }\r
167                                 \r
168                                 return internalBuffer.Length;\r
169                         }\r
170                 }\r
171 \r
172                 public override long Position {\r
173                         get {\r
174                                 if( streamClosed ) {\r
175                                         throw new IOException( "MemoryStream is closed" );\r
176                                 }\r
177 \r
178                                 return position;\r
179                         }\r
180 \r
181                         set {\r
182 \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
190                                 }\r
191                                 \r
192                                 position = value;\r
193                         }\r
194                 }\r
195                 \r
196                 public override void Close() {\r
197                         if( streamClosed ) {\r
198                                 return;\r
199                         }\r
200 \r
201                         streamClosed = true;\r
202                         internalBuffer = null;\r
203                 }\r
204 \r
205                 public override void Flush() { }\r
206 \r
207                 public virtual byte[] GetBuffer() {\r
208                         if( !allowGetBuffer ) {\r
209                                 throw new UnauthorizedAccessException();\r
210                         }  \r
211 \r
212                         return internalBuffer;\r
213                 }\r
214 \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
224                         }\r
225 \r
226                         long ReadTo;\r
227 \r
228                         if( position + count > internalBuffer.Length ) {\r
229                                 ReadTo = internalBuffer.Length;\r
230                         } else {\r
231                                 ReadTo = position + (long)count;\r
232                         }\r
233 \r
234                         Buffer.BlockCopyInternal (internalBuffer, (int)position, buffer, offset, (int)(ReadTo - position) );\r
235 \r
236                         int bytesRead = (int)(ReadTo - position);\r
237 \r
238                         position = ReadTo;\r
239 \r
240                         return bytesRead;\r
241                 }\r
242 \r
243                 public override int ReadByte( ) {\r
244                         if( streamClosed ) {\r
245                                 throw new ObjectDisposedException( "MemoryStream" );\r
246                         }\r
247 \r
248 \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
251                         //\r
252 \r
253                         if( position >= internalBuffer.Length ) {\r
254                                 return -1;\r
255                         } else {\r
256                                 return internalBuffer[ position++ ];\r
257                         }\r
258                 }\r
259                  \r
260                 public override long Seek( long offset, SeekOrigin loc ) { \r
261                         long refPoint;\r
262 \r
263                         if( streamClosed ) {\r
264                                 throw new ObjectDisposedException( "MemoryStream" );\r
265                         }\r
266 \r
267                         switch( loc ) {\r
268                                 case SeekOrigin.Begin:\r
269                                         refPoint = 0;\r
270                                         break;\r
271                                 case SeekOrigin.Current:\r
272                                         refPoint = position;\r
273                                         break;\r
274                                 case SeekOrigin.End:\r
275                                         refPoint = internalBuffer.Length;\r
276                                         break;\r
277                                 default:\r
278                                         throw new ArgumentException( "Invalid SeekOrigin" );\r
279                         }\r
280 \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
287 \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
293                         }\r
294 \r
295                         position = refPoint + offset;\r
296 \r
297                         return position;\r
298                 }\r
299                 \r
300                 \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
309 \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
314 \r
315                                 throw new ArgumentOutOfRangeException();\r
316                         } \r
317 \r
318                         byte[] newBuffer;\r
319                         newBuffer = new byte[ value ];\r
320                         \r
321                         if (value < internalBuffer.Length) {\r
322                                 // truncate\r
323                                 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, (int)value );\r
324                         } else {\r
325                                 // expand\r
326                                 Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, internalBuffer.Length );\r
327                         }\r
328                         internalBuffer = newBuffer;\r
329                         capacity = (int)value;\r
330 \r
331                 }\r
332                 \r
333                 \r
334                 public virtual byte[] ToArray() { \r
335                         byte[] outBuffer = new byte[capacity];\r
336                         \r
337                         Buffer.BlockCopyInternal (internalBuffer, 0, outBuffer, 0, capacity);\r
338                         return outBuffer; \r
339                 }\r
340 \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
352                         }\r
353 \r
354                         if( position + count > capacity ) {\r
355                                 if( expandable ) {\r
356                                         // expand the buffer\r
357                                         SetLength( position + count );                       \r
358                                 } else {\r
359                                         // only write as many bytes as will fit\r
360                                         count = (int)((long)capacity - position);\r
361                                 }\r
362                         }\r
363 \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
368 \r
369                         Buffer.BlockCopyInternal (buffer, offset, internalBuffer, (int)position, count);\r
370                         position += count;\r
371                 }\r
372 \r
373 \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
379 \r
380                         if( position >= internalBuffer.Length )\r
381                                 SetLength ( position + 1 );\r
382 \r
383                         internalBuffer[ position++ ] = value;\r
384                 }\r
385                 \r
386 \r
387                 public virtual void WriteTo( Stream stream ) { \r
388                         if( stream == null ) {\r
389                                 throw new ArgumentNullException();\r
390                         }\r
391 \r
392                         stream.Write( internalBuffer, 0, internalBuffer.Length );\r
393                 \r
394                 }\r
395                                \r
396                        \r
397         }               \r
398 \r
399 \r
400 }                      \r