add ISafeSerializationData
[mcs.git] / class / System / System.IO.Compression / DeflateStream.cs
blob5d484120745ac6c5dc46e8d5eac77f33b47ea855
1 /* -*- Mode: csharp; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 //
3 // DeflateStream.cs
4 //
5 // Authors:
6 // Christopher James Lahey <clahey@ximian.com>
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
31 #if NET_2_0
32 using System;
33 using System.IO;
34 using System.Runtime.InteropServices;
35 using System.Runtime.Remoting.Messaging;
37 #if MONOTOUCH
38 using MonoTouch;
39 #endif
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);
50 Stream base_stream;
51 CompressionMode mode;
52 bool leaveOpen;
53 bool disposed;
54 UnmanagedReadOrWrite feeder; // This will be passed to unmanaged code and used there
55 IntPtr z_stream;
56 byte [] io_buffer;
58 GCHandle data;
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;
85 this.feeder = 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.");
88 this.mode = mode;
89 this.leaveOpen = leaveOpen;
92 protected override void Dispose (bool disposing)
94 if (disposing && !disposed) {
95 disposed = true;
96 IntPtr zz = z_stream;
97 z_stream = IntPtr.Zero;
98 int res = 0;
99 if (zz != IntPtr.Zero)
100 res = CloseZStream (zz); // This will Flush() the remaining output if any
102 io_buffer = null;
103 if (!leaveOpen) {
104 Stream st = base_stream;
105 if (st != null)
106 st.Close ();
107 base_stream = null;
109 CheckResult (res, "Dispose");
112 if (data.IsAllocated) {
113 data.Free ();
114 data = new GCHandle ();
117 base.Dispose (disposing);
120 #if MONOTOUCH
121 [MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
122 #endif
123 static int UnmanagedRead (IntPtr buffer, int length, IntPtr data)
125 GCHandle s = GCHandle.FromIntPtr (data);
126 var self = s.Target as DeflateStream;
127 if (self == null)
128 return -1;
129 return self.UnmanagedRead (buffer, length);
132 int UnmanagedRead (IntPtr buffer, int length)
134 int total = 0;
135 int n = 1;
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);
142 if (n > 0) {
143 Marshal.Copy (io_buffer, 0, buffer, n);
144 unsafe {
145 buffer = new IntPtr ((byte *) buffer.ToPointer () + n);
147 length -= n;
148 total += n;
151 return total;
154 #if MONOTOUCH
155 [MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
156 #endif
157 static int UnmanagedWrite (IntPtr buffer, int length, IntPtr data)
159 GCHandle s = GCHandle.FromIntPtr (data);
160 var self = s.Target as DeflateStream;
161 if (self == null)
162 return -1;
163 return self.UnmanagedWrite (buffer, length);
166 int UnmanagedWrite (IntPtr buffer, int length)
168 int total = 0;
169 while (length > 0) {
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);
176 unsafe {
177 buffer = new IntPtr ((byte *) buffer.ToPointer () + count);
179 length -= count;
180 total += count;
182 return total;
185 unsafe int ReadInternal (byte[] array, int offset, int count)
187 if (count == 0)
188 return 0;
190 int result = 0;
191 fixed (byte *b = array) {
192 IntPtr ptr = new IntPtr (b + offset);
193 result = ReadZStream (z_stream, ptr, count);
195 CheckResult (result, "ReadInternal");
196 return result;
199 public override int Read (byte[] dest, int dest_offset, int count)
201 if (disposed)
202 throw new ObjectDisposedException (GetType ().FullName);
203 if (dest == null)
204 throw new ArgumentNullException ("Destination array is null.");
205 if (!CanRead)
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)
220 if (count == 0)
221 return;
223 int result = 0;
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)
233 if (disposed)
234 throw new ObjectDisposedException (GetType ().FullName);
236 if (src == null)
237 throw new ArgumentNullException ("src");
239 if (src_offset < 0)
240 throw new ArgumentOutOfRangeException ("src_offset");
242 if (count < 0)
243 throw new ArgumentOutOfRangeException ("count");
245 if (!CanWrite)
246 throw new NotSupportedException ("Stream does not support writing");
248 WriteInternal (src, src_offset, count);
251 static void CheckResult (int result, string where)
253 if (result >= 0)
254 return;
256 string error;
257 switch (result) {
258 case -1: // Z_ERRNO
259 error = "Unknown error"; // Marshal.GetLastWin32() ?
260 break;
261 case -2: // Z_STREAM_ERROR
262 error = "Internal error";
263 break;
264 case -3: // Z_DATA_ERROR
265 error = "Corrupted data";
266 break;
267 case -4: // Z_MEM_ERROR
268 error = "Not enough memory";
269 break;
270 case -5: // Z_BUF_ERROR
271 error = "Internal error (no progress possible)";
272 break;
273 case -6: // Z_VERSION_ERROR
274 error = "Invalid version";
275 break;
276 case -10:
277 error = "Invalid argument(s)";
278 break;
279 case -11:
280 error = "IO error";
281 break;
282 default:
283 error = "Unknown error";
284 break;
287 throw new IOException (error + " " + where);
290 public override void Flush ()
292 if (disposed)
293 throw new ObjectDisposedException (GetType ().FullName);
295 if (CanWrite) {
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)
304 if (disposed)
305 throw new ObjectDisposedException (GetType ().FullName);
307 if (!CanRead)
308 throw new NotSupportedException ("This stream does not support reading");
310 if (buffer == null)
311 throw new ArgumentNullException ("buffer");
313 if (count < 0)
314 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
316 if (offset < 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)
329 if (disposed)
330 throw new ObjectDisposedException (GetType ().FullName);
332 if (!CanWrite)
333 throw new InvalidOperationException ("This stream does not support writing");
335 if (buffer == null)
336 throw new ArgumentNullException ("buffer");
338 if (count < 0)
339 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
341 if (offset < 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;
357 if (ares == null)
358 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
360 ReadMethod r = ares.AsyncDelegate as ReadMethod;
361 if (r == null)
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;
373 if (ares == null)
374 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
376 WriteMethod w = ares.AsyncDelegate as WriteMethod;
377 if (w == null)
378 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
380 w.EndInvoke (async_result);
381 return;
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(); }
419 #if MONOTOUCH
420 const string LIBNAME = "__Internal";
421 #else
422 const string LIBNAME = "MonoPosixHelper";
423 #endif
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);
441 #endif