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