Merge pull request #853 from echampet/onclick
[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 static gint flush_internal (ZStream *stream, gboolean is_final);
46
47 static void *
48 z_alloc (void *opaque, unsigned int nitems, unsigned int item_size)
49 {
50         return g_malloc0 (nitems * item_size);
51 }
52
53 static void
54 z_free (void *opaque, void *ptr)
55 {
56         g_free (ptr);
57 }
58
59 ZStream *
60 CreateZStream (gint compress, guchar gzip, read_write_func func, void *gchandle)
61 {
62         z_stream *z;
63         gint retval;
64         ZStream *result;
65
66         if (func == NULL)
67                 return NULL;
68
69 #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204)
70         /* Older versions of zlib do not support raw deflate or gzip */
71         return NULL;
72 #endif
73
74         z = g_new0 (z_stream, 1);
75         if (compress) {
76                 retval = deflateInit2 (z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, gzip ? 31 : -15, 8, Z_DEFAULT_STRATEGY);
77         } else {
78                 retval = inflateInit2 (z, gzip ? 31 : -15);
79         }
80
81         if (retval != Z_OK) {
82                 g_free (z);
83                 return NULL;
84         }
85         z->zalloc = z_alloc;
86         z->zfree = z_free;
87         result = g_new0 (ZStream, 1);
88         result->stream = z;
89         result->func = func;
90         result->gchandle = gchandle;
91         result->compress = compress;
92         result->buffer = g_new (guchar, BUFFER_SIZE);
93         result->stream->next_out = result->buffer;
94         result->stream->avail_out = BUFFER_SIZE;
95         return result;
96 }
97
98 gint
99 CloseZStream (ZStream *zstream)
100 {
101         gint status;
102         gint flush_status;
103
104         if (zstream == NULL)
105                 return ARGUMENT_ERROR;
106
107         status = 0;
108         if (zstream->compress) {
109                 if (zstream->stream->total_in > 0) {
110                         do {
111                                 status = deflate (zstream->stream, Z_FINISH);
112                                 flush_status = flush_internal (zstream, TRUE);
113                         } while (status == Z_OK); /* We want Z_STREAM_END or error here here */
114                         if (status == Z_STREAM_END)
115                                 status = flush_status;
116                 }
117                 deflateEnd (zstream->stream);
118         } else {
119                 inflateEnd (zstream->stream);
120         }
121         g_free (zstream->buffer);
122         g_free (zstream->stream);
123         memset (zstream, 0, sizeof (ZStream));
124         g_free (zstream);
125         return status;
126 }
127
128 static gint
129 write_to_managed (ZStream *stream)
130 {
131         gint n;
132         z_stream *zs;
133
134         zs = stream->stream;
135         if (zs->avail_out != BUFFER_SIZE) {
136                 n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out, stream->gchandle);
137                 zs->next_out = stream->buffer;
138                 zs->avail_out = BUFFER_SIZE;
139                 if (n < 0)
140                         return IO_ERROR;
141         }
142         return 0;
143 }
144
145 static gint
146 flush_internal (ZStream *stream, gboolean is_final)
147 {
148         gint status;
149
150         if (!stream->compress)
151                 return 0;
152
153         if (!is_final && stream->stream->avail_in != 0) {
154                 status = deflate (stream->stream, Z_PARTIAL_FLUSH);
155                 if (status != Z_OK && status != Z_STREAM_END)
156                         return status;
157         }
158         return write_to_managed (stream);
159 }
160
161 gint
162 Flush (ZStream *stream)
163 {
164         return flush_internal (stream, FALSE);
165 }
166
167 gint
168 ReadZStream (ZStream *stream, guchar *buffer, gint length)
169 {
170         gint n;
171         gint status;
172         z_stream *zs;
173
174         if (stream == NULL || buffer == NULL || length < 0)
175                 return ARGUMENT_ERROR;
176
177         if (stream->eof)
178                 return 0;
179
180         zs = stream->stream;
181         zs->next_out = buffer;
182         zs->avail_out = length;
183         while (zs->avail_out > 0) {
184                 if (zs->avail_in == 0) {
185                         n = stream->func (stream->buffer, BUFFER_SIZE, stream->gchandle);
186                         if (n <= 0) {
187                                 stream->eof = TRUE;
188                         }
189                         zs->next_in = stream->buffer;
190                         zs->avail_in = n;
191                 }
192
193                 if (zs->avail_in == 0 && zs->total_in == 0)
194                         return Z_STREAM_END;
195
196                 status = inflate (stream->stream, Z_SYNC_FLUSH);
197                 if (status == Z_STREAM_END) {
198                         stream->eof = TRUE;
199                         break;
200                 } else if (status != Z_OK) {
201                         return status;
202                 }
203         }
204         return length - zs->avail_out;
205 }
206
207 gint
208 WriteZStream (ZStream *stream, guchar *buffer, gint length)
209 {
210         gint n;
211         gint status;
212         z_stream *zs;
213
214         if (stream == NULL || buffer == NULL || length < 0)
215                 return ARGUMENT_ERROR;
216
217         if (stream->eof)
218                 return IO_ERROR;
219
220         zs = stream->stream;
221         zs->next_in = buffer;
222         zs->avail_in = length;
223         while (zs->avail_in > 0) {
224                 if (zs->avail_out == 0) {
225                         zs->next_out = stream->buffer;
226                         zs->avail_out = BUFFER_SIZE;
227                 }
228                 status = deflate (stream->stream, Z_NO_FLUSH);
229                 if (status != Z_OK && status != Z_STREAM_END)
230                         return status;
231
232                 if (zs->avail_out == 0) {
233                         n = write_to_managed (stream);
234                         if (n < 0)
235                                 return n;
236                 }
237         }
238         return length;
239 }
240