Merge pull request #665 from andreas-auerswald/master
[mono.git] / mcs / class / corlib / System.IO / UnmanagedMemoryStream.cs
index 25578dd001b94deeb764d0fdaa70f6d8b30c0c8c..4083e35d73b7df5f386cbbdeac9177dec5e23208 100644 (file)
@@ -1,16 +1,14 @@
-//------------------------------------------------------------------------------
 //
 // System.IO.UnmanagedMemoryStream.cs
 //
 // Copyright (C) 2006 Sridhar Kulkarni, All Rights Reserved
 //
-// Author:         Sridhar Kulkarni (sridharkulkarni@gmail.com)
-// Created:        Monday, July 10, 2006
+// Authors:
+//     Sridhar Kulkarni (sridharkulkarni@gmail.com)
+//     Gert Driesen (drieseng@users.sourceforge.net)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-//------------------------------------------------------------------------------
-
-//
-// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005-2006, 2009 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if NET_2_0
-
 using System;
 using System.IO;
 using System.Runtime.InteropServices;
 
-
 namespace System.IO
 {
-       [CLSCompliantAttribute(false)]
        public class UnmanagedMemoryStream : Stream
        {
                long length;
                bool closed;
-               bool canseek = false;
                long capacity;
                FileAccess fileaccess;
                IntPtr initial_pointer;
-               IntPtr pointer_position;
                long initial_position;
                long current_position;
+#if NET_4_0
+               SafeBuffer safebuffer;
+#endif
+               
+               internal event EventHandler Closed;
                
 #region Constructor
                protected UnmanagedMemoryStream()
                {
-                       fileaccess = FileAccess.Read;
-                       initial_position = 0;
-                       canseek = true;
-                       closed = false;
-                       current_position = initial_position;
+                       closed = true;
                }
-               
-               public unsafe UnmanagedMemoryStream (byte *pointer, long len)
+
+               [CLSCompliantAttribute(false)]
+               public unsafe UnmanagedMemoryStream (byte *pointer, long length) :
+                       this (pointer, length, length, FileAccess.Read)
                {
-                       if (pointer == null)
-                               throw new ArgumentNullException("The pointer value is a null reference ");
-                       if (len < 0)
-                               throw new ArgumentOutOfRangeException("The length value is less than zero");
-                       fileaccess = FileAccess.Read;
-                       length = len;
-                       capacity = len;
-                       initial_position = 0;
-                       current_position = initial_position;
-                       canseek = true;
-                       closed = false;
-                       initial_pointer = new IntPtr((void*)pointer);
                }
                
-               public unsafe UnmanagedMemoryStream (byte *pointer, long len, long cap, FileAccess access)
+               [CLSCompliantAttribute(false)]
+               public unsafe UnmanagedMemoryStream (byte *pointer, long length, long capacity, FileAccess access)
                {
-                       if (pointer == null)
-                               throw new ArgumentNullException("The pointer value is a null reference");
-                       if (len < 0)
-                               throw new ArgumentOutOfRangeException("The length value is less than zero");
-                       if (capacity < 0)
-                               throw new ArgumentOutOfRangeException("The capacity value is less than zero");
-                       if (len > capacity)
-                               throw new ArgumentOutOfRangeException("The length value is greater than the capacity value");
-                       fileaccess = access;
-                       length = len;
-                       capacity = cap;
-                       initial_position = 0;
-                       current_position = initial_position;
-                       canseek = true;
-                       initial_pointer = new IntPtr ((void*)pointer);
-                       closed = false;
-                       fileaccess = access;
+                       closed = true;
+                       Initialize (pointer, length, capacity, access);
                }
+
+#if NET_4_0
+               public UnmanagedMemoryStream (SafeBuffer buffer, long offset, long length) :
+                       this (buffer, offset, length, FileAccess.Read)
+               {
+               }
+
+               public UnmanagedMemoryStream (SafeBuffer buffer, long offset, long length, FileAccess access)
+               {
+                       closed = true;
+                       Initialize (buffer, offset, length, access);
+               }
+#endif
 #endregion
        
 #region Properties
                public override bool CanRead {
                        get {
-                               if (closed)
-                                       return false;
-                               else
-                                       return ((fileaccess == FileAccess.Read || fileaccess == FileAccess.ReadWrite)? true:false);
+                               return (!closed && (fileaccess != FileAccess.Write));
                        }
                }
 
                public override bool CanSeek {
                        get {
-                               return ((closed) ? false : true);
+                               return !closed;
                        }
                }
                
                public override bool CanWrite {
                        get {
-                               if (closed)
-                                       return (false);
-                               else
-                                       return ((fileaccess == FileAccess.Write || fileaccess == FileAccess.ReadWrite)? true:false);
+                               return (!closed && (fileaccess != FileAccess.Read));
                        }
                }
                public long Capacity {
@@ -142,89 +118,132 @@ namespace System.IO
                                        return (length);
                        }
                }
+
                public override long Position {
                        get {
                                if (closed)
                                        throw new ObjectDisposedException("The stream is closed");
-                               else
-                                       return (current_position);
+                               return (current_position);
                        }
                        set {
                                if (closed)
                                        throw new ObjectDisposedException("The stream is closed");
-                               if (value < 0 || value > (long)Int32.MaxValue || value > capacity)
-                                       throw new ArgumentOutOfRangeException("value that is less than zero, or the position is larger than Int32.MaxValue or capacity of the stream");
-                               else
-                                       current_position = value;
+                               if (value < 0)
+                                       throw new ArgumentOutOfRangeException("value", "Non-negative number required.");
+                               if (value > (long)Int32.MaxValue)
+                                       throw new ArgumentOutOfRangeException("value", "The position is larger than Int32.MaxValue.");
+                               current_position = value;
                        }
                }
 
+               [CLSCompliantAttribute (false)]
                public unsafe byte* PositionPointer {
                        get {
-                               throw new NotImplementedException("Error");
+#if NET_4_0
+                               if (safebuffer != null)
+                                       throw new NotSupportedException ("Not supported when using SafeBuffer");
+#endif
+                               if (closed)
+                                       throw new ObjectDisposedException("The stream is closed");
+                               if (current_position >= length)
+                                       throw new IndexOutOfRangeException ("value");
+
+                               return (byte *) initial_pointer + current_position;
                        }
                        set {
-                               throw new NotImplementedException("Error");
+#if NET_4_0
+                               if (safebuffer != null)
+                                       throw new NotSupportedException ("Not supported when using SafeBuffer");
+#endif
+                               if (closed)
+                                       throw new ObjectDisposedException("The stream is closed");
+
+                               if (value < (byte *)initial_pointer)
+                                       throw new IOException ("Address is below the inital address");
+
+                               Position = value - (byte*) initial_pointer;
                        }
                }
 #endregion
                
 #region Methods
                public override int Read ([InAttribute] [OutAttribute] byte[] buffer, int offset, int count)
-                        {
-                               if (closed)
-                                       throw new ObjectDisposedException("The stream is closed");
-
+               {
+                       if (closed)
+                               throw new ObjectDisposedException("The stream is closed");
+                       
+                       if (buffer == null)
+                               throw new ArgumentNullException("buffer");
+                       if (offset < 0)
+                               throw new ArgumentOutOfRangeException("offset", "Non-negative number required.");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
+                       if ((buffer.Length - offset) < count)
+                               throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
+                       
+                       if (fileaccess == FileAccess.Write)
+                               throw new NotSupportedException("Stream does not support reading");
 
-                               if (buffer == null)
-                                       throw new ArgumentNullException("The buffer parameter is set to a null reference");
-                               if (offset < 0 || count < 0)
-                                       throw new ArgumentOutOfRangeException("The offset or count parameter is less than zero");
-                               if ((buffer.Length - offset) < count)
-                                       throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
+                       if (current_position >= length)
+                               return 0;
 
-                               if (fileaccess == FileAccess.Write)
-                                       throw new NotSupportedException("Read property is false");
-                               else {
-                                       if (current_position == capacity)
-                                               return (0);
-                                       else {
-                                               unsafe {
-                                                       Marshal.Copy(initial_pointer, buffer, offset, 
-                                                                    (int)length);
-                                                       current_position += length;
-                                               }
-                                               return (buffer.GetLength(0));
+                       int progress = current_position + count < length ? count : (int) (length - current_position);
+#if NET_4_0
+                       if (safebuffer != null) {
+                               unsafe {
+                                       byte *ptr = null;
+                                       try {
+                                               safebuffer.AcquirePointer (ref ptr);
+                                               Marshal.Copy (new IntPtr (ptr + current_position), buffer, offset, progress);
+                                       } finally {
+                                               if (ptr != null)
+                                                       safebuffer.ReleasePointer ();
                                        }
                                }
+                       } else
+#endif
+                       {
+                               Marshal.Copy (new IntPtr (initial_pointer.ToInt64 () + current_position), buffer, offset, progress);
                        }
-               public override int ReadByte () {
+                       current_position += progress;
+                       return progress;
+               }
+
+               public override int ReadByte ()
+               {
                        if (closed)
                                throw new ObjectDisposedException("The stream is closed");
-                       if (current_position == capacity)
-                               throw new NotSupportedException("The current position is at the end of the stream");
+                       
+                       if (fileaccess== FileAccess.Write)
+                               throw new NotSupportedException("Stream does not support reading");
 
-                       int byteread;
+                       if (current_position >= length)
+                               return (-1);
 
-                       if (fileaccess== FileAccess.Write)
-                               throw new NotSupportedException("The underlying memory does not support reading");
-                       else {
-                               if (current_position == length)
-                                       return (-1);
-                               else {
-                                       unsafe {
-                                               byteread = (int)Marshal.ReadByte(initial_pointer, (int)current_position);
-                                               current_position++;
+#if NET_4_0
+                       if (safebuffer != null) {
+                               unsafe {
+                                       byte *ptr = null;
+                                       try {
+                                               safebuffer.AcquirePointer (ref ptr);
+                                               return (int) Marshal.ReadByte (new IntPtr (ptr), (int) current_position++);
+                                       } finally {
+                                               if (ptr != null)
+                                                       safebuffer.ReleasePointer ();
                                        }
-                                       return(byteread);
                                }
+                       } else
+#endif
+                       {
+                               return (int) Marshal.ReadByte(initial_pointer, (int) current_position++);
                        }
                }
-               public override long Seek (long offset, SeekOrigin loc) {
+               
+               public override long Seek (long offset, SeekOrigin loc)
+               {
                        if (closed)
                                throw new ObjectDisposedException("The stream is closed");
-                       if (offset > capacity)
-                               throw new ArgumentOutOfRangeException("The offset value is larger than the maximum size of the stream");
+
                        long refpoint;
                        switch(loc) {
                        case SeekOrigin.Begin:
@@ -241,7 +260,7 @@ namespace System.IO
                        default:
                                throw new ArgumentException("Invalid SeekOrigin option");
                        }
-                       refpoint =+ (int)offset;
+                       refpoint += offset;
                        if (refpoint < initial_position)
                                throw new IOException("An attempt was made to seek before the beginning of the stream");
                        current_position = refpoint;
@@ -250,16 +269,21 @@ namespace System.IO
                 
                public override void SetLength (long value)
                {
+#if NET_4_0
+                       if (safebuffer != null)
+                               throw new NotSupportedException ("Not supported when using SafeBuffer");
+#endif
                        if (closed)
                                throw new ObjectDisposedException("The stream is closed");
-                       if (value < 0 || value > capacity)
-                               throw new ArgumentOutOfRangeException("The specified value is negetive exceeds the capacity of the stream");
+                       if (value < 0)
+                               throw new ArgumentOutOfRangeException("length", "Non-negative number required.");
+                       if (value > capacity)
+                               throw new IOException ("Unable to expand length of this stream beyond its capacity.");
                        if (fileaccess == FileAccess.Read)
-                               throw new NotSupportedException("write property is set to false");
-                       if (fileaccess == FileAccess.Read)
-                               throw new NotSupportedException("Length change not supported see object construction");
-                       else
-                               length = value;
+                               throw new NotSupportedException ("Stream does not support writing.");
+                       length = value;
+                       if (length < current_position)
+                               current_position = length;
                }
 
                public override void Flush ()
@@ -270,15 +294,13 @@ namespace System.IO
                        //but is included as part of the Stream base class
                }
                 
-               public void Dispose ()
-               {
-                       Dispose(true);
-               }
-                
                protected override void Dispose (bool disposing)
                {
-
+                       if (closed)
+                               return;
                        closed = true;
+                       if (Closed != null)
+                               Closed (this, null);
                }
                 
                public override void Write (byte[] buffer, int offset, int count)
@@ -287,41 +309,141 @@ namespace System.IO
                                throw new ObjectDisposedException("The stream is closed");
                        if (buffer == null)
                                throw new ArgumentNullException("The buffer parameter is a null reference");
-                       if (count > capacity)
-                               throw new ArgumentOutOfRangeException("The count value is greater than the capacity of the stream");
-                       if (offset < 0 || count < 0)
-                               throw new ArgumentOutOfRangeException("One of the specified parameters is less than zero");
+                       if (offset < 0)
+                               throw new ArgumentOutOfRangeException("offset", "Non-negative number required.");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
                        if ((buffer.Length - offset) < count)
                                throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
+                       if (current_position > capacity - count)
+                               throw new NotSupportedException ("Unable to expand length of this stream beyond its capacity.");
                        if (fileaccess == FileAccess.Read)
-                               throw new NotSupportedException("write property is set to false");
-                       else {
+                               throw new NotSupportedException ("Stream does not support writing.");
+
+#if NET_4_0
+                       if (safebuffer != null) {
+                               unsafe {
+                                       byte *dest = null;
+                                       try {
+                                               safebuffer.AcquirePointer (ref dest);
+                                               fixed (byte *src = buffer) {
+                                                       dest += current_position;
+                                                       String.memcpy (dest, src + offset, count);
+                                               }
+                                       } finally {
+                                               if (dest != null)
+                                                       safebuffer.ReleasePointer ();
+                                       }
+                               }
+                       } else
+#endif
+                       {
                                unsafe {
-                                       //COPY data from managed buffer to unmanaged mem pointer
-                                       Marshal.Copy(buffer, offset, initial_pointer, (int)length);
-                                       current_position += length;
+                                       fixed (byte *src = buffer) {
+                                               byte *dest = (byte *) initial_pointer + current_position;
+                                               String.memcpy (dest, src + offset, count);
+                                       }
                                }
                        }
+                       current_position += count;
+                       if (current_position > length)
+                               length = current_position;
                }
                
                public override void WriteByte (byte value)
-                {
+               {
                        if (closed)
                                throw new ObjectDisposedException("The stream is closed");
                        
                        if (current_position == capacity)
                                throw new NotSupportedException("The current position is at the end of the capacity of the stream");
                        if (fileaccess == FileAccess.Read)
-                               throw new NotSupportedException("write property is set to false");
-                       else {
+                               throw new NotSupportedException("Stream does not support writing.");
+#if NET_4_0
+                       if (safebuffer != null) {
+                               unsafe {
+                                       byte *dest = null;
+                                       try {
+                                               safebuffer.AcquirePointer (ref dest);
+                                               dest += current_position++;
+                                               *dest = value;
+                                       } finally {
+                                               if (dest != null)
+                                                       safebuffer.ReleasePointer ();
+                                       }
+                               }
+                       } else
+#endif
+                       {
                                unsafe {
-                                       Marshal.WriteByte(initial_pointer, (int)current_position, value);
-                                       current_position++;
+                                       byte *dest = (byte *) initial_pointer + (int) current_position++;
+                                       *dest = value;
                                }
                        }
+                       if (current_position > length)
+                               length = current_position;
                }
+
+               [CLSCompliant (false)]
+               protected unsafe void Initialize (byte* pointer, long length,
+                                                 long capacity,
+                                                 FileAccess access)
+               {
+                       if (pointer == null)
+                               throw new ArgumentNullException("pointer");
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException("length", "Non-negative number required.");
+                       if (capacity < 0)
+                               throw new ArgumentOutOfRangeException("capacity", "Non-negative number required.");
+                       if (length > capacity)
+                               throw new ArgumentOutOfRangeException("length", "The length cannot be greater than the capacity.");
+                       if ((access < FileAccess.Read) || (access > FileAccess.ReadWrite))
+                               throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
+                       if (!closed)
+                               throw new InvalidOperationException ("Called Initialize twice");
+                               
+                       fileaccess = access;
+                       this.length = length;
+                       this.capacity = capacity;
+                       initial_position = 0;
+                       current_position = initial_position;
+                       initial_pointer = new IntPtr ((void*)pointer);
+                       closed = false;
+               }
+
+#if NET_4_0
+               protected void Initialize (SafeBuffer buffer, long offset, long length, FileAccess access)
+               {
+                       if (buffer == null)
+                               throw new ArgumentNullException ("buffer");
+
+                       if (offset < 0)
+                               throw new ArgumentOutOfRangeException ("offset");
+
+                       if (length < 0)
+                               throw new ArgumentOutOfRangeException ("length");
+
+                       ulong blength = buffer.ByteLength;
+                       if ((blength - (ulong) length) < (ulong) offset)
+                               throw new ArgumentException ("Invalid offset and/or length");
+
+                       if (access < FileAccess.Read || access > FileAccess.ReadWrite)
+                               throw new ArgumentOutOfRangeException ("access");
+
+                       if (!closed)
+                               throw new InvalidOperationException ("Called Initialize twice");
+
+                       this.length = length;
+                       this.capacity = length;
+                       this.fileaccess = access;
+                       this.safebuffer = buffer;
+                       initial_position = offset;
+                       current_position = offset;
+                       closed = false;
+               }
+#endif
 #endregion
        }
 }
-#endif