1 #include "gskzlibinflator.h"
2 #include <zlib.h>
3 #include "gskzlib.h"
4
5 static GObjectClass *parent_class = NULL;
6
7 #define MAX_BUFFER_SIZE 4096
8
9 enum
10 {
11 PROP_0,
12 PROP_USE_GZIP
13 };
14
15 static guint
gsk_zlib_inflator_raw_read(GskStream * stream,gpointer data,guint length,GError ** error)16 gsk_zlib_inflator_raw_read (GskStream *stream,
17 gpointer data,
18 guint length,
19 GError **error)
20 {
21 GskZlibInflator *zlib_inflator = GSK_ZLIB_INFLATOR (stream);
22 guint rv = gsk_buffer_read (&zlib_inflator->decompressed, data, length);
23
24 if (!gsk_io_get_is_writable (zlib_inflator))
25 {
26 if (zlib_inflator->decompressed.size == 0)
27 gsk_io_notify_read_shutdown (zlib_inflator);
28 }
29 else
30 {
31 if (zlib_inflator->decompressed.size < MAX_BUFFER_SIZE)
32 gsk_io_mark_idle_notify_write (zlib_inflator);
33 if (zlib_inflator->decompressed.size == 0)
34 gsk_io_clear_idle_notify_read (zlib_inflator);
35 }
36
37 return rv;
38 }
39
40 static guint
gsk_zlib_inflator_raw_read_buffer(GskStream * stream,GskBuffer * buffer,GError ** error)41 gsk_zlib_inflator_raw_read_buffer(GskStream *stream,
42 GskBuffer *buffer,
43 GError **error)
44 {
45 GskZlibInflator *zlib_inflator = GSK_ZLIB_INFLATOR (stream);
46 guint rv = gsk_buffer_drain (buffer, &zlib_inflator->decompressed);
47 if (!gsk_io_get_is_writable (zlib_inflator))
48 {
49 gsk_io_notify_read_shutdown (zlib_inflator);
50 }
51 else
52 {
53 gsk_io_mark_idle_notify_write (zlib_inflator);
54 gsk_io_clear_idle_notify_read (zlib_inflator);
55 }
56
57 return rv;
58 }
59
60
61 static gboolean
do_sync(GskZlibInflator * zlib_inflator,GError ** error)62 do_sync (GskZlibInflator *zlib_inflator, GError **error)
63 {
64 z_stream *zst = zlib_inflator->private_stream;
65 guint8 buf[4096];
66 int rv;
67 if (zst == NULL)
68 return TRUE;
69
70 zst->next_in = NULL;
71 zst->avail_in = 0;
72 do
73 {
74 /* Set up output location */
75 zst->next_out = buf;
76 zst->avail_out = sizeof (buf);
77
78 /* Decompress */
79 rv = inflate (zst, Z_SYNC_FLUSH);
80 if (rv == Z_OK || rv == Z_STREAM_END)
81 gsk_buffer_append (&zlib_inflator->decompressed, buf, zst->next_out - buf);
82 }
83 while (rv == Z_OK && zst->avail_out == 0);
84 if (rv != Z_OK && rv != Z_STREAM_END)
85 {
86 GskErrorCode zerror_code = gsk_zlib_error_to_gsk_error (rv);
87 const char *zmsg = gsk_zlib_error_to_message (rv);
88 g_set_error (error, GSK_G_ERROR_DOMAIN, zerror_code,
89 "could not inflate: %s", zmsg);
90 return FALSE;
91 }
92 return TRUE;
93 }
94
95 static gboolean
gsk_zlib_inflator_shutdown_write(GskIO * io,GError ** error)96 gsk_zlib_inflator_shutdown_write (GskIO *io,
97 GError **error)
98 {
99 GskZlibInflator *zlib_inflator = GSK_ZLIB_INFLATOR (io);
100 if (! do_sync (GSK_ZLIB_INFLATOR (io), error))
101 return FALSE;
102 if (zlib_inflator->decompressed.size == 0)
103 gsk_io_notify_read_shutdown (io);
104 else
105 gsk_io_mark_idle_notify_write (io);
106 return TRUE;
107 }
108
109 static guint
gsk_zlib_inflator_raw_write(GskStream * stream,gconstpointer data,guint length,GError ** error)110 gsk_zlib_inflator_raw_write (GskStream *stream,
111 gconstpointer data,
112 guint length,
113 GError **error)
114 {
115 GskZlibInflator *zlib_inflator = GSK_ZLIB_INFLATOR (stream);
116 z_stream *zst;
117 guint8 buf[4096];
118 int rv;
119 if (zlib_inflator->private_stream == NULL)
120 {
121 zst = g_new (z_stream, 1);
122 zlib_inflator->private_stream = zst;
123 zst->next_in = (gpointer) data;
124 zst->avail_in = length;
125 zst->zalloc = NULL;
126 zst->zfree = NULL;
127 zst->opaque = NULL;
128 inflateInit2 (zst,
129 15|32 /* windowSize: see zlib.h */
130 );
131 }
132 else
133 {
134 zst = zlib_inflator->private_stream;
135 zst->next_in = (gpointer) data;
136 zst->avail_in = length;
137 }
138
139 do
140 {
141 /* Set up output location */
142 zst->next_out = buf;
143 zst->avail_out = sizeof (buf);
144
145 /* Decompress */
146 rv = inflate (zst, Z_NO_FLUSH);
147 if (rv == Z_OK || rv == Z_STREAM_END)
148 gsk_buffer_append (&zlib_inflator->decompressed, buf, zst->next_out - buf);
149 }
150 while (rv == Z_OK && zst->avail_in > 0);
151 if (rv != Z_OK && rv != Z_STREAM_END)
152 {
153 GskErrorCode zerror_code = gsk_zlib_error_to_gsk_error (rv);
154 const char *zmsg = gsk_zlib_error_to_message (rv);
155 g_set_error (error, GSK_G_ERROR_DOMAIN, zerror_code,
156 "could not inflate: %s", zmsg);
157 }
158 if (zlib_inflator->decompressed.size > MAX_BUFFER_SIZE)
159 gsk_io_clear_idle_notify_write (zlib_inflator);
160 if (zlib_inflator->decompressed.size > 0)
161 gsk_io_mark_idle_notify_read (zlib_inflator);
162
163 return length - zst->avail_in;
164 }
165
166 static void
gsk_zlib_inflator_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)167 gsk_zlib_inflator_set_property (GObject *object,
168 guint property_id,
169 const GValue *value,
170 GParamSpec *pspec)
171 {
172 GskZlibInflator *zlib_inflator = GSK_ZLIB_INFLATOR (object);
173 switch (property_id)
174 {
175 case PROP_USE_GZIP:
176 zlib_inflator->use_gzip = g_value_get_boolean (value);
177 break;
178 default:
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
180 break;
181 }
182 }
183
184 static void
gsk_zlib_inflator_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)185 gsk_zlib_inflator_get_property (GObject *object,
186 guint property_id,
187 GValue *value,
188 GParamSpec *pspec)
189 {
190 GskZlibInflator *zlib_inflator = GSK_ZLIB_INFLATOR (object);
191 switch (property_id)
192 {
193 case PROP_USE_GZIP:
194 g_value_set_boolean (value, zlib_inflator->use_gzip);
195 break;
196 default:
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
198 break;
199 }
200 }
201
202 static void
gsk_zlib_inflator_finalize(GObject * object)203 gsk_zlib_inflator_finalize (GObject *object)
204 {
205 GskZlibInflator *inflator = GSK_ZLIB_INFLATOR (object);
206 if (inflator->private_stream)
207 {
208 inflateEnd (inflator->private_stream);
209 g_free (inflator->private_stream);
210 }
211 gsk_buffer_destruct (&inflator->decompressed);
212 (*parent_class->finalize) (object);
213 }
214
215 /* --- functions --- */
216 static void
gsk_zlib_inflator_init(GskZlibInflator * zlib_inflator)217 gsk_zlib_inflator_init (GskZlibInflator *zlib_inflator)
218 {
219 gsk_io_mark_is_readable (zlib_inflator);
220 gsk_io_mark_is_writable (zlib_inflator);
221 gsk_io_mark_idle_notify_write (zlib_inflator);
222 }
223
224 static void
gsk_zlib_inflator_class_init(GskZlibInflatorClass * class)225 gsk_zlib_inflator_class_init (GskZlibInflatorClass *class)
226 {
227 GskIOClass *io_class = GSK_IO_CLASS (class);
228 GskStreamClass *stream_class = GSK_STREAM_CLASS (class);
229 GObjectClass *object_class = G_OBJECT_CLASS (class);
230 GParamSpec *pspec;
231 parent_class = g_type_class_peek_parent (class);
232 stream_class->raw_read = gsk_zlib_inflator_raw_read;
233 stream_class->raw_read_buffer = gsk_zlib_inflator_raw_read_buffer;
234 stream_class->raw_write = gsk_zlib_inflator_raw_write;
235 io_class->shutdown_write = gsk_zlib_inflator_shutdown_write;
236 object_class->set_property = gsk_zlib_inflator_set_property;
237 object_class->get_property = gsk_zlib_inflator_get_property;
238 object_class->finalize = gsk_zlib_inflator_finalize;
239
240 pspec = g_param_spec_boolean ("use-gzip", "Use Gzip",
241 "whether to expect gzip-encapsulated data",
242 FALSE,
243 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
244 g_object_class_install_property (object_class, PROP_USE_GZIP, pspec);
245 }
246
gsk_zlib_inflator_get_type()247 GType gsk_zlib_inflator_get_type()
248 {
249 static GType zlib_inflator_type = 0;
250 if (!zlib_inflator_type)
251 {
252 static const GTypeInfo zlib_inflator_info =
253 {
254 sizeof(GskZlibInflatorClass),
255 (GBaseInitFunc) NULL,
256 (GBaseFinalizeFunc) NULL,
257 (GClassInitFunc) gsk_zlib_inflator_class_init,
258 NULL, /* class_finalize */
259 NULL, /* class_data */
260 sizeof (GskZlibInflator),
261 0, /* n_preallocs */
262 (GInstanceInitFunc) gsk_zlib_inflator_init,
263 NULL /* value_table */
264 };
265 zlib_inflator_type = g_type_register_static (GSK_TYPE_STREAM,
266 "GskZlibInflator",
267 &zlib_inflator_info, 0);
268 }
269 return zlib_inflator_type;
270 }
271
272 /**
273 * gsk_zlib_inflator_new:
274 *
275 * Create a new zlib inflator: this takes deflated (compressed) input
276 * which is written into it, and uncompressed data can be read from it.
277 *
278 * returns: the newly allocated stream.
279 */
280 GskStream *
gsk_zlib_inflator_new(void)281 gsk_zlib_inflator_new (void)
282 {
283 return g_object_new (GSK_TYPE_ZLIB_INFLATOR, NULL);
284 }
285 GskStream *
gsk_zlib_inflator_new2(gboolean use_gzip)286 gsk_zlib_inflator_new2 (gboolean use_gzip)
287 {
288 return g_object_new (GSK_TYPE_ZLIB_INFLATOR,
289 "use-gzip", use_gzip,
290 NULL);
291 }
292