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