Ver código fonte

2009-07-20 Gonzalo Paniagua Javier <[email protected]>

	* GzipStream.cs: fixed Dispose (bool).
	* DeflateStream.cs: simplify the interface with unmanaged code, big
	reduction of managaed<->unmanaged round trips.
	Also fixes bug #523418.


svn path=/trunk/mcs/; revision=138252
Gonzalo Paniagua Javier 16 anos atrás
pai
commit
8c052b4698

+ 7 - 0
mcs/class/System/System.IO.Compression/ChangeLog

@@ -1,3 +1,10 @@
+2009-07-20 Gonzalo Paniagua Javier <[email protected]>
+
+	* GzipStream.cs: fixed Dispose (bool).
+	* DeflateStream.cs: simplify the interface with unmanaged code, big
+	reduction of managaed<->unmanaged round trips.
+	Also fixes bug #523418.
+
 2007-09-22  Gert Driesen  <[email protected]>
 
 	* DeflateStream.cs: Avoid double free. Fixes bug #327480.

+ 203 - 251
mcs/class/System/System.IO.Compression/DeflateStream.cs

@@ -4,8 +4,28 @@
 //
 // Authors:
 //	Christopher James Lahey <[email protected]>
+//	Gonzalo Paniagua Javier ([email protected])
 //
-// (c) 2004 Novell, Inc. <http://www.novell.com>
+// (c) Copyright 2004,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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
 #if NET_2_0
