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