1 /* -*- Mode: csharp; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 // Christopher James Lahey <clahey@ximian.com>
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 // Marek Safar (marek.safar@gmail.com)
10 // (c) Copyright 2004,2009 Novell, Inc. <http://www.novell.com>
11 // Copyright (C) 2013 Xamarin Inc (http://www.xamarin.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Runtime.InteropServices;
36 using System.Runtime.Remoting.Messaging;
42 namespace System.IO.Compression
44 public class DeflateStream : Stream
46 delegate int ReadMethod (byte[] array, int offset, int count);
47 delegate void WriteMethod (byte[] array, int offset, int count);
53 DeflateStreamNative native;
55 public DeflateStream (Stream compressedStream, CompressionMode mode) :
56 this (compressedStream, mode, false, false)
60 public DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen) :
61 this (compressedStream, mode, leaveOpen, false)
65 internal DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen, bool gzip)
67 if (compressedStream == null)
68 throw new ArgumentNullException ("compressedStream");
70 if (mode != CompressionMode.Compress && mode != CompressionMode.Decompress)
71 throw new ArgumentException ("mode");
73 this.base_stream = compressedStream;
75 this.native = DeflateStreamNative.Create (compressedStream, mode, gzip);
76 if (this.native == null) {
77 throw new NotImplementedException ("Failed to initialize zlib. You probably have an old zlib installed. Version 1.2.0.4 or later is required.");
80 this.leaveOpen = leaveOpen;
85 public DeflateStream (Stream stream, CompressionLevel compressionLevel)
86 : this (stream, CompressionMode.Compress)
88 throw new NotImplementedException ();
92 public DeflateStream (Stream stream, CompressionLevel compressionLevel, bool leaveOpen)
93 : this (stream, CompressionMode.Compress, leaveOpen)
95 throw new NotImplementedException ();
99 protected override void Dispose (bool disposing)
101 native.Dispose (disposing);
103 if (disposing && !disposed) {
107 Stream st = base_stream;
114 base.Dispose (disposing);
117 unsafe int ReadInternal (byte[] array, int offset, int count)
122 fixed (byte *b = array) {
123 IntPtr ptr = new IntPtr (b + offset);
124 return native.ReadZStream (ptr, count);
128 public override int Read (byte[] dest, int dest_offset, int count)
131 throw new ObjectDisposedException (GetType ().FullName);
133 throw new ArgumentNullException ("Destination array is null.");
135 throw new InvalidOperationException ("Stream does not support reading.");
136 int len = dest.Length;
137 if (dest_offset < 0 || count < 0)
138 throw new ArgumentException ("Dest or count is negative.");
139 if (dest_offset > len)
140 throw new ArgumentException ("destination offset is beyond array size");
141 if ((dest_offset + count) > len)
142 throw new ArgumentException ("Reading would overrun buffer");
144 return ReadInternal (dest, dest_offset, count);
147 unsafe void WriteInternal (byte[] array, int offset, int count)
152 fixed (byte *b = array) {
153 IntPtr ptr = new IntPtr (b + offset);
154 native.WriteZStream (ptr, count);
158 public override void Write (byte[] src, int src_offset, int count)
161 throw new ObjectDisposedException (GetType ().FullName);
164 throw new ArgumentNullException ("src");
167 throw new ArgumentOutOfRangeException ("src_offset");
170 throw new ArgumentOutOfRangeException ("count");
173 throw new NotSupportedException ("Stream does not support writing");
175 WriteInternal (src, src_offset, count);
178 public override void Flush ()
181 throw new ObjectDisposedException (GetType ().FullName);
188 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
189 AsyncCallback cback, object state)
192 throw new ObjectDisposedException (GetType ().FullName);
195 throw new NotSupportedException ("This stream does not support reading");
198 throw new ArgumentNullException ("buffer");
201 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
204 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
206 if (count + offset > buffer.Length)
207 throw new ArgumentException ("Buffer too small. count/offset wrong.");
209 ReadMethod r = new ReadMethod (ReadInternal);
210 return r.BeginInvoke (buffer, offset, count, cback, state);
213 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
214 AsyncCallback cback, object state)
217 throw new ObjectDisposedException (GetType ().FullName);
220 throw new InvalidOperationException ("This stream does not support writing");
223 throw new ArgumentNullException ("buffer");
226 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
229 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
231 if (count + offset > buffer.Length)
232 throw new ArgumentException ("Buffer too small. count/offset wrong.");
234 WriteMethod w = new WriteMethod (WriteInternal);
235 return w.BeginInvoke (buffer, offset, count, cback, state);
238 public override int EndRead(IAsyncResult async_result)
240 if (async_result == null)
241 throw new ArgumentNullException ("async_result");
243 AsyncResult ares = async_result as AsyncResult;
245 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
247 ReadMethod r = ares.AsyncDelegate as ReadMethod;
249 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
251 return r.EndInvoke (async_result);
254 public override void EndWrite (IAsyncResult async_result)
256 if (async_result == null)
257 throw new ArgumentNullException ("async_result");
259 AsyncResult ares = async_result as AsyncResult;
261 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
263 WriteMethod w = ares.AsyncDelegate as WriteMethod;
265 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
267 w.EndInvoke (async_result);
271 public override long Seek (long offset, SeekOrigin origin)
273 throw new NotSupportedException();
276 public override void SetLength (long value)
278 throw new NotSupportedException();
281 public Stream BaseStream {
282 get { return base_stream; }
285 public override bool CanRead {
286 get { return !disposed && mode == CompressionMode.Decompress && base_stream.CanRead; }
289 public override bool CanSeek {
290 get { return false; }
293 public override bool CanWrite {
294 get { return !disposed && mode == CompressionMode.Compress && base_stream.CanWrite; }
297 public override long Length {
298 get { throw new NotSupportedException(); }
301 public override long Position {
302 get { throw new NotSupportedException(); }
303 set { throw new NotSupportedException(); }
307 class DeflateStreamNative
309 const int BufferSize = 4096;
311 [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
312 delegate int UnmanagedReadOrWrite (IntPtr buffer, int length, IntPtr data);
314 UnmanagedReadOrWrite feeder; // This will be passed to unmanaged code and used there
322 private DeflateStreamNative ()
326 public static DeflateStreamNative Create (Stream compressedStream, CompressionMode mode, bool gzip)
328 var dsn = new DeflateStreamNative ();
329 dsn.data = GCHandle.Alloc (dsn);
330 dsn.feeder = mode == CompressionMode.Compress ? new UnmanagedReadOrWrite (UnmanagedWrite) : new UnmanagedReadOrWrite (UnmanagedRead);
331 dsn.z_stream = CreateZStream (mode, gzip, dsn.feeder, GCHandle.ToIntPtr (dsn.data));
332 if (dsn.z_stream == IntPtr.Zero) {
337 dsn.base_stream = compressedStream;
341 ~DeflateStreamNative ()
346 public void Dispose (bool disposing)
348 if (disposing && !disposed) {
350 GC.SuppressFinalize (this);
354 IntPtr zz = z_stream;
355 z_stream = IntPtr.Zero;
356 if (zz != IntPtr.Zero)
357 CloseZStream (zz); // This will Flush() the remaining output if any
360 if (data.IsAllocated) {
367 var res = Flush (z_stream);
368 CheckResult (res, "Flush");
371 public int ReadZStream (IntPtr buffer, int length)
373 var res = ReadZStream (z_stream, buffer, length);
374 CheckResult (res, "ReadInternal");
378 public void WriteZStream (IntPtr buffer, int length)
380 var res = WriteZStream (z_stream, buffer, length);
381 CheckResult (res, "WriteInternal");
385 [MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
387 static int UnmanagedRead (IntPtr buffer, int length, IntPtr data)
389 GCHandle s = GCHandle.FromIntPtr (data);
390 var self = s.Target as DeflateStreamNative;
393 return self.UnmanagedRead (buffer, length);
396 int UnmanagedRead (IntPtr buffer, int length)
400 while (length > 0 && n > 0) {
401 if (io_buffer == null)
402 io_buffer = new byte [BufferSize];
404 int count = Math.Min (length, io_buffer.Length);
405 n = base_stream.Read (io_buffer, 0, count);
407 Marshal.Copy (io_buffer, 0, buffer, n);
409 buffer = new IntPtr ((byte *) buffer.ToPointer () + n);
419 [MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
421 static int UnmanagedWrite (IntPtr buffer, int length, IntPtr data)
423 GCHandle s = GCHandle.FromIntPtr (data);
424 var self = s.Target as DeflateStreamNative;
427 return self.UnmanagedWrite (buffer, length);
430 int UnmanagedWrite (IntPtr buffer, int length)
434 if (io_buffer == null)
435 io_buffer = new byte [BufferSize];
437 int count = Math.Min (length, io_buffer.Length);
438 Marshal.Copy (buffer, io_buffer, 0, count);
439 base_stream.Write (io_buffer, 0, count);
441 buffer = new IntPtr ((byte *) buffer.ToPointer () + count);
449 static void CheckResult (int result, string where)
457 error = "Unknown error"; // Marshal.GetLastWin32() ?
459 case -2: // Z_STREAM_ERROR
460 error = "Internal error";
462 case -3: // Z_DATA_ERROR
463 error = "Corrupted data";
465 case -4: // Z_MEM_ERROR
466 error = "Not enough memory";
468 case -5: // Z_BUF_ERROR
469 error = "Internal error (no progress possible)";
471 case -6: // Z_VERSION_ERROR
472 error = "Invalid version";
475 error = "Invalid argument(s)";
481 error = "Unknown error";
485 throw new IOException (error + " " + where);
488 #if MONOTOUCH || MONODROID
489 const string LIBNAME = "__Internal";
491 const string LIBNAME = "MonoPosixHelper";
494 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
495 static extern IntPtr CreateZStream (CompressionMode compress, bool gzip, UnmanagedReadOrWrite feeder, IntPtr data);
497 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
498 static extern int CloseZStream (IntPtr stream);
500 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
501 static extern int Flush (IntPtr stream);
503 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
504 static extern int ReadZStream (IntPtr stream, IntPtr buffer, int length);
506 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
507 static extern int WriteZStream (IntPtr stream, IntPtr buffer, int length);