Merge branch 'msbuilddll2'
[mono.git] / mcs / class / corlib / System.IO / MemoryStream.cs
index a68fe6ad30bed281fb159fbd45091c87eb1a632c..4ce86df30b616f0b9ff96498f8d7fa783d76a698 100644 (file)
@@ -1,17 +1,15 @@
 //
-// System.IO.MemoryStream 
+// System.IO.MemoryStream.cs
 //
 // Authors:    Marcin Szczepanski (marcins@zipworld.com.au)
 //             Patrik Torstensson
 //             Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//             Marek Safar (marek.safar@gmail.com)
 //
 // (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
-// Copyright (C) 2004 Novell (http://www.novell.com)
-//
-
-//
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 
 using System.Globalization;
 using System.Runtime.InteropServices;
+using System.Threading;
+#if NET_4_5
+using System.Threading.Tasks;
+#endif
 
 namespace System.IO
 {
        [Serializable]
-       [MonoTODO ("Fix serialization compatibility with MS.NET")]
+       [ComVisible (true)]
+       [MonoLimitation ("Serialization format not compatible with .NET")]
        public class MemoryStream : Stream
        {
                bool canWrite;
@@ -51,6 +54,11 @@ namespace System.IO
                bool expandable;
                bool streamClosed;
                int position;
+               int dirty_bytes;
+#if NET_4_5
+               [NonSerialized]
+               Task<int> read_task;
+#endif
 
                public MemoryStream () : this (0)
                {
@@ -78,12 +86,12 @@ namespace System.IO
                        InternalConstructor (buffer, 0, buffer.Length, true, false);                        
                }
 
-               public MemoryStream (byte [] buffer, bool writeable)
+               public MemoryStream (byte [] buffer, bool writable)
                {
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
                        
-                       InternalConstructor (buffer, 0, buffer.Length, writeable, false);
+                       InternalConstructor (buffer, 0, buffer.Length, writable, false);
                }
 
                public MemoryStream (byte [] buffer, int index, int count)
@@ -91,17 +99,17 @@ namespace System.IO
                        InternalConstructor (buffer, index, count, true, false);
                }
 
-               public MemoryStream (byte [] buffer, int index, int count, bool writeable)
+               public MemoryStream (byte [] buffer, int index, int count, bool writable)
                {
-                       InternalConstructor (buffer, index, count, writeable, false);
+                       InternalConstructor (buffer, index, count, writable, false);
                }
 
-               public MemoryStream (byte [] buffer, int index, int count, bool writeable, bool publicallyVisible)
+               public MemoryStream (byte [] buffer, int index, int count, bool writable, bool publiclyVisible)
                {
-                       InternalConstructor (buffer, index, count, writeable, publicallyVisible);
+                       InternalConstructor (buffer, index, count, writable, publiclyVisible);
                }
 
-               void InternalConstructor (byte [] buffer, int index, int count, bool writeable, bool publicallyVisible)
+               void InternalConstructor (byte [] buffer, int index, int count, bool writable, bool publicallyVisible)
                {
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
@@ -113,7 +121,7 @@ namespace System.IO
                                throw new ArgumentException ("index+count", 
                                                             "The size of the buffer is less than index + count.");
 
-                       canWrite = writeable;
+                       canWrite = writable;
 
                        internalBuffer = buffer;
                        capacity = count + index;
@@ -131,12 +139,6 @@ namespace System.IO
                                throw new ObjectDisposedException ("MemoryStream");
                }
                
-               void CheckIfClosedThrowIO ()
-               {
-                       if (streamClosed)
-                               throw new IOException ("MemoryStream is closed");
-               }
-               
                public override bool CanRead {
                        get { return !streamClosed; }
                }
@@ -157,8 +159,6 @@ namespace System.IO
 
                        set {
                                CheckIfClosedThrowDisposed ();
-                               if (value == capacity)
-                                       return; // LAMENESS: see MemoryStreamTest.ConstructorFive
 
                                if (!expandable)
                                        throw new NotSupportedException ("Cannot expand this MemoryStream");
@@ -167,12 +167,17 @@ namespace System.IO
                                        throw new ArgumentOutOfRangeException ("value",
                                        "New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
 
+                               if (internalBuffer != null && value == internalBuffer.Length)
+                                       return;
+
                                byte [] newBuffer = null;
                                if (value != 0) {
                                        newBuffer = new byte [value];
-                                       Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, length);
+                                       if (internalBuffer != null)
+                                               Buffer.BlockCopy (internalBuffer, 0, newBuffer, 0, length);
                                }
 
+                               dirty_bytes = 0; // discard any dirty area beyond previous length
                                internalBuffer = newBuffer; // It's null when capacity is set to 0
                                capacity = value;
                        }
@@ -212,7 +217,7 @@ namespace System.IO
                        }
                }
 
-               public override void Close ()
+               protected override void Dispose (bool disposing)
                {
                        streamClosed = true;
                        expandable = false;
@@ -233,8 +238,6 @@ namespace System.IO
 
                public override int Read ([In,Out] byte [] buffer, int offset, int count)
                {
-                       CheckIfClosedThrowDisposed ();
-
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
 
@@ -245,13 +248,15 @@ namespace System.IO
                                throw new ArgumentException ("offset+count",
                                                              "The size of the buffer is less than offset + count.");
 
+                       CheckIfClosedThrowDisposed ();
+
                        if (position >= length || count == 0)
                                return 0;
 
                        if (position > length - count)
                                count = length - position;
 
-                       Buffer.BlockCopyInternal (internalBuffer, position, buffer, offset, count);
+                       Buffer.BlockCopy (internalBuffer, position, buffer, offset, count);
                        position += count;
                        return count;
                }
@@ -316,6 +321,18 @@ namespace System.IO
                        return minimum;
                }
 
