Wrap always_inline and noinline attributes in compiler checks and use MSVC equivalent.
[mono.git] / mcs / class / corlib / System.IO / MemoryStream.cs
1 //
2 // System.IO.MemoryStream.cs
3 //
4 // Authors:     Marcin Szczepanski (marcins@zipworld.com.au)
5 //              Patrik Torstensson
6 //              Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //              Marek Safar (marek.safar@gmail.com)
8 //
9 // (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
10 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Globalization;
35 using System.Runtime.InteropServices;
36 using System.Threading;
37 #if NET_4_5
38 using System.Threading.Tasks;
39 #endif
40
41 namespace System.IO
42 {
43         [Serializable]
44         [ComVisible (true)]
45         [MonoLimitation ("Serialization format not compatible with .NET")]
46         public class MemoryStream : Stream
47         {
48                 bool canWrite;
49                 bool allowGetBuffer;
50                 int capacity;
51                 int length;
52                 byte [] internalBuffer;
53                 int initialIndex;
54                 bool expandable;
55                 bool streamClosed;
56                 int position;
57                 int dirty_bytes;
58 #if NET_4_5
59                 [NonSerialized]
60                 Task<int> read_task;
61 #endif
62
63                 public MemoryStream () : this (0)
64                 {
65                 }
66
67                 public MemoryStream (int capacity)
68                 {
69                         if (capacity < 0)
70                                 throw new ArgumentOutOfRangeException ("capacity");
71
72                         canWrite = true;
73
74                         this.capacity = capacity;
75                         internalBuffer = new byte [capacity];
76
77                         expandable = true;
78                         allowGetBuffer = true;
79                 }
80
81                 public MemoryStream (byte [] buffer)
82                 {
83                         if (buffer == null)
84                                 throw new ArgumentNullException ("buffer");
85                         
86                         InternalConstructor (buffer, 0, buffer.Length, true, false);                        
87                 }
88
89                 public MemoryStream (byte [] buffer, bool writable)
90                 {
91                         if (buffer == null)
92                                 throw new ArgumentNullException ("buffer");
93                         
94                         InternalConstructor (buffer, 0, buffer.Length, writable, false);
95                 }
96
97                 public MemoryStream (byte [] buffer, int index, int count)
98                 {
99                         InternalConstructor (buffer, index, count, true, false);
100                 }
101
102                 public MemoryStream (byte [] buffer, int index, int count, bool writable)
103                 {
104                         InternalConstructor (buffer, index, count, writable, false);
105                 }
106
107                 public MemoryStream (byte [] buffer, int index, int count, bool writable, bool publiclyVisible)
108                 {
109                         InternalConstructor (buffer, index, count, writable, publiclyVisible);
110                 }
111
112                 void InternalConstructor (byte [] buffer, int index, int count, bool writable, bool publicallyVisible)
113                 {
114                         if (buffer == null)
115                                 throw new ArgumentNullException ("buffer");
116
117                         if (index < 0 || count < 0)
118                                 throw new ArgumentOutOfRangeException ("index or count is less than 0.");
119
120                         if (buffer.Length - index < count)
121                                 throw new ArgumentException ("index+count", 
122                                                              "The size of the buffer is less than index + count.");
123
124                         canWrite = writable;
125
126                         internalBuffer = buffer;
127                         capacity = count + index;
128                         length = capacity;
129                         position = index;
130                         initialIndex = index;
131
132                         allowGetBuffer = publicallyVisible;
133                         expandable = false;                
134                 }
135
136                 void CheckIfClosedThrowDisposed ()
137                 {
138                         if (streamClosed)
139                                 throw new ObjectDisposedException ("MemoryStream");
140                 }
141                 
142                 public override bool CanRead {
143                         get { return !streamClosed; }
144                 }
145
146                 public override bool CanSeek {
147                         get { return !streamClosed; }
148                 }
149
150                 public override bool CanWrite {
151                         get { return (!streamClosed && canWrite); }
152                 }
153
154                 public virtual int Capacity {
155                         get {
156                                 CheckIfClosedThrowDisposed ();
157                                 return capacity - initialIndex;
158                         }
159
160                         set {
161                                 CheckIfClosedThrowDisposed ();
162
163                                 if (!expandable)
164                                         throw new NotSupportedException ("Cannot expand this MemoryStream");
165
166                                 if (value < 0 || value < length)
167                                         throw new ArgumentOutOfRangeException ("value",
168                                         "New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
169
170                                 if (internalBuffer != null && value == internalBuffer.Length)
171                                         return;
172
173                                 byte [] newBuffer = null;
174                                 if (value != 0) {
175                                         newBuffer = new byte [value];
176                                         if (internalBuffer != null)
177                                                 Buffer.BlockCopy (internalBuffer, 0, newBuffer, 0, length);
178                                 }
179
180                                 dirty_bytes = 0; // discard any dirty area beyond previous length
181                                 internalBuffer = newBuffer; // It's null when capacity is set to 0
182                                 capacity = value;
183                         }
184                 }
185
186                 public override long Length {
187                         get {
188                                 // LAMESPEC: The spec says to throw an IOException if the
189                                 // stream is closed and an ObjectDisposedException if
190                                 // "methods were called after the stream was closed".  What
191                                 // is the difference?
192
193                                 CheckIfClosedThrowDisposed ();
194
195                                 // This is ok for MemoryStreamTest.ConstructorFive
196                                 return length - initialIndex;
197                         }
198                 }
199
200                 public override long Position {
201                         get {
202                                 CheckIfClosedThrowDisposed ();
203                                 return position - initialIndex;
204                         }
205
206                         set {
207                                 CheckIfClosedThrowDisposed ();
208                                 if (value < 0)
209                                         throw new ArgumentOutOfRangeException ("value",
210                                                                 "Position cannot be negative" );
211
212                                 if (value > Int32.MaxValue)
213                                         throw new ArgumentOutOfRangeException ("value",
214                                         "Position must be non-negative and less than 2^31 - 1 - origin");
215
216                                 position = initialIndex + (int) value;
217                         }
218                 }
219
220                 protected override void Dispose (bool disposing)
221                 {
222                         streamClosed = true;
223                         expandable = false;
224                 }
225
226                 public override void Flush ()
227                 {
228                         // Do nothing
229                 }
230
231                 public virtual byte [] GetBuffer ()
232                 {
233                         if (!allowGetBuffer)
234                                 throw new UnauthorizedAccessException ();
235
236                         return internalBuffer;
237                 }
238
239                 public override int Read ([In,Out] byte [] buffer, int offset, int count)
240                 {
241                         if (buffer == null)
242                                 throw new ArgumentNullException ("buffer");
243
244                         if (offset < 0 || count < 0)
245                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
246
247                         if (buffer.Length - offset < count )
248                                 throw new ArgumentException ("offset+count",
249                                                               "The size of the buffer is less than offset + count.");
250
251                         CheckIfClosedThrowDisposed ();
252
253                         if (position >= length || count == 0)
254                                 return 0;
255
256                         if (position > length - count)
257                                 count = length - position;
258
259                         Buffer.BlockCopy (internalBuffer, position, buffer, offset, count);
260                         position += count;
261                         return count;
262                 }
263
264                 public override int ReadByte ()
265                 {
266                         CheckIfClosedThrowDisposed ();
267                         if (position >= length)
268                                 return -1;
269
270                         return internalBuffer [position++];
271                 }
272
273                 public override long Seek (long offset, SeekOrigin loc)
274                 {
275                         CheckIfClosedThrowDisposed ();
276
277                         // It's funny that they don't throw this exception for < Int32.MinValue
278                         if (offset > (long) Int32.MaxValue)
279                                 throw new ArgumentOutOfRangeException ("Offset out of range. " + offset);
280
281                         int refPoint;
282                         switch (loc) {
283                         case SeekOrigin.Begin:
284                                 if (offset < 0)
285                                         throw new IOException ("Attempted to seek before start of MemoryStream.");
286                                 refPoint = initialIndex;
287                                 break;
288                         case SeekOrigin.Current:
289                                 refPoint = position;
290                                 break;
291                         case SeekOrigin.End:
292                                 refPoint = length;
293                                 break;
294                         default:
295                                 throw new ArgumentException ("loc", "Invalid SeekOrigin");
296                         }
297
298                         // LAMESPEC: My goodness, how may LAMESPECs are there in this
299                         // class! :)  In the spec for the Position property it's stated
300                         // "The position must not be more than one byte beyond the end of the stream."
301                         // In the spec for seek it says "Seeking to any location beyond the length of the 
302                         // stream is supported."  That's a contradiction i'd say.
303                         // I guess seek can go anywhere but if you use position it may get moved back.
304
305                         refPoint += (int) offset;
306                         if (refPoint < initialIndex)
307                                 throw new IOException ("Attempted to seek before start of MemoryStream.");
308
309                         position = refPoint;
310                         return position;
311                 }
312
313                 int CalculateNewCapacity (int minimum)
314                 {
315                         if (minimum < 256)
316                                 minimum = 256; // See GetBufferTwo test
317
318                         if (minimum < capacity * 2)
319                                 minimum = capacity * 2;
320
321                         return minimum;
322                 }
323
324                 void Expand (int newSize)
325                 {
326                         // We don't need to take into account the dirty bytes when incrementing the
327                         // Capacity, as changing it will only preserve the valid clear region.
328                         if (newSize > capacity)
329                                 Capacity = CalculateNewCapacity (newSize);
330                         else if (dirty_bytes > 0) {
331                                 Array.Clear (internalBuffer, length, dirty_bytes);
332                                 dirty_bytes = 0;
333                         }
334                 }
335
336                 public override void SetLength (long value)
337                 {
338                         if (!expandable && value > capacity)
339                                 throw new NotSupportedException ("Expanding this MemoryStream is not supported");
340
341                         CheckIfClosedThrowDisposed ();
342
343                         if (!canWrite) {
344                                 throw new NotSupportedException (Locale.GetText 
345                                         ("Cannot write to this MemoryStream"));
346                         }
347
348                         // LAMESPEC: AGAIN! It says to throw this exception if value is
349                         // greater than "the maximum length of the MemoryStream".  I haven't
350                         // seen anywhere mention what the maximum length of a MemoryStream is and
351                         // since we're this far this memory stream is expandable.
352                         if (value < 0 || (value + initialIndex) > (long) Int32.MaxValue)
353                                 throw new ArgumentOutOfRangeException ();
354
355                         int newSize = (int) value + initialIndex;
356
357                         if (newSize > length)
358                                 Expand (newSize);
359                         else if (newSize < length) // Postpone the call to Array.Clear till expand time
360                                 dirty_bytes += length - newSize;
361
362                         length = newSize;
363                         if (position > length)
364                                 position = length;
365                 }
366
367                 public virtual byte [] ToArray ()
368                 {
369                         int l = length - initialIndex;
370                         byte[] outBuffer = new byte [l];
371
372                         if (internalBuffer != null)
373                                 Buffer.BlockCopy (internalBuffer, initialIndex, outBuffer, 0, l);
374                         return outBuffer; 
375                 }
376
377                 public override void Write (byte [] buffer, int offset, int count)
378                 {
379                         if (!canWrite)
380                                 throw new NotSupportedException ("Cannot write to this stream.");
381
382                         if (buffer == null)
383                                 throw new ArgumentNullException ("buffer");
384                         
385                         if (offset < 0 || count < 0)
386                                 throw new ArgumentOutOfRangeException ();
387
388                         if (buffer.Length - offset < count)
389                                 throw new ArgumentException ("offset+count",
390                                                              "The size of the buffer is less than offset + count.");
391
392                         CheckIfClosedThrowDisposed ();
393
394                         // reordered to avoid possible integer overflow
395                         if (position > length - count)
396                                 Expand (position + count);
397
398                         Buffer.BlockCopy (buffer, offset, internalBuffer, position, count);
399                         position += count;
400                         if (position >= length)
401                                 length = position;
402                 }
403
404                 public override void WriteByte (byte value)
405                 {
406                         CheckIfClosedThrowDisposed ();
407                         if (!canWrite)
408                                 throw new NotSupportedException ("Cannot write to this stream.");
409
410                         if (position >= length) {
411                                 Expand (position + 1);
412                                 length = position + 1;
413                         }
414
415                         internalBuffer [position++] = value;
416                 }
417
418                 public virtual void WriteTo (Stream stream)
419                 {
420                         CheckIfClosedThrowDisposed ();
421
422                         if (stream == null)
423                                 throw new ArgumentNullException ("stream");
424
425                         stream.Write (internalBuffer, initialIndex, length - initialIndex);
426                 }
427
428 #if NET_4_5
429
430                 public override Task CopyToAsync (Stream destination, int bufferSize, CancellationToken cancellationToken)
431                 {
432                         // TODO: Specialization but what for?
433                         return base.CopyToAsync (destination, bufferSize, cancellationToken);
434                 }
435
436                 public override Task FlushAsync (CancellationToken cancellationToken)
437                 {
438                         if (cancellationToken.IsCancellationRequested)
439                                 return TaskConstants<int>.Canceled;
440
441                         Flush ();
442                         return TaskConstants.Finished;
443                 }
444
445                 public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
446                 {
447                         if (cancellationToken.IsCancellationRequested)
448                                 return TaskConstants<int>.Canceled;
449
450                         count = Read (buffer, offset, count);
451
452                         // Try not to allocate a new task for every buffer read
453                         if (read_task == null || read_task.Result != count)
454                                 read_task = Task<int>.FromResult (count);
455
456                         return read_task;
457                 }
458
459                 public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
460                 {
461                         if (cancellationToken.IsCancellationRequested)
462                                 return TaskConstants<int>.Canceled;
463
464                         Write (buffer, offset, count);
465                         return TaskConstants.Finished;
466                 }
467 #endif
468         }               
469 }