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;
38 namespace System.IO.Compression
40 public class DeflateStream : Stream
42 delegate int ReadMethod (byte[] array, int offset, int count);
43 delegate void WriteMethod (byte[] array, int offset, int count);
49 DeflateStreamNative native;
51 public DeflateStream (Stream stream, CompressionMode mode) :
52 this (stream, mode, false, false)
56 public DeflateStream (Stream stream, CompressionMode mode, bool leaveOpen) :
57 this (stream, mode, leaveOpen, false)
61 internal DeflateStream (Stream stream, CompressionMode mode, bool leaveOpen, int windowsBits) :
62 this (stream, mode, leaveOpen, true)
66 internal DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen, bool gzip)
68 if (compressedStream == null)
69 throw new ArgumentNullException ("compressedStream");
71 if (mode != CompressionMode.Compress && mode != CompressionMode.Decompress)
72 throw new ArgumentException ("mode");
74 this.base_stream = compressedStream;
76 this.native = DeflateStreamNative.Create (compressedStream, mode, gzip);
77 if (this.native == null) {
78 throw new NotImplementedException ("Failed to initialize zlib. You probably have an old zlib installed. Version 1.2.0.4 or later is required.");
81 this.leaveOpen = leaveOpen;
84 public DeflateStream (Stream stream, CompressionLevel compressionLevel)
85 : this (stream, compressionLevel, false, false)
89 public DeflateStream (Stream stream, CompressionLevel compressionLevel, bool leaveOpen)
90 : this (stream, compressionLevel, leaveOpen, false)
94 internal DeflateStream (Stream stream, CompressionLevel compressionLevel, bool leaveOpen, int windowsBits)
95 : this (stream, compressionLevel, leaveOpen, true)
99 internal DeflateStream (Stream stream, CompressionLevel compressionLevel, bool leaveOpen, bool gzip)
100 : this (stream, CompressionMode.Compress, leaveOpen, gzip)
104 protected override void Dispose (bool disposing)
106 native.Dispose (disposing);
108 if (disposing && !disposed) {
112 Stream st = base_stream;
119 base.Dispose (disposing);
122 unsafe int ReadInternal (byte[] array, int offset, int count)
127 fixed (byte *b = array) {
128 IntPtr ptr = new IntPtr (b + offset);
129 return native.ReadZStream (ptr, count);
133 internal int ReadCore (Span<byte> destination)
135 throw new NotImplementedException ();
138 public override int Read (byte[] array, int offset, int count)
141 throw new ObjectDisposedException (GetType ().FullName);
143 throw new ArgumentNullException ("Destination array is null.");
145 throw new InvalidOperationException ("Stream does not support reading.");
146 int len = array.Length;
147 if (offset < 0 || count < 0)
148 throw new ArgumentException ("Dest or count is negative.");
150 throw new ArgumentException ("destination offset is beyond array size");
151 if ((offset + count) > len)
152 throw new ArgumentException ("Reading would overrun buffer");
154 return ReadInternal (array, offset, count);
157 unsafe void WriteInternal (byte[] array, int offset, int count)
162 fixed (byte *b = array) {
163 IntPtr ptr = new IntPtr (b + offset);
164 native.WriteZStream (ptr, count);
168 internal void WriteCore (ReadOnlySpan<byte> source)
170 throw new NotImplementedException ();
173 public override void Write (byte[] array, int offset, int count)
176 throw new ObjectDisposedException (GetType ().FullName);
179 throw new ArgumentNullException ("array");
182 throw new ArgumentOutOfRangeException ("offset");
185 throw new ArgumentOutOfRangeException ("count");
188 throw new NotSupportedException ("Stream does not support writing");
190 if (offset > array.Length - count)
191 throw new ArgumentException ("Buffer too small. count/offset wrong.");
193 WriteInternal (array, offset, count);
196 public override void Flush ()
199 throw new ObjectDisposedException (GetType ().FullName);
206 public override IAsyncResult BeginRead (byte [] array, int offset, int count,
207 AsyncCallback asyncCallback, object asyncState)
210 throw new ObjectDisposedException (GetType ().FullName);
213 throw new NotSupportedException ("This stream does not support reading");
216 throw new ArgumentNullException ("array");
219 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
222 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
224 if (count + offset > array.Length)
225 throw new ArgumentException ("Buffer too small. count/offset wrong.");
227 ReadMethod r = new ReadMethod (ReadInternal);
228 return r.BeginInvoke (array, offset, count, asyncCallback, asyncState);
231 public override IAsyncResult BeginWrite (byte [] array, int offset, int count,
232 AsyncCallback asyncCallback, object asyncState)
235 throw new ObjectDisposedException (GetType ().FullName);
238 throw new InvalidOperationException ("This stream does not support writing");
241 throw new ArgumentNullException ("array");
244 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
247 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
249 if (count + offset > array.Length)
250 throw new ArgumentException ("Buffer too small. count/offset wrong.");
252 WriteMethod w = new WriteMethod (WriteInternal);
253 return w.BeginInvoke (array, offset, count, asyncCallback, asyncState);
256 public override int EndRead(IAsyncResult asyncResult)
258 if (asyncResult == null)
259 throw new ArgumentNullException ("asyncResult");
261 AsyncResult ares = asyncResult as AsyncResult;
263 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
265 ReadMethod r = ares.AsyncDelegate as ReadMethod;
267 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
269 return r.EndInvoke (asyncResult);
272 public override void EndWrite (IAsyncResult asyncResult)
274 if (asyncResult == null)
275 throw new ArgumentNullException ("asyncResult");
277 AsyncResult ares = asyncResult as AsyncResult;
279 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
281 WriteMethod w = ares.AsyncDelegate as WriteMethod;
283 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
285 w.EndInvoke (asyncResult);
289 public override long Seek (long offset, SeekOrigin origin)
291 throw new NotSupportedException();
294 public override void SetLength (long value)
296 throw new NotSupportedException();
299 public Stream BaseStream {
300 get { return base_stream; }
303 public override bool CanRead {
304 get { return !disposed && mode == CompressionMode.Decompress && base_stream.CanRead; }
307 public override bool CanSeek {
308 get { return false; }
311 public override bool CanWrite {
312 get { return !disposed && mode == CompressionMode.Compress && base_stream.CanWrite; }
315 public override long Length {
316 get { throw new NotSupportedException(); }
319 public override long Position {
320 get { throw new NotSupportedException(); }
321 set { throw new NotSupportedException(); }
325 class DeflateStreamNative
327 const int BufferSize = 4096;
329 [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
330 delegate int UnmanagedReadOrWrite (IntPtr buffer, int length, IntPtr data);
332 UnmanagedReadOrWrite feeder; // This will be passed to unmanaged code and used there
340 private DeflateStreamNative ()
344 public static DeflateStreamNative Create (Stream compressedStream, CompressionMode mode, bool gzip)
346 var dsn = new DeflateStreamNative ();
347 dsn.data = GCHandle.Alloc (dsn);
348 dsn.feeder = mode == CompressionMode.Compress ? new UnmanagedReadOrWrite (UnmanagedWrite) : new UnmanagedReadOrWrite (UnmanagedRead);
349 dsn.z_stream = CreateZStream (mode, gzip, dsn.feeder, GCHandle.ToIntPtr (dsn.data));
350 if (dsn.z_stream == IntPtr.Zero) {
355 dsn.base_stream = compressedStream;
359 ~DeflateStreamNative ()
364 public void Dispose (bool disposing)
366 if (disposing && !disposed) {
368 GC.SuppressFinalize (this);
372 IntPtr zz = z_stream;
373 z_stream = IntPtr.Zero;
374 if (zz != IntPtr.Zero)
375 CloseZStream (zz); // This will Flush() the remaining output if any
378 if (data.IsAllocated) {
385 var res = Flush (z_stream);
386 CheckResult (res, "Flush");
389 public int ReadZStream (IntPtr buffer, int length)
391 var res = ReadZStream (z_stream, buffer, length);
392 CheckResult (res, "ReadInternal");
396 public void WriteZStream (IntPtr buffer, int length)
398 var res = WriteZStream (z_stream, buffer, length);
399 CheckResult (res, "WriteInternal");
402 [Mono.Util.MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
403 static int UnmanagedRead (IntPtr buffer, int length, IntPtr data)
405 GCHandle s = GCHandle.FromIntPtr (data);
406 var self = s.Target as DeflateStreamNative;
409 return self.UnmanagedRead (buffer, length);
412 int UnmanagedRead (IntPtr buffer, int length)
414 if (io_buffer == null)
415 io_buffer = new byte [BufferSize];
417 int count = Math.Min (length, io_buffer.Length);
418 int n = base_stream.Read (io_buffer, 0, count);
420 Marshal.Copy (io_buffer, 0, buffer, n);
425 [Mono.Util.MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
426 static int UnmanagedWrite (IntPtr buffer, int length, IntPtr data)
428 GCHandle s = GCHandle.FromIntPtr (data);
429 var self = s.Target as DeflateStreamNative;
432 return self.UnmanagedWrite (buffer, length);
435 int UnmanagedWrite (IntPtr buffer, int length)
439 if (io_buffer == null)
440 io_buffer = new byte [BufferSize];
442 int count = Math.Min (length, io_buffer.Length);
443 Marshal.Copy (buffer, io_buffer, 0, count);
444 base_stream.Write (io_buffer, 0, count);
446 buffer = new IntPtr ((byte *) buffer.ToPointer () + count);
454 static void CheckResult (int result, string where)
462 error = "Unknown error"; // Marshal.GetLastWin32() ?
464 case -2: // Z_STREAM_ERROR
465 error = "Internal error";
467 case -3: // Z_DATA_ERROR
468 error = "Corrupted data";
470 case -4: // Z_MEM_ERROR
471 error = "Not enough memory";
473 case -5: // Z_BUF_ERROR
474 error = "Internal error (no progress possible)";
476 case -6: // Z_VERSION_ERROR
477 error = "Invalid version";
480 error = "Invalid argument(s)";
486 error = "Unknown error";
490 throw new IOException (error + " " + where);
493 #if MONOTOUCH || MONODROID
494 const string LIBNAME = "__Internal";
496 const string LIBNAME = "MonoPosixHelper";
500 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
501 static extern IntPtr CreateZStream (CompressionMode compress, bool gzip, UnmanagedReadOrWrite feeder, IntPtr data);
503 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
504 static extern int CloseZStream (IntPtr stream);
506 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
507 static extern int Flush (IntPtr stream);
509 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
510 static extern int ReadZStream (IntPtr stream, IntPtr buffer, int length);
512 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
513 static extern int WriteZStream (IntPtr stream, IntPtr buffer, int length);
515 static IntPtr CreateZStream (CompressionMode compress, bool gzip, UnmanagedReadOrWrite feeder, IntPtr data)
517 throw new PlatformNotSupportedException ();
520 static int CloseZStream (IntPtr stream)
522 throw new PlatformNotSupportedException ();
525 static int Flush (IntPtr stream)
527 throw new PlatformNotSupportedException ();
530 static int ReadZStream (IntPtr stream, IntPtr buffer, int length)
532 throw new PlatformNotSupportedException ();
535 static int WriteZStream (IntPtr stream, IntPtr buffer, int length)
537 throw new PlatformNotSupportedException ();