free internal buffer
[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->buffer);
114         free (zstream->stream);
115         memset (zstream, 0, sizeof (ZStream));
116         free (zstream);
117         return status;
118 }
119
120 static int
121 write_to_managed (ZStream *stream)
122 {
123         int n;
124         z_stream *zs;
125
126         zs = stream->stream;
127         if (zs->avail_out != BUFFER_SIZE) {
128                 n = stream->func (stream->buffer, BUFFER_SIZE - zs->avail_out);
129                 zs->next_out = stream->buffer;
130                 zs->avail_out =  BUFFER_SIZE;
131                 if (n < 0)
132                         return IO_ERROR;
133         }
134         return 0;
135 }
136
137 int
138 Flush (ZStream *stream)
139 {
140         if (!stream->compress)
141                 return 0;
142
143         return write_to_managed (stream);
144 }
145
146 int
147 ReadZStream (ZStream *stream, unsigned char *buffer, int length)
148 {
149         int n;
150         int status;
151         z_stream *zs;
152
153         if (stream == NULL || buffer == NULL || length < 0)
154                 return ARGUMENT_ERROR;
155
156         if (stream->eof)
157                 return 0;
158
159         zs = stream->stream;
160         zs->next_out = buffer;
161         zs->avail_out = length;
162         while (zs->avail_out > 0) {
163                 if (zs->avail_in == 0) {
164                         n = stream->func (stream->buffer, BUFFER_SIZE);
165                         if (n <= 0) {
166                                 stream->eof = TRUE;
167                                 break;
168                         }
169                         zs->next_in = stream->buffer;
170                         zs->avail_in = n;
171                 }
172
173                 status = inflate (stream->stream, Z_SYNC_FLUSH);
174                 if (status != Z_OK && status != Z_STREAM_END)
175                         return status;
176         }
177         return length - zs->avail_out;
178 }
179
180 int
181 WriteZStream (ZStream *stream, unsigned char *buffer, int length)
182 {
183         int n;
184         int status;
185         z_stream *zs;
186
187         if (stream == NULL || buffer == NULL || length < 0)
188                 return ARGUMENT_ERROR;
189
190         if (stream->eof)
191                 return IO_ERROR;
192
193         zs = stream->stream;
194         zs->next_in = buffer;
195         zs->avail_in = length;
196         while (zs->avail_in > 0) {
197                 if (zs->avail_out == 0) {
198                         zs->next_out = stream->buffer;
199                         zs->avail_out = BUFFER_SIZE;
200                 }
201                 status = deflate (stream->stream, Z_SYNC_FLUSH);
202                 if (status != Z_OK && status != Z_STREAM_END)
203                         return status;
204
205                 if (zs->avail_out == 0) {
206                         n = write_to_managed (stream);
207                         if (n < 0)
208                                 return n;
209                 }
210         }
211         return length;
212 }
213