Rename the mobile_static profile to aot_only
[mono.git] / mcs / class / System / System.IO.Compression / DeflateStream.cs
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 //  Marek Safar (marek.safar@gmail.com)
9 //
10 // (c) Copyright 2004,2009 Novell, Inc. <http://www.novell.com>
11 // Copyright (C) 2013 Xamarin Inc (http://www.xamarin.com)
12 //
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:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
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.
31 //
32
33 using System;
34 using System.IO;
35 using System.Runtime.InteropServices;
36 using System.Runtime.Remoting.Messaging;
37
38 namespace System.IO.Compression
39 {
40         public class DeflateStream : Stream
41         {
42                 delegate int ReadMethod (byte[] array, int offset, int count);
43                 delegate void WriteMethod (byte[] array, int offset, int count);
44
45                 Stream base_stream;
46                 CompressionMode mode;
47                 bool leaveOpen;
48                 bool disposed;
49                 DeflateStreamNative native;
50
51                 public DeflateStream (Stream stream, CompressionMode mode) :
52                         this (stream, mode, false, false)
53                 {
54                 }
55
56                 public DeflateStream (Stream stream, CompressionMode mode, bool leaveOpen) :
57                         this (stream, mode, leaveOpen, false)
58                 {
59                 }
60
61                 internal DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen, bool gzip)
62                 {
63                         if (compressedStream == null)
64                                 throw new ArgumentNullException ("compressedStream");
65
66                         if (mode != CompressionMode.Compress && mode != CompressionMode.Decompress)
67                                 throw new ArgumentException ("mode");
68
69                         this.base_stream = compressedStream;
70
71                         this.native = DeflateStreamNative.Create (compressedStream, mode, gzip);
72                         if (this.native == null) {
73                                 throw new NotImplementedException ("Failed to initialize zlib. You probably have an old zlib installed. Version 1.2.0.4 or later is required.");
74                         }
75                         this.mode = mode;
76                         this.leaveOpen = leaveOpen;
77                 }
78                 
79                 public DeflateStream (Stream stream, CompressionLevel compressionLevel)
80                         : this (stream, compressionLevel, false, false)
81                 {
82                 }
83                 
84                 public DeflateStream (Stream stream, CompressionLevel compressionLevel, bool leaveOpen)
85                         : this (stream, compressionLevel, leaveOpen, false)
86                 {
87                 }
88
89                 internal DeflateStream (Stream stream, CompressionLevel compressionLevel, bool leaveOpen, bool gzip)
90                         : this (stream, CompressionMode.Compress, leaveOpen, gzip)
91                 {
92                 }               
93
94                 protected override void Dispose (bool disposing)
95                 {
96                         native.Dispose (disposing);
97
98                         if (disposing && !disposed) {
99                                 disposed = true;
100
101                                 if (!leaveOpen) {
102                                         Stream st = base_stream;
103                                         if (st != null)
104                                                 st.Close ();
105                                         base_stream = null;
106                                 }
107                         }
108
109                         base.Dispose (disposing);
110                 }
111
112                 unsafe int ReadInternal (byte[] array, int offset, int count)
113                 {
114                         if (count == 0)
115                                 return 0;
116
117                         fixed (byte *b = array) {
118                                 IntPtr ptr = new IntPtr (b + offset);
119                                 return native.ReadZStream (ptr, count);
120                         }
121                 }
122
123                 public override int Read (byte[] array, int offset, int count)
124                 {
125                         if (disposed)
126                                 throw new ObjectDisposedException (GetType ().FullName);
127                         if (array == null)
128                                 throw new ArgumentNullException ("Destination array is null.");
129                         if (!CanRead)
130                                 throw new InvalidOperationException ("Stream does not support reading.");
131                         int len = array.Length;
132                         if (offset < 0 || count < 0)
133                                 throw new ArgumentException ("Dest or count is negative.");
134                         if (offset > len)
135                                 throw new ArgumentException ("destination offset is beyond array size");
136                         if ((offset + count) > len)
137                                 throw new ArgumentException ("Reading would overrun buffer");
138
139                         return ReadInternal (array, offset, count);
140                 }
141
142                 unsafe void WriteInternal (byte[] array, int offset, int count)
143                 {
144                         if (count == 0)
145                                 return;
146
147                         fixed (byte *b = array) {
148                                 IntPtr ptr = new IntPtr (b + offset);
149                                 native.WriteZStream (ptr, count);
150                         }
151                 }
152
153                 public override void Write (byte[] array, int offset, int count)
154                 {
155                         if (disposed)
156                                 throw new ObjectDisposedException (GetType ().FullName);
157
158                         if (array == null)
159                                 throw new ArgumentNullException ("array");
160
161                         if (offset < 0)
162                                 throw new ArgumentOutOfRangeException ("offset");
163
164                         if (count < 0)
165                                 throw new ArgumentOutOfRangeException ("count");
166
167                         if (!CanWrite)
168                                 throw new NotSupportedException ("Stream does not support writing");
169
170                         if (offset > array.Length - count)
171                                 throw new ArgumentException ("Buffer too small. count/offset wrong.");
172
173                         WriteInternal (array, offset, count);
174                 }
175
176                 public override void Flush ()
177                 {
178                         if (disposed)
179                                 throw new ObjectDisposedException (GetType ().FullName);
180
181                         if (CanWrite) {
182                                 native.Flush ();
183                         }
184                 }
185
186                 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
187                                                         AsyncCallback cback, object state)
188                 {
189                         if (disposed)
190                                 throw new ObjectDisposedException (GetType ().FullName);
191
192                         if (!CanRead)
193                                 throw new NotSupportedException ("This stream does not support reading");
194
195                         if (buffer == null)
196                                 throw new ArgumentNullException ("buffer");
197
198                         if (count < 0)
199                                 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
200
201                         if (offset < 0)
202                                 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
203
204                         if (count + offset > buffer.Length)
205                                 throw new ArgumentException ("Buffer too small. count/offset wrong.");
206
207                         ReadMethod r = new ReadMethod (ReadInternal);
208                         return r.BeginInvoke (buffer, offset, count, cback, state);
209                 }
210
211                 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
212                                                         AsyncCallback cback, object state)
213                 {
214                         if (disposed)
215                                 throw new ObjectDisposedException (GetType ().FullName);
216
217                         if (!CanWrite)
218                                 throw new InvalidOperationException ("This stream does not support writing");
219
220                         if (buffer == null)
221                                 throw new ArgumentNullException ("buffer");
222
223                         if (count < 0)
224                                 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
225
226                         if (offset < 0)
227                                 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
228
229                         if (count + offset > buffer.Length)
230                                 throw new ArgumentException ("Buffer too small. count/offset wrong.");
231
232                         WriteMethod w = new WriteMethod (WriteInternal);
233                         return w.BeginInvoke (buffer, offset, count, cback, state);                     
234                 }
235
236                 public override int EndRead(IAsyncResult async_result)
237                 {
238                         if (async_result == null)
239                                 throw new ArgumentNullException ("async_result");
240
241                         AsyncResult ares = async_result as AsyncResult;
242                         if (ares == null)
243                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
244
245                         ReadMethod r = ares.AsyncDelegate as ReadMethod;
246                         if (r == null)
247                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
248
249                         return r.EndInvoke (async_result);
250                 }
251
252                 public override void EndWrite (IAsyncResult async_result)
253                 {
254                         if (async_result == null)
255                                 throw new ArgumentNullException ("async_result");
256
257                         AsyncResult ares = async_result as AsyncResult;
258                         if (ares == null)
259                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
260
261                         WriteMethod w = ares.AsyncDelegate as WriteMethod;
262                         if (w == null)
263                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
264
265                         w.EndInvoke (async_result);
266                         return;
267                 }
268
269                 public override long Seek (long offset, SeekOrigin origin)
270                 {
271                         throw new NotSupportedException();
272                 }
273
274                 public override void SetLength (long value)
275                 {
276                         throw new NotSupportedException();
277                 }
278
279                 public Stream BaseStream {
280                         get { return base_stream; }
281                 }
282
283                 public override bool CanRead {
284                         get { return !disposed && mode == CompressionMode.Decompress && base_stream.CanRead; }
285                 }
286
287                 public override bool CanSeek {
288                         get { return false; }
289                 }
290
291                 public override bool CanWrite {
292                         get { return !disposed && mode == CompressionMode.Compress && base_stream.CanWrite; }
293                 }
294
295                 public override long Length {
296                         get { throw new NotSupportedException(); }
297                 }
298
299                 public override long Position {
300                         get { throw new NotSupportedException(); }
301                         set { throw new NotSupportedException(); }
302                 }
303         }
304
305         class DeflateStreamNative
306         {
307                 const int BufferSize = 4096;
308
309                 [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
310                 delegate int UnmanagedReadOrWrite (IntPtr buffer, int length, IntPtr data);
311
312                 UnmanagedReadOrWrite feeder; // This will be passed to unmanaged code and used there
313
314                 Stream base_stream;
315                 IntPtr z_stream;
316                 GCHandle data;
317                 bool disposed;
318                 byte [] io_buffer;
319
320                 private DeflateStreamNative ()
321                 {
322                 }
323
324                 public static DeflateStreamNative Create (Stream compressedStream, CompressionMode mode, bool gzip)
325                 {
326                         var dsn = new DeflateStreamNative ();
327                         dsn.data = GCHandle.Alloc (dsn);
328                         dsn.feeder = mode == CompressionMode.Compress ? new UnmanagedReadOrWrite (UnmanagedWrite) : new UnmanagedReadOrWrite (UnmanagedRead);
329                         dsn.z_stream = CreateZStream (mode, gzip, dsn.feeder, GCHandle.ToIntPtr (dsn.data));
330                         if (dsn.z_stream == IntPtr.Zero) {
331                                 dsn.Dispose (true);
332                                 return null;
333                         }
334
335                         dsn.base_stream = compressedStream;
336                         return dsn;
337                 }
338
339                 ~DeflateStreamNative ()
340                 {
341                         Dispose (false);
342                 }
343
344                 public void Dispose (bool disposing)
345                 {
346                         if (disposing && !disposed) {
347                                 disposed = true;
348                                 GC.SuppressFinalize (this);
349                         
350                                 io_buffer = null;
351                         
352                                 IntPtr zz = z_stream;
353                                 z_stream = IntPtr.Zero;
354                                 if (zz != IntPtr.Zero)
355                                         CloseZStream (zz); // This will Flush() the remaining output if any
356                         }
357
358                         if (data.IsAllocated) {
359                                 data.Free ();
360                         }
361                 }
362
363                 public void Flush ()
364                 {
365                         var res = Flush (z_stream);
366                         CheckResult (res, "Flush");
367                 }
368
369                 public int ReadZStream (IntPtr buffer, int length)
370                 {
371                         var res = ReadZStream (z_stream, buffer, length);
372                         CheckResult (res, "ReadInternal");
373                         return res;
374                 }
375
376                 public void WriteZStream (IntPtr buffer, int length)
377                 {
378                         var res = WriteZStream (z_stream, buffer, length);
379                         CheckResult (res, "WriteInternal");
380                 }
381
382 #if MONOTOUCH || FULL_AOT_RUNTIME
383                 [Mono.Util.MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
384 #endif
385                 static int UnmanagedRead (IntPtr buffer, int length, IntPtr data)
386                 {
387                         GCHandle s = GCHandle.FromIntPtr (data);
388                         var self = s.Target as DeflateStreamNative;
389                         if (self == null)
390                                 return -1;
391                         return self.UnmanagedRead (buffer, length);
392                 }
393
394                 int UnmanagedRead (IntPtr buffer, int length)
395                 {
396                         if (io_buffer == null)
397                                 io_buffer = new byte [BufferSize];
398
399                         int count = Math.Min (length, io_buffer.Length);
400                         int n = base_stream.Read (io_buffer, 0, count);
401                         if (n > 0)
402                                 Marshal.Copy (io_buffer, 0, buffer, n);
403
404                         return n;
405                 }
406
407 #if MONOTOUCH || FULL_AOT_RUNTIME
408                 [Mono.Util.MonoPInvokeCallback (typeof (UnmanagedReadOrWrite))]
409 #endif
410                 static int UnmanagedWrite (IntPtr buffer, int length, IntPtr data)
411                 {
412                         GCHandle s = GCHandle.FromIntPtr (data);
413                         var self = s.Target as DeflateStreamNative;
414                         if (self == null)
415                                 return -1;
416                         return self.UnmanagedWrite (buffer, length);
417                 }
418
419                 int UnmanagedWrite (IntPtr buffer, int length)
420                 {
421                         int total = 0;
422                         while (length > 0) {
423                                 if (io_buffer == null)
424                                         io_buffer = new byte [BufferSize];
425
426                                 int count = Math.Min (length, io_buffer.Length);
427                                 Marshal.Copy (buffer, io_buffer, 0, count);
428                                 base_stream.Write (io_buffer, 0, count);
429                                 unsafe {
430                                         buffer = new IntPtr ((byte *) buffer.ToPointer () + count);
431                                 }
432                                 length -= count;
433                                 total += count;
434                         }
435                         return total;
436                 }
437
438                 static void CheckResult (int result, string where)
439                 {
440                         if (result >= 0)
441                                 return;
442
443                         string error;
444                         switch (result) {
445                         case -1: // Z_ERRNO
446                                 error = "Unknown error"; // Marshal.GetLastWin32() ?
447                                 break;
448                         case -2: // Z_STREAM_ERROR
449                                 error = "Internal error";
450                                 break;
451                         case -3: // Z_DATA_ERROR
452                                 error = "Corrupted data";
453                                 break;
454                         case -4: // Z_MEM_ERROR
455                                 error = "Not enough memory";
456                                 break;
457                         case -5: // Z_BUF_ERROR
458                                 error = "Internal error (no progress possible)";
459                                 break;
460                         case -6: // Z_VERSION_ERROR
461                                 error = "Invalid version";
462                                 break;
463                         case -10:
464                                 error = "Invalid argument(s)";
465                                 break;
466                         case -11:
467                                 error = "IO error";
468                                 break;
469                         default:
470                                 error = "Unknown error";
471                                 break;
472                         }
473
474                         throw new IOException (error + " " + where);
475                 }
476
477 #if MONOTOUCH || MONODROID
478                 const string LIBNAME = "__Internal";
479 #else
480                 const string LIBNAME = "MonoPosixHelper";
481 #endif
482
483                 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
484                 static extern IntPtr CreateZStream (CompressionMode compress, bool gzip, UnmanagedReadOrWrite feeder, IntPtr data);
485
486                 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
487                 static extern int CloseZStream (IntPtr stream);
488
489                 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
490                 static extern int Flush (IntPtr stream);
491
492                 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
493                 static extern int ReadZStream (IntPtr stream, IntPtr buffer, int length);
494
495                 [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
496                 static extern int WriteZStream (IntPtr stream, IntPtr buffer, int length);
497         }
498 }
499