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)
9 // (c) Copyright 2004,2009 Novell, Inc. <http://www.novell.com>
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Runtime
.InteropServices
;
35 using System
.Runtime
.Remoting
.Messaging
;
41 namespace System
.IO
.Compression
{
42 public class DeflateStream
: Stream
44 const int BufferSize
= 4096;
45 [UnmanagedFunctionPointer (CallingConvention
.Cdecl
)]
46 delegate int UnmanagedReadOrWrite (IntPtr buffer
, int length
, IntPtr data
);
47 delegate int ReadMethod (byte[] array
, int offset
, int count
);
48 delegate void WriteMethod (byte[] array
, int offset
, int count
);
54 UnmanagedReadOrWrite feeder
; // This will be passed to unmanaged code and used there
60 public DeflateStream (Stream compressedStream
, CompressionMode mode
) :
61 this (compressedStream
, mode
, false, false)
65 public DeflateStream (Stream compressedStream
, CompressionMode mode
, bool leaveOpen
) :
66 this (compressedStream
, mode
, leaveOpen
, false)
70 internal DeflateStream (Stream compressedStream
, CompressionMode mode
, bool leaveOpen
, bool gzip
)
72 if (compressedStream
== null)
73 throw new ArgumentNullException ("compressedStream");
75 if (mode
!= CompressionMode
.Compress
&& mode
!= CompressionMode
.Decompress
)
76 throw new ArgumentException ("mode");
78 this.data
= GCHandle
.Alloc (this);
79 this.base_stream
= compressedStream
;
80 this.feeder
= (mode
== CompressionMode
.Compress
) ? new UnmanagedReadOrWrite (UnmanagedWrite
) :
81 new UnmanagedReadOrWrite (UnmanagedRead
);
82 this.z_stream
= CreateZStream (mode
, gzip
, feeder
, GCHandle
.ToIntPtr (data
));
83 if (z_stream
== IntPtr
.Zero
) {
84 this.base_stream
= null;
86 throw new NotImplementedException ("Failed to initialize zlib. You probably have an old zlib installed. Version 1.2.0.4 or later is required.");
89 this.leaveOpen
= leaveOpen
;
92 protected override void Dispose (bool disposing
)
94 if (disposing
&& !disposed
) {
97 z_stream
= IntPtr
.Zero
;
99 if (zz
!= IntPtr
.Zero
)
100 res
= CloseZStream (zz
); // This will Flush() the remaining output if any
104 Stream st
= base_stream
;
109 CheckResult (res
, "Dispose");
112 if (data
.IsAllocated
) {
114 data
= new GCHandle ();
117 base.Dispose (disposing
);
121 [MonoPInvokeCallback (typeof (UnmanagedReadOrWrite
))]
123 static int UnmanagedRead (IntPtr buffer
, int length
, IntPtr data
)
125 GCHandle s
= GCHandle
.FromIntPtr (data
);
126 var self
= s
.Target
as DeflateStream
;
129 return self
.UnmanagedRead (buffer
, length
);
132 int UnmanagedRead (IntPtr buffer
, int length
)
136 while (length
> 0 && n
> 0) {
137 if (io_buffer
== null)
138 io_buffer
= new byte [BufferSize
];
140 int count
= Math
.Min (length
, io_buffer
.Length
);
141 n
= base_stream
.Read (io_buffer
, 0, count
);
143 Marshal
.Copy (io_buffer
, 0, buffer
, n
);
145 buffer
= new IntPtr ((byte *) buffer
.ToPointer () + n
);
155 [MonoPInvokeCallback (typeof (UnmanagedReadOrWrite
))]
157 static int UnmanagedWrite (IntPtr buffer
, int length
, IntPtr data
)
159 GCHandle s
= GCHandle
.FromIntPtr (data
);
160 var self
= s
.Target
as DeflateStream
;
163 return self
.UnmanagedWrite (buffer
, length
);
166 int UnmanagedWrite (IntPtr buffer
, int length
)
170 if (io_buffer
== null)
171 io_buffer
= new byte [BufferSize
];
173 int count
= Math
.Min (length
, io_buffer
.Length
);
174 Marshal
.Copy (buffer
, io_buffer
, 0, count
);
175 base_stream
.Write (io_buffer
, 0, count
);
177 buffer
= new IntPtr ((byte *) buffer
.ToPointer () + count
);
185 unsafe int ReadInternal (byte[] array
, int offset
, int count
)
191 fixed (byte *b
= array
) {
192 IntPtr ptr
= new IntPtr (b
+ offset
);
193 result
= ReadZStream (z_stream
, ptr
, count
);
195 CheckResult (result
, "ReadInternal");
199 public override int Read (byte[] dest
, int dest_offset
, int count
)
202 throw new ObjectDisposedException (GetType ().FullName
);
204 throw new ArgumentNullException ("Destination array is null.");
206 throw new InvalidOperationException ("Stream does not support reading.");
207 int len
= dest
.Length
;
208 if (dest_offset
< 0 || count
< 0)
209 throw new ArgumentException ("Dest or count is negative.");
210 if (dest_offset
> len
)
211 throw new ArgumentException ("destination offset is beyond array size");
212 if ((dest_offset
+ count
) > len
)
213 throw new ArgumentException ("Reading would overrun buffer");
215 return ReadInternal (dest
, dest_offset
, count
);
218 unsafe void WriteInternal (byte[] array
, int offset
, int count
)
224 fixed (byte *b
= array
) {
225 IntPtr ptr
= new IntPtr (b
+ offset
);
226 result
= WriteZStream (z_stream
, ptr
, count
);
228 CheckResult (result
, "WriteInternal");
231 public override void Write (byte[] src
, int src_offset
, int count
)
234 throw new ObjectDisposedException (GetType ().FullName
);
237 throw new ArgumentNullException ("src");
240 throw new ArgumentOutOfRangeException ("src_offset");
243 throw new ArgumentOutOfRangeException ("count");
246 throw new NotSupportedException ("Stream does not support writing");
248 WriteInternal (src
, src_offset
, count
);
251 static void CheckResult (int result
, string where
)
259 error
= "Unknown error"; // Marshal.GetLastWin32() ?
261 case -2: // Z_STREAM_ERROR
262 error
= "Internal error";
264 case -3: // Z_DATA_ERROR
265 error
= "Corrupted data";
267 case -4: // Z_MEM_ERROR
268 error
= "Not enough memory";
270 case -5: // Z_BUF_ERROR
271 error
= "Internal error (no progress possible)";
273 case -6: // Z_VERSION_ERROR
274 error
= "Invalid version";
277 error
= "Invalid argument(s)";
283 error
= "Unknown error";
287 throw new IOException (error
+ " " + where
);
290 public override void Flush ()
293 throw new ObjectDisposedException (GetType ().FullName
);
296 int result
= Flush (z_stream
);
297 CheckResult (result
, "Flush");
301 public override IAsyncResult
BeginRead (byte [] buffer
, int offset
, int count
,
302 AsyncCallback cback
, object state
)
305 throw new ObjectDisposedException (GetType ().FullName
);
308 throw new NotSupportedException ("This stream does not support reading");
311 throw new ArgumentNullException ("buffer");
314 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
317 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
319 if (count
+ offset
> buffer
.Length
)
320 throw new ArgumentException ("Buffer too small. count/offset wrong.");
322 ReadMethod r
= new ReadMethod (ReadInternal
);
323 return r
.BeginInvoke (buffer
, offset
, count
, cback
, state
);
326 public override IAsyncResult
BeginWrite (byte [] buffer
, int offset
, int count
,
327 AsyncCallback cback
, object state
)
330 throw new ObjectDisposedException (GetType ().FullName
);
333 throw new InvalidOperationException ("This stream does not support writing");
336 throw new ArgumentNullException ("buffer");
339 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
342 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
344 if (count
+ offset
> buffer
.Length
)
345 throw new ArgumentException ("Buffer too small. count/offset wrong.");
347 WriteMethod w
= new WriteMethod (WriteInternal
);
348 return w
.BeginInvoke (buffer
, offset
, count
, cback
, state
);
351 public override int EndRead(IAsyncResult async_result
)
353 if (async_result
== null)
354 throw new ArgumentNullException ("async_result");
356 AsyncResult ares
= async_result
as AsyncResult
;
358 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
360 ReadMethod r
= ares
.AsyncDelegate
as ReadMethod
;
362 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
364 return r
.EndInvoke (async_result
);
367 public override void EndWrite (IAsyncResult async_result
)
369 if (async_result
== null)
370 throw new ArgumentNullException ("async_result");
372 AsyncResult ares
= async_result
as AsyncResult
;
374 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
376 WriteMethod w
= ares
.AsyncDelegate
as WriteMethod
;
378 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
380 w
.EndInvoke (async_result
);
384 public override long Seek (long offset
, SeekOrigin origin
)
386 throw new NotSupportedException();
389 public override void SetLength (long value)
391 throw new NotSupportedException();
394 public Stream BaseStream
{
395 get { return base_stream; }
398 public override bool CanRead
{
399 get { return !disposed && mode == CompressionMode.Decompress && base_stream.CanRead; }
402 public override bool CanSeek
{
403 get { return false; }
406 public override bool CanWrite
{
407 get { return !disposed && mode == CompressionMode.Compress && base_stream.CanWrite; }
410 public override long Length
{
411 get { throw new NotSupportedException(); }
414 public override long Position
{
415 get { throw new NotSupportedException(); }
416 set { throw new NotSupportedException(); }
420 const string LIBNAME
= "__Internal";
422 const string LIBNAME
= "MonoPosixHelper";
425 [DllImport (LIBNAME
, CallingConvention
=CallingConvention
.Cdecl
)]
426 static extern IntPtr
CreateZStream (CompressionMode compress
, bool gzip
, UnmanagedReadOrWrite feeder
, IntPtr data
);
428 [DllImport (LIBNAME
, CallingConvention
=CallingConvention
.Cdecl
)]
429 static extern int CloseZStream (IntPtr stream
);
431 [DllImport (LIBNAME
, CallingConvention
=CallingConvention
.Cdecl
)]
432 static extern int Flush (IntPtr stream
);
434 [DllImport (LIBNAME
, CallingConvention
=CallingConvention
.Cdecl
)]
435 static extern int ReadZStream (IntPtr stream
, IntPtr buffer
, int length
);
437 [DllImport (LIBNAME
, CallingConvention
=CallingConvention
.Cdecl
)]
438 static extern int WriteZStream (IntPtr stream
, IntPtr buffer
, int length
);