Wed Feb 24 15:47:16 CET 2010 Paolo Molaro <lupus@ximian.com>
[mono.git] / support / zlib-helper.c
1 /*
2  * Used by System.IO.Compression.DeflateStream
3  *
4  * Author:
5  *   Gonzalo Paniagua Javier (gonzalo@novell.com)
6  *
7  * (c) Copyright 2009 Novell, Inc.
8  */
9 #include <config.h>
10 #if defined (HAVE_ZLIB)
11 #include <zlib.h>
12 #else
13 #include "zlib.h"
14 #endif
15
16 #include <glib.h>
17 #include <string.h>
18 #include <stdlib.h>
19
20 #ifndef TRUE
21 #define FALSE 0
22 #define TRUE 1
23 #endif
24
25 #define BUFFER_SIZE 4096
26 #define ARGUMENT_ERROR -10
27 #define IO_ERROR -11
28
29 typedef gint (*read_write_func) (guchar *buffer, gint length, void *gchandle);
30 struct _ZStream {
31         z_stream *stream;
32         guchar *buffer;
33         read_write_func func;
34         void *gchandle;
35         guchar compress;
36         guchar eof;
37 };
38 typedef struct _ZStream ZStream;
39
40 ZStream *CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle);
41 gint CloseZStream (ZStream *zstream);
42 gint Flush (ZStream *stream);
43 gint ReadZStream (ZStream *stream, guchar *buffer, gint length);
44 gint WriteZStream (ZStream *stream, guchar *buffer, gint length);
45
46 static void *
47 z_alloc (void *opaque, gsize nitems, gsize item_size)
48 {
49         return g_malloc0 (nitems * item_size);
50 }
51
52 static void
53 z_free (void *opaque, void *ptr)
54 {
55         g_free (ptr);
56 }
57
58 ZStream *
59 CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle)
60 {
61         z_stream *z;
62         gint retval;
63         ZStream *result;
64
65         if (func == NULL)
66                 return NULL;
67
68 #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204)
69         /* Older versions of zlib do not support raw deflate or gzip */
70         return NULL;
71 #endif
72
73         z = g_new0 (z_stream, 1);
74         if (compress) {
75                 retval = deflateInit2 (z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, gzip ? 31 : -15, 8, Z_DEFAULT_STRATEGY);
76         } else {
77                 retval = inflateInit2 (z, gzip ? 31 : -15);
78         }
79
80         if (retval != Z_OK) {
81                 g_free (z);
82                 return NULL;
83         }
84         z->zalloc = z_alloc;
85         z->zfree = z_free;
86         result = g_new0 (ZStream, 1);
87         result->stream = z;
88         result->func = func;
89         result->gchandle = gchandle;
90         result->compress = compress;
91         result->buffer = g_new (guchar, BUFFER_SIZE);
92         return result;
93 }
94
95 gint
96 CloseZStream (ZStream *zstream)
97 {
98         gint status;
99         gint flush_status;
100
101         if (zstream == NULL)
102                 return ARGUMENT_ERROR;
103
104         status = 0;
105         if (zstream->compress) {
106                 if (zstream->stream->total_out) {
107                         do {
108                                 status = deflate (zstream->stream, Z_FINISH);
109                                 flush_status = Flush (zstream);
110                         } while (status == Z_OK); /* We want Z_STREAM_END or error here here */
111                         if (status == Z_STREAM_END)
112                                 status = flush_status;
113                 }
114                 deflateEnd (zstream->stream);
115         } else {
116                 inflateEnd (zstream->stream);
117         }
118         g_free (zstream->buffer);
119         g_free (zstream->stream);
120         memset (zstream, 0, sizeof (ZStream));
121         g_free (zstream);
122         return status;
123 }
124
125 static gint
126 write_to_managed (ZStream *stream)
127 {
128         gint n;
129         z_stream *zs;
130
131         zs = stream->stream;
132         if (zs->avail_out != BUFFER_SIZE) {
133                 n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out, stream->gchandle);
134                 zs->next_out = stream->buffer;
135                 zs->avail_out =  BUFFER_SIZE;
136                 if (n < 0)
137                         return IO_ERROR;
138         }
139         return 0;
140 }
141
142 gint
143 Flush (ZStream *stream)
144 {
145         if (!stream->compress)
146                 return 0;
147
148         return write_to_managed (stream);
149 }
150
151 gint
152 ReadZStream (ZStream *stream, guchar *buffer, gint length)
153 {
154         gint n;
155         gint status;
156         z_stream *zs;
157
158         if (stream == NULL || buffer == NULL || length < 0)
159                 return ARGUMENT_ERROR;
160
161         if (stream->eof)
162                 return 0;
163
164         zs = stream->stream;
165         zs->next_out = buffer;
166         zs->avail_out = length;
167         while (zs->avail_out > 0) {
168                 if (zs->avail_in == 0) {
169                         n = stream->func (stream->buffer, BUFFER_SIZE, stream->gchandle);
170                         if (n <= 0) {
171                                 stream->eof = TRUE;
172                                 break;
173                         }
174                         zs->next_in = stream->buffer;
175                         zs->avail_in = n;
176                 }
177
178                 status = inflate (stream->stream, Z_SYNC_FLUSH);
179                 if (status == Z_STREAM_END) {
180                         stream->eof = TRUE;
181                         break;
182                 } else if (status != Z_OK) {
183                         return status;
184                 }
185         }
186         return length - zs->avail_out;
187 }
188
189 gint
190 WriteZStream (ZStream *stream, guchar *buffer, gint length)
191 {
192         gint n;
193         gint status;
194         z_stream *zs;
195
196         if (stream == NULL || buffer == NULL || length < 0)
197                 return ARGUMENT_ERROR;
198
199         if (stream->eof)
200                 return IO_ERROR;
201
202         zs = stream->stream;
203         zs->next_in = buffer;
204         zs->avail_in = length;
205         while (zs->avail_in > 0) {
206                 if (zs->avail_out == 0) {
207                         zs->next_out = stream->buffer;
208                         zs->avail_out = BUFFER_SIZE;
209                 }
210                 status = deflate (stream->stream, Z_NO_FLUSH);
211                 if (status != Z_OK && status != Z_STREAM_END)
212                         return status;
213
214                 if (zs->avail_out == 0) {
215                         n = write_to_managed (stream);
216                         if (n < 0)
217                                 return n;
218                 }
219         }
220         return length;
221 }
222