@@ -17,188 +37,127 @@ using System.Runtime.Remoting.Messaging;
 namespace System.IO.Compression {
 	public class DeflateStream : Stream
 	{
-		private Stream compressedStream;
-		private CompressionMode mode;
-		private bool leaveOpen;
-		private bool open;
-		private IntPtr z_stream;
-
-		private const int BUFFER_SIZE = 4096;
-		private IntPtr sized_buffer;
-		static int bytes_read = 0;
-		private bool finished = false;
-		private enum ZReturnConsts {
-			Z_OK = 0,
-			Z_STREAM_END = 1,
-			Z_NEED_DICT = 2,
-			Z_STREAM_ERROR = -2,
-			Z_DATA_ERROR = -3,
-			Z_MEM_ERROR = -4,
-			Z_BUF_ERROR = -5,
+		delegate int UnmanagedReadOrWrite (IntPtr buffer, int length);
+		delegate int ReadMethod (byte[] array, int offset, int count);
+		delegate void WriteMethod (byte[] array, int offset, int count);
+
+		Stream base_stream;
+		CompressionMode mode;
+		bool leaveOpen;
+		bool disposed;
+		UnmanagedReadOrWrite feeder; // This will be passed to unmanaged code and used there
+		IntPtr z_stream;
+		byte [] io_buffer;
+
+		public DeflateStream (Stream compressedStream, CompressionMode mode) :
+			this (compressedStream, mode, false, false)
+		{
+		}
+
+		public DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen) :
+			this (compressedStream, mode, leaveOpen, false)
+		{
 		}
-		private enum ZFlushConsts {
-			Z_NO_FLUSH     = 0,
-			Z_PARTIAL_FLUSH = 1, /* will be removed, use Z_SYNC_FLUSH instead */
-			Z_SYNC_FLUSH    = 2,
-			Z_FULL_FLUSH    = 3,
-			Z_FINISH        = 4,
-			Z_BLOCK         = 5,
-		};
-
-		[DllImport("MonoPosixHelper")]
-		static extern IntPtr create_z_stream(CompressionMode compress, bool gzip);
-		[DllImport("MonoPosixHelper")]
-		static extern void free_z_stream(IntPtr z_stream, CompressionMode compress );
-		[DllImport("MonoPosixHelper")]
-		static extern void z_stream_set_next_in(IntPtr z_stream, IntPtr next_in);
-		[DllImport("MonoPosixHelper")]
-		static extern void z_stream_set_avail_in(IntPtr z_stream, int avail_in);
-		[DllImport("MonoPosixHelper")]
-		static extern int z_stream_get_avail_in(IntPtr z_stream);
-		[DllImport("MonoPosixHelper")]
-		static extern void z_stream_set_next_out(IntPtr z_stream, IntPtr next_out);
-		//[DllImport("MonoPosixHelper")]
-		//static extern void z_stream_set_avail_out(IntPtr z_stream, int avail_out);
-		[DllImport("MonoPosixHelper")]
-		static extern ZReturnConsts z_stream_inflate(IntPtr z_stream, ref int avail_out);
-		[DllImport("MonoPosixHelper")]
-		static extern ZReturnConsts z_stream_deflate(IntPtr z_stream, ZFlushConsts flush, IntPtr next_out, ref int avail_out);
-		delegate int  ReadMethod (byte[] array, int offset, int count);
-		delegate void WriteMethod(byte[] array, int offset, int count);
 
 		internal DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen, bool gzip)
 		{
 			if (compressedStream == null)
 				throw new ArgumentNullException ("compressedStream");
 
-			switch (mode) {
-			case CompressionMode.Compress:
-			case CompressionMode.Decompress:
-				break;
-			default:
+			if (mode != CompressionMode.Compress && mode != CompressionMode.Decompress)
 				throw new ArgumentException ("mode");
-			}
 
-			this.compressedStream = compressedStream;
-			this.mode = mode;
-			this.leaveOpen = leaveOpen;
-			this.sized_buffer = Marshal.AllocHGlobal (BUFFER_SIZE);
-			this.z_stream = create_z_stream (mode, gzip);
+			this.base_stream = compressedStream;
+			this.feeder = (mode == CompressionMode.Compress) ? new UnmanagedReadOrWrite (UnmanagedWrite) :
+									   new UnmanagedReadOrWrite (UnmanagedRead);
+			this.z_stream = CreateZStream (mode, gzip, feeder);
 			if (z_stream == IntPtr.Zero) {
+				this.base_stream = null;
+				this.feeder = null;
 				throw new NotImplementedException ("Failed to initialize zlib. You probably have an old zlib installed. Version 1.2.0.4 or later is required.");
 			}
-			this.open = true;
-			if (mode == CompressionMode.Compress) {
-				Flush();
-			}
+			this.mode = mode;
+			this.leaveOpen = leaveOpen;
 		}
 
-		public DeflateStream (Stream compressedStream, CompressionMode mode) :
-			this (compressedStream, mode, false, false) { }
-
-		public DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen) :
-			this (compressedStream, mode, leaveOpen, false) { }
-
 		protected override void Dispose (bool disposing)
 		{
-			if (!open) {
-				base.Dispose (disposing);
-				return;
-			}
-
-			try {
-				FlushInternal (true);
-				base.Dispose (disposing);
-			} finally {
-				try {
-					DisposeCore ();
-				} finally {
-					if (disposing)
-						Marshal.FreeHGlobal (sized_buffer);
+			if (disposing && !disposed) {
+				disposed = true;
+				IntPtr zz = z_stream;
+				z_stream = IntPtr.Zero;
+				int res = 0;
+				if (zz != IntPtr.Zero)
+					res = CloseZStream (zz); // This will Flush() the remaining output if any
+
+				io_buffer = null;
+				if (!leaveOpen) {
+					Stream st = base_stream;
+					if (st != null)
+						st.Close ();
+					base_stream = null;
 				}
+				CheckResult (res, "Dispose");
 			}
+
+			base.Dispose (disposing);
 		}
 
-		void DisposeCore ()
+		int UnmanagedRead (IntPtr buffer, int length)
 		{
-			if (/*mode == CompressionMode.Decompress &&*/ compressedStream.CanSeek) {
-				int avail_in = z_stream_get_avail_in (z_stream);
-				if (avail_in != 0) {
-					compressedStream.Seek (- avail_in, SeekOrigin.Current);
-					z_stream_set_avail_in (z_stream, 0);
+			int total = 0;
+			int n = 1;
+			while (length > 0 && n > 0) {
+				if (io_buffer == null)
+					io_buffer = new byte [4096];
+
+				n = base_stream.Read (io_buffer, 0, io_buffer.Length);
+				if (n > 0) {
+					Marshal.Copy (io_buffer, 0, buffer, n);
+					unsafe {
+						buffer = new IntPtr ((byte *) buffer.ToPointer () + n);
+					}
+					length -= n;
+					total += n;
 				}
 			}
+			return total;
+		}
 
-			free_z_stream (z_stream,mode);
-			z_stream = IntPtr.Zero;
-
-			if (!leaveOpen) {
-				compressedStream.Close();
+		int UnmanagedWrite (IntPtr buffer, int length)
+		{
+			int total = 0;
+			while (length > 0) {
+				if (io_buffer == null)
+					io_buffer = new byte [4096];
+
+				int count = Math.Min (length, io_buffer.Length);
+				Marshal.Copy (buffer, io_buffer, 0, count);
+				base_stream.Write (io_buffer, 0, count);
+				unsafe {
+					buffer = new IntPtr ((byte *) buffer.ToPointer () + count);
+				}
+				length -= count;
+				total += count;
 			}
-
-			open = false;
+			return total;
 		}
 
-		private int ReadInternal(byte[] array, int offset, int count) {
-			int buffer_size;
-			if (finished)
-				return 0;
-
-			if (compressedStream.CanSeek)
-				buffer_size = BUFFER_SIZE;
-			else
-				buffer_size = 1;
-
-			IntPtr buffer = Marshal.AllocHGlobal(count);
-			try {
-				int avail_out;
-
-				avail_out = count;
-				z_stream_set_next_out (z_stream, buffer);
-
-				while (avail_out != 0 && !finished) {
-					if (z_stream_get_avail_in (z_stream) == 0) {
-						byte[] read_buf = new byte[buffer_size];
-						int length_read = compressedStream.Read (read_buf, 0, buffer_size);
-						bytes_read += length_read;
-						if (length_read == 0) {
-							break;
-						}
-						Marshal.Copy (read_buf, 0, sized_buffer, length_read);
-						z_stream_set_next_in (z_stream, sized_buffer);
-						z_stream_set_avail_in (z_stream, length_read);
-					}
-					ZReturnConsts ret_val = z_stream_inflate(z_stream, ref avail_out);
-					switch (ret_val) {
-					case ZReturnConsts.Z_OK:
-						break;
-					case ZReturnConsts.Z_STREAM_END:
-						finished = true;
-						break;
-					case ZReturnConsts.Z_NEED_DICT:
-						throw new InvalidDataException ("ZLib stream requires a dictionary.");
-					case ZReturnConsts.Z_DATA_ERROR:
-						throw new InvalidDataException ("Invalid ZLib data.");
-					case ZReturnConsts.Z_STREAM_ERROR:
-						throw new InvalidOperationException ("Internal DeflateStream error.");
-					case ZReturnConsts.Z_MEM_ERROR:
-						throw new OutOfMemoryException ();
-					case ZReturnConsts.Z_BUF_ERROR:
-						throw new InvalidOperationException ("Internal DeflateStream error: Buf error.");
-					}
-				}
-				if (count != avail_out)
-					Marshal.Copy (buffer, array, offset, count - avail_out);
-				return count - avail_out; 
-			} finally {
-				Marshal.FreeHGlobal(buffer);
+		unsafe int ReadInternal (byte[] array, int offset, int count)
+		{
+			int result = 0;
+			fixed (byte *b = array) {
+				IntPtr ptr = new IntPtr (b + offset);
+				result = ReadZStream (z_stream, ptr, count);
 			}
+			CheckResult (result, "ReadInternal");
+			return result;
 		}
 
 		public override int Read (byte[] dest, int dest_offset, int count)
 		{
-			if (!open)
-				throw new ObjectDisposedException ("DeflateStream");
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().FullName);
 			if (dest == null)
 				throw new ArgumentNullException ("Destination array is null.");
 			if (!CanRead)
@@ -214,51 +173,20 @@ namespace System.IO.Compression {
 			return ReadInternal (dest, dest_offset, count);
 		}
 
-
-		private ZReturnConsts do_deflate (ZFlushConsts flush, out int avail_out) {
-			avail_out = BUFFER_SIZE;
-			ZReturnConsts ret_val = z_stream_deflate (z_stream, flush, sized_buffer, ref avail_out);
-			switch (ret_val) {
-			case ZReturnConsts.Z_STREAM_ERROR:
-				throw new InvalidOperationException ("Internal error.");
-			case ZReturnConsts.Z_MEM_ERROR:
-				throw new InvalidOperationException ("Memory error.");
-			}
-			return ret_val;
-		}
-
-		private void WriteInternal(byte[] array, int offset, int count) {
-			IntPtr buffer = Marshal.AllocHGlobal(count);
-			try {
-				int avail_in;
-
-				avail_in = count;
-
-				Marshal.Copy (array, offset, buffer, count);
-				z_stream_set_next_in (z_stream, buffer);
-				z_stream_set_avail_in (z_stream, avail_in);
-				while (avail_in != 0) {
-					int avail_out;
-				
-					do_deflate (ZFlushConsts.Z_NO_FLUSH, out avail_out);
-
-					if (avail_out != BUFFER_SIZE) {
-						byte[] output = new byte[BUFFER_SIZE - avail_out];
-						Marshal.Copy (sized_buffer, output, 0, BUFFER_SIZE - avail_out);
-						compressedStream.Write(output, 0, BUFFER_SIZE - avail_out);
-					}
-
-					avail_in = z_stream_get_avail_in (z_stream);
-				}
-			} finally {
-				Marshal.FreeHGlobal(buffer);
+		unsafe void WriteInternal (byte[] array, int offset, int count)
+		{
+			int result = 0;
+			fixed (byte *b = array) {
+				IntPtr ptr = new IntPtr (b + offset);
+				result = WriteZStream (z_stream, ptr, count);
 			}
+			CheckResult (result, "WriteInternal");
 		}
 
 		public override void Write (byte[] src, int src_offset, int count)
 		{
-			if (!open)
-				throw new ObjectDisposedException ("DeflateStream");
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().FullName);
 
 			if (src == null)
 				throw new ArgumentNullException ("src");
@@ -275,55 +203,61 @@ namespace System.IO.Compression {
 			WriteInternal (src, src_offset, count);
 		}
 
-		private void FlushInternal (bool finish) 
+		static void CheckResult (int result, string where)
 		{
-			if (!open)
-				throw new ObjectDisposedException ("DeflateStream");
-
-			int avail_out;
-			ZReturnConsts ret_val;
-
-			if (!(open && mode == CompressionMode.Compress && compressedStream.CanWrite))
+			if (result >= 0)
 				return;
 
-			z_stream_set_next_in (z_stream, IntPtr.Zero);
-			z_stream_set_avail_in (z_stream, 0);
-
-			while (true) {
-				ret_val = do_deflate (finish ? ZFlushConsts.Z_FINISH : ZFlushConsts.Z_SYNC_FLUSH, out avail_out);
-
-				if (BUFFER_SIZE != avail_out) {
-					byte[] output = new byte[BUFFER_SIZE - avail_out];
-					Marshal.Copy (sized_buffer, output, 0, BUFFER_SIZE - avail_out);
-					compressedStream.Write(output, 0, BUFFER_SIZE - avail_out);
-				} else {
-					if (!finish)
-						break;
-				}
-				if (ret_val == ZReturnConsts.Z_STREAM_END)
-					break;
+			string error;
+			switch (result) {
+			case -1: // Z_ERRNO
+				error = "Unknown error"; // Marshal.GetLastWin32() ?
+				break;
+			case -2: // Z_STREAM_ERROR
+				error = "Internal error";
+				break;
+			case -3: // Z_DATA_ERROR
+				error = "Corrupted data";
+				break;
+			case -4: // Z_MEM_ERROR
+				error = "Not enough memory";
+				break;
+			case -5: // Z_BUF_ERROR
+				error = "Internal error (no progress possible)";
+				break;
+			case -6: // Z_VERSION_ERROR
+				error = "Invalid version";
+				break;
+			case -10:
+				error = "Invalid argument(s)";
+				break;
+			case -11:
+				error = "IO error";
+				break;
+			default:
+				error = "Unknown error";
+				break;
 			}
 
-			compressedStream.Flush();
+			throw new IOException (error + " " + where);
 		}
 
-		public override void Flush () {
-			FlushInternal (false);
-		}
-
-		public override long Seek (long offset, SeekOrigin origin) {
-			throw new System.NotSupportedException();
-		}
+		public override void Flush ()
+		{
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().FullName);
 
-		public override void SetLength (long value) {
-			throw new System.NotSupportedException();
+			if (CanWrite) {
+				int result = Flush (z_stream);
+				CheckResult (result, "Flush");
+			}
 		}
 
 		public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
 							AsyncCallback cback, object state)
 		{
-			if (!open)
-				throw new ObjectDisposedException ("DeflateStream");
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().FullName);
 
 			if (!CanRead)
 				throw new NotSupportedException ("This stream does not support reading");
@@ -347,8 +281,8 @@ namespace System.IO.Compression {
 		public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
 							AsyncCallback cback, object state)
 		{
-			if (!open)
-				throw new ObjectDisposedException ("DeflateStream");
+			if (disposed)
+				throw new ObjectDisposedException (GetType ().FullName);
 
 			if (!CanWrite)
 				throw new InvalidOperationException ("This stream does not support writing");
@@ -369,7 +303,8 @@ namespace System.IO.Compression {
 			return w.BeginInvoke (buffer, offset, count, cback, state);			
 		}
 
-		public override int EndRead(IAsyncResult async_result) {
+		public override int EndRead(IAsyncResult async_result)
+		{
 			if (async_result == null)
 				throw new ArgumentNullException ("async_result");
 
@@ -401,39 +336,56 @@ namespace System.IO.Compression {
 			return;
 		}
 
+		public override long Seek (long offset, SeekOrigin origin)
+		{
+			throw new NotSupportedException();
+		}
+
+		public override void SetLength (long value)
+		{
+			throw new NotSupportedException();
+		}
+
 		public Stream BaseStream {
-			get {
-				return compressedStream;
-			}
+			get { return base_stream; }
 		}
+
 		public override bool CanRead {
-			get {
-				return open && mode == CompressionMode.Decompress && compressedStream.CanRead;
-			}
+			get { return !disposed && mode == CompressionMode.Decompress && base_stream.CanRead; }
 		}
+
 		public override bool CanSeek {
-			get {
-				return false;
-			}
+			get { return false; }
 		}
+
 		public override bool CanWrite {
-			get {
-				return open && mode == CompressionMode.Compress && compressedStream.CanWrite;
-			}
+			get { return !disposed && mode == CompressionMode.Compress && base_stream.CanWrite; }
 		}
+
 		public override long Length {
-			get {
-				throw new System.NotSupportedException();
-			}
+			get { throw new NotSupportedException(); }
 		}
+
 		public override long Position {
-			get {
-				throw new System.NotSupportedException();
-			}
-			set {
-				throw new System.NotSupportedException();
-			}
+			get { throw new NotSupportedException(); }
+			set { throw new NotSupportedException(); }
 		}
+
+		[DllImport ("MonoPosixHelper")]
+		static extern IntPtr CreateZStream (CompressionMode compress, bool gzip, UnmanagedReadOrWrite feeder);
+
+		[DllImport ("MonoPosixHelper")]
+		static extern int CloseZStream (IntPtr stream);
+
+		[DllImport ("MonoPosixHelper")]
+		static extern int Flush (IntPtr stream);
+
+		[DllImport ("MonoPosixHelper")]
+		static extern int ReadZStream (IntPtr stream, IntPtr buffer, int length);
+
+		[DllImport ("MonoPosixHelper")]
+		static extern int WriteZStream (IntPtr stream, IntPtr buffer, int length);
 	}
 }
 #endif
+

+ 6 - 4
mcs/class/System/System.IO.Compression/GzipStream.cs

@@ -50,11 +50,13 @@ namespace System.IO.Compression {
 
 		protected override void Dispose (bool disposing)
 		{
-			try {
-				base.Dispose (disposing);
-			} finally {
-				deflateStream.Dispose ();
+			if (disposing) {
+				if (deflateStream != null) {
+					deflateStream.Dispose ();
+					deflateStream = null;
+				}
 			}
+			base.Dispose (disposing);
 		}
 
 		public override int Read (byte[] dest, int dest_offset, int count)