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