2009-07-21 Gonzalo Paniagua Javier <gonzalo@novell.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);
30 struct _ZStream {
31         z_stream *stream;
32         guchar *buffer;
33         read_write_func func;
34         guchar compress;
35         guchar eof;
36 };
37 typedef struct _ZStream ZStream;
38
39 ZStream *CreateZStream (gint compress, guchar gzip, read_write_func func);
40 gint CloseZStream (ZStream *zstream);
41 gint Flush (ZStream *stream);
42 gint ReadZStream (ZStream *stream, guchar *buffer, gint length);
43 gint WriteZStream (ZStream *stream, guchar *buffer, gint length);
44
45 static void *
46 z_alloc (void *opaque, gsize nitems, gsize item_size)
47 {
48         return g_malloc0 (nitems * item_size);
49 }
50
51 static void
52 z_free (void *opaque, void *ptr)
53 {
54         g_free (ptr);
55 }
56
57 ZStream *
58 CreateZStream (gint compress, guchar gzip, read_write_func func)
59 {
60         z_stream *z;
61         gint retval;
62         ZStream *result;
63
64         if (func == NULL)
65                 return NULL;
66
67 #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204)
68         /* Older versions of zlib do not support raw deflate or gzip */
69         return NULL;
70 #endif
71
72         z = g_new0 (z_stream, 1);
73         if (compress) {
74                 retval = deflateInit2 (z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, gzip ? 31 : -15, 8, Z_DEFAULT_STRATEGY);
75         } else {
76                 retval = inflateInit2 (z, gzip ? 31 : -15);
77         }
78
79         if (retval != Z_OK) {
80                 g_free (z);
81                 return NULL;
82         }
83         z->zalloc = z_alloc;
84         z->zfree = z_free;
85         result = g_new0 (ZStream, 1);
86         result->stream = z;
87         result->func = func;
88         result->compress = compress;
89         result->buffer = g_new (guchar, BUFFER_SIZE);
90         return result;
91 }
92
93 gint
94 CloseZStream (ZStream *zstream)
95 {
96         gint status;
97         gint flush_status;
98
99         if (zstream == NULL)
100                 return ARGUMENT_ERROR;
101
102         status = 0;
103         if (zstream->compress) {
104                 if (zstream->stream->total_out) {
105                         status = deflate (zstream->stream, Z_FINISH);
106                         flush_status = Flush (zstream);
107                         if (status == Z_OK || status == Z_STREAM_END)
108                                 status = flush_status;
109                 }
110                 deflateEnd (zstream->stream);
111         } else {
112                 inflateEnd (zstream->stream);
113         }
114         g_free (zstream->buffer);
115         g_free (zstream->stream);
116         memset (zstream, 0, sizeof (ZStream));
117         g_free (zstream);
118         return status;
119 }
120
121 static gint
122 write_to_managed (ZStream *stream)
123 {
124         gint n;
125         z_stream *zs;
126
127         zs = stream->stream;
128         if (zs->avail_out != BUFFER_SIZE) {
129                 n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out);
130                 zs->next_out = stream->buffer;
131                 zs->avail_out =  BUFFER_SIZE;
132                 if (n < 0)
133                         return IO_ERROR;
134         }
135         return 0;
136 }
137
138 gint
139 Flush (ZStream *stream)
140 {
141         if (!stream->compress)
142                 return 0;
143
144         return write_to_managed (stream);
145 }
146
147 gint
148 ReadZStream (ZStream *stream, guchar *buffer, gint length)
149 {
150         gint n;
151         gint status;
152         z_stream *zs;
153
154         if (stream == NULL || buffer == NULL || length < 0)
155                 return ARGUMENT_ERROR;
156
157         if (stream->eof)
158                 return 0;
159
160         zs = stream->stream;
161         zs->next_out = buffer;
162         zs->avail_out = length;
163         while (zs->avail_out > 0) {
164                 if (zs->avail_in == 0) {
165                         n = stream->func (stream->buffer, BUFFER_SIZE);
166                         if (n <= 0) {
167                                 stream->eof = TRUE;
168                                 break;
169                         }
170                         zs->next_in = stream->buffer;
171                         zs->avail_in = n;
172                 }
173
174                 status = inflate (stream->stream, Z_SYNC_FLUSH);
175                 if (status != Z_OK && status != Z_STREAM_END)
176                         return status;
177         }
178         return length - zs->avail_out;
179 }
180
181 gint
182 WriteZStream (ZStream *stream, guchar *buffer, gint length)
183 {
184         gint n;
185         gint status;
186         z_stream *zs;
187
188         if (stream == NULL || buffer == NULL || length < 0)
189                 return ARGUMENT_ERROR;
190
191         if (stream->eof)
192                 return IO_ERROR;
193
194         zs = stream->stream;
195         zs->next_in = buffer;
196         zs->avail_in = length;
197         while (zs->avail_in > 0) {
198                 if (zs->avail_out == 0) {
199                         zs->next_out = stream->buffer;
200                         zs->avail_out = BUFFER_SIZE;
201                 }
202                 status = deflate (stream->stream, Z_SYNC_FLUSH);
203                 if (status != Z_OK && status != Z_STREAM_END)
204                         return status;
205
206                 if (zs->avail_out == 0) {
207                         n = write_to_managed (stream);
208                         if (n < 0)
209                                 return n;
210                 }
211         }
212         return length;
213 }
214