+               void Expand (int newSize)
+               {
+                       // We don't need to take into account the dirty bytes when incrementing the
+                       // Capacity, as changing it will only preserve the valid clear region.
+                       if (newSize > capacity)
+                               Capacity = CalculateNewCapacity (newSize);
+                       else if (dirty_bytes > 0) {
+                               Array.Clear (internalBuffer, length, dirty_bytes);
+                               dirty_bytes = 0;
+                       }
+               }
+
                public override void SetLength (long value)
                {
                        if (!expandable && value > capacity)
@@ -336,12 +353,11 @@ namespace System.IO
                                throw new ArgumentOutOfRangeException ();
 
                        int newSize = (int) value + initialIndex;
-                       if (newSize > capacity)
-                               Capacity = CalculateNewCapacity (newSize);
-                       else if (newSize < length)
-                               // zeroize present data (so we don't get it 
-                               // back if we expand the stream using Seek)
-                               Array.Clear (internalBuffer, newSize, length - newSize);
+
+                       if (newSize > length)
+                               Expand (newSize);
+                       else if (newSize < length) // Postpone the call to Array.Clear till expand time
+                               dirty_bytes += length - newSize;
 
                        length = newSize;
                        if (position > length)
@@ -353,17 +369,13 @@ namespace System.IO
                        int l = length - initialIndex;
                        byte[] outBuffer = new byte [l];
 
-                       Buffer.BlockCopyInternal (internalBuffer, initialIndex, outBuffer, 0, l);
+                       if (internalBuffer != null)
+                               Buffer.BlockCopy (internalBuffer, initialIndex, outBuffer, 0, l);
                        return outBuffer; 
                }
 
                public override void Write (byte [] buffer, int offset, int count)
                {
-                       CheckIfClosedThrowDisposed ();
-
-                       if (!canWrite)
-                               throw new NotSupportedException ("Cannot write to this stream.");
-
                        if (buffer == null)
                                throw new ArgumentNullException ("buffer");
                        
@@ -374,11 +386,16 @@ namespace System.IO
                                throw new ArgumentException ("offset+count",
                                                             "The size of the buffer is less than offset + count.");
 
+                       CheckIfClosedThrowDisposed ();
+
+                       if (!CanWrite)
+                               throw new NotSupportedException ("Cannot write to this stream.");
+
                        // reordered to avoid possible integer overflow
-                       if (position > capacity - count)
-                               Capacity = CalculateNewCapacity (position + count);
+                       if (position > length - count)
+                               Expand (position + count);
 
-                       Buffer.BlockCopyInternal (buffer, offset, internalBuffer, position, count);
+                       Buffer.BlockCopy (buffer, offset, internalBuffer, position, count);
                        position += count;
                        if (position >= length)
                                length = position;
@@ -390,11 +407,10 @@ namespace System.IO
                        if (!canWrite)
                                throw new NotSupportedException ("Cannot write to this stream.");
 
-                       if (position >= capacity)
-                               Capacity = CalculateNewCapacity (position + 1);
-
-                       if (position >= length)
+                       if (position >= length) {
+                               Expand (position + 1);
                                length = position + 1;
+                       }
 
                        internalBuffer [position++] = value;
                }
@@ -408,5 +424,77 @@ namespace System.IO
 
                        stream.Write (internalBuffer, initialIndex, length - initialIndex);
                }
+
+#if NET_4_5
+
+               public override Task CopyToAsync (Stream destination, int bufferSize, CancellationToken cancellationToken)
+               {
+                       // TODO: Specialization but what for?
+                       return base.CopyToAsync (destination, bufferSize, cancellationToken);
+               }
+
+               public override Task FlushAsync (CancellationToken cancellationToken)
+               {
+                       if (cancellationToken.IsCancellationRequested)
+                               return TaskConstants.Canceled;
+
+                       try {
+                               Flush ();
+                               return TaskConstants.Finished;
+                       } catch (Exception ex) {
+                               return Task<object>.FromException (ex);
+                       }
+               }
+
+               public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+
+                       if (offset < 0 || count < 0)
+                               throw new ArgumentOutOfRangeException ("offset or count less than zero.");
+
+                       if (buffer.Length - offset < count )
+                               throw new ArgumentException ("offset+count",
+                                                            "The size of the buffer is less than offset + count.");
+                       if (cancellationToken.IsCancellationRequested)
+                               return TaskConstants<int>.Canceled;
+
+                       try {
+                               count = Read (buffer, offset, count);
+
+                               // Try not to allocate a new task for every buffer read
+                               if (read_task == null || read_task.Result != count)
+                                       read_task = Task<int>.FromResult (count);
+
+                               return read_task;
+                       } catch (Exception ex) {
+                               return Task<int>.FromException (ex);
+                       }
+               }
+
+               public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+                       
+                       if (offset < 0 || count < 0)
+                               throw new ArgumentOutOfRangeException ();
+
+                       if (buffer.Length - offset < count)
+                               throw new ArgumentException ("offset+count",
+                                                            "The size of the buffer is less than offset + count.");
+
+                       if (cancellationToken.IsCancellationRequested)
+                               return TaskConstants.Canceled;
+
+                       try {
+                               Write (buffer, offset, count);
+                               return TaskConstants.Finished;
+                       } catch (Exception ex) {
+                               return Task<object>.FromException (ex);
+                       }
+               }
+#endif
        }               
 }