Tizen 2.1 base
[platform/upstream/glib2.0.git] / gio / gconverterinputstream.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2009 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include "gconverterinputstream.h"
28 #include "gsimpleasyncresult.h"
29 #include "gcancellable.h"
30 #include "gioenumtypes.h"
31 #include "gioerror.h"
32 #include "glibintl.h"
33
34
35 /**
36  * SECTION:gconverterinputstream
37  * @short_description: Converter Input Stream
38  * @include: gio/gio.h
39  * @see_also: #GInputStream, #GConverter
40  *
41  * Converter input stream implements #GInputStream and allows
42  * conversion of data of various types during reading.
43  *
44  **/
45
46 #define INITIAL_BUFFER_SIZE 4096
47
48 typedef struct {
49   char *data;
50   gsize start;
51   gsize end;
52   gsize size;
53 } Buffer;
54
55 struct _GConverterInputStreamPrivate {
56   gboolean at_input_end;
57   gboolean finished;
58   GConverter *converter;
59   Buffer input_buffer;
60   Buffer converted_buffer;
61 };
62
63 enum {
64   PROP_0,
65   PROP_CONVERTER
66 };
67
68 static void   g_converter_input_stream_set_property (GObject       *object,
69                                                      guint          prop_id,
70                                                      const GValue  *value,
71                                                      GParamSpec    *pspec);
72 static void   g_converter_input_stream_get_property (GObject       *object,
73                                                      guint          prop_id,
74                                                      GValue        *value,
75                                                      GParamSpec    *pspec);
76 static void   g_converter_input_stream_finalize     (GObject       *object);
77 static gssize g_converter_input_stream_read         (GInputStream  *stream,
78                                                      void          *buffer,
79                                                      gsize          count,
80                                                      GCancellable  *cancellable,
81                                                      GError       **error);
82
83 G_DEFINE_TYPE (GConverterInputStream,
84                g_converter_input_stream,
85                G_TYPE_FILTER_INPUT_STREAM)
86
87 static void
88 g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
89 {
90   GObjectClass *object_class;
91   GInputStreamClass *istream_class;
92
93   g_type_class_add_private (klass, sizeof (GConverterInputStreamPrivate));
94
95   object_class = G_OBJECT_CLASS (klass);
96   object_class->get_property = g_converter_input_stream_get_property;
97   object_class->set_property = g_converter_input_stream_set_property;
98   object_class->finalize     = g_converter_input_stream_finalize;
99
100   istream_class = G_INPUT_STREAM_CLASS (klass);
101   istream_class->read_fn = g_converter_input_stream_read;
102
103   g_object_class_install_property (object_class,
104                                    PROP_CONVERTER,
105                                    g_param_spec_object ("converter",
106                                                         P_("Converter"),
107                                                         P_("The converter object"),
108                                                         G_TYPE_CONVERTER,
109                                                         G_PARAM_READWRITE|
110                                                         G_PARAM_CONSTRUCT_ONLY|
111                                                         G_PARAM_STATIC_STRINGS));
112
113 }
114
115 static void
116 g_converter_input_stream_finalize (GObject *object)
117 {
118   GConverterInputStreamPrivate *priv;
119   GConverterInputStream        *stream;
120
121   stream = G_CONVERTER_INPUT_STREAM (object);
122   priv = stream->priv;
123
124   g_free (priv->input_buffer.data);
125   g_free (priv->converted_buffer.data);
126   if (priv->converter)
127     g_object_unref (priv->converter);
128
129   G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object);
130 }
131
132 static void
133 g_converter_input_stream_set_property (GObject      *object,
134                                        guint         prop_id,
135                                        const GValue *value,
136                                        GParamSpec   *pspec)
137 {
138   GConverterInputStream *cstream;
139
140   cstream = G_CONVERTER_INPUT_STREAM (object);
141
142    switch (prop_id)
143     {
144     case PROP_CONVERTER:
145       cstream->priv->converter = g_value_dup_object (value);
146       break;
147
148     default:
149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150       break;
151     }
152
153 }
154
155 static void
156 g_converter_input_stream_get_property (GObject    *object,
157                                        guint       prop_id,
158                                        GValue     *value,
159                                        GParamSpec *pspec)
160 {
161   GConverterInputStreamPrivate *priv;
162   GConverterInputStream        *cstream;
163
164   cstream = G_CONVERTER_INPUT_STREAM (object);
165   priv = cstream->priv;
166
167   switch (prop_id)
168     {
169     case PROP_CONVERTER:
170       g_value_set_object (value, priv->converter);
171       break;
172
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175       break;
176     }
177
178 }
179 static void
180 g_converter_input_stream_init (GConverterInputStream *stream)
181 {
182   stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
183                                               G_TYPE_CONVERTER_INPUT_STREAM,
184                                               GConverterInputStreamPrivate);
185 }
186
187 /**
188  * g_converter_input_stream_new:
189  * @base_stream: a #GInputStream
190  * @converter: a #GConverter
191  *
192  * Creates a new converter input stream for the @base_stream.
193  *
194  * Returns: a new #GInputStream.
195  **/
196 GInputStream *
197 g_converter_input_stream_new (GInputStream *base_stream,
198                               GConverter   *converter)
199 {
200   GInputStream *stream;
201
202   g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
203
204   stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
205                          "base-stream", base_stream,
206                          "converter", converter,
207                          NULL);
208
209   return stream;
210 }
211
212 static gsize
213 buffer_data_size (Buffer *buffer)
214 {
215   return buffer->end - buffer->start;
216 }
217
218 static gsize
219 buffer_tailspace (Buffer *buffer)
220 {
221   return buffer->size - buffer->end;
222 }
223
224 static char *
225 buffer_data (Buffer *buffer)
226 {
227   return buffer->data + buffer->start;
228 }
229
230 static void
231 buffer_consumed (Buffer *buffer,
232                  gsize count)
233 {
234   buffer->start += count;
235   if (buffer->start == buffer->end)
236     buffer->start = buffer->end = 0;
237 }
238
239 static void
240 buffer_read (Buffer *buffer,
241              char *dest,
242              gsize count)
243 {
244   memcpy (dest, buffer->data + buffer->start, count);
245   buffer_consumed (buffer, count);
246 }
247
248 static void
249 compact_buffer (Buffer *buffer)
250 {
251   gsize in_buffer;
252
253   in_buffer = buffer_data_size (buffer);
254   memmove (buffer->data,
255            buffer->data + buffer->start,
256            in_buffer);
257   buffer->end -= buffer->start;
258   buffer->start = 0;
259 }
260
261 static void
262 grow_buffer (Buffer *buffer)
263 {
264   char *data;
265   gsize size, in_buffer;
266
267   if (buffer->size == 0)
268     size = INITIAL_BUFFER_SIZE;
269   else
270     size = buffer->size * 2;
271
272   data = g_malloc (size);
273   in_buffer = buffer_data_size (buffer);
274
275   memcpy (data,
276           buffer->data + buffer->start,
277           in_buffer);
278   g_free (buffer->data);
279   buffer->data = data;
280   buffer->end -= buffer->start;
281   buffer->start = 0;
282   buffer->size = size;
283 }
284
285 /* Ensures that the buffer can fit at_least_size bytes,
286  * *including* the current in-buffer data */
287 static void
288 buffer_ensure_space (Buffer *buffer,
289                      gsize at_least_size)
290 {
291   gsize in_buffer, left_to_fill;
292
293   in_buffer = buffer_data_size (buffer);
294
295   if (in_buffer >= at_least_size)
296     return;
297
298   left_to_fill = buffer_tailspace (buffer);
299
300   if (in_buffer + left_to_fill >= at_least_size)
301     {
302       /* We fit in remaining space at end */
303       /* If the copy is small, compact now anyway so we can fill more */
304       if (in_buffer < 256)
305         compact_buffer (buffer);
306     }
307   else if (buffer->size >= at_least_size)
308     {
309       /* We fit, but only if we compact */
310       compact_buffer (buffer);
311     }
312   else
313     {
314       /* Need to grow buffer */
315       while (buffer->size < at_least_size)
316         grow_buffer (buffer);
317     }
318 }
319
320 static gssize
321 fill_input_buffer (GConverterInputStream  *stream,
322                    gsize                   at_least_size,
323                    GCancellable           *cancellable,
324                    GError                **error)
325 {
326   GConverterInputStreamPrivate *priv;
327   GInputStream *base_stream;
328   gssize nread;
329
330   priv = stream->priv;
331
332   buffer_ensure_space (&priv->input_buffer, at_least_size);
333
334   base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
335   nread = g_input_stream_read (base_stream,
336                                priv->input_buffer.data + priv->input_buffer.end,
337                                buffer_tailspace (&priv->input_buffer),
338                                cancellable,
339                                error);
340
341   if (nread > 0)
342     priv->input_buffer.end += nread;
343
344   return nread;
345 }
346
347
348 static gssize
349 g_converter_input_stream_read (GInputStream *stream,
350                                void         *buffer,
351                                gsize         count,
352                                GCancellable *cancellable,
353                                GError      **error)
354 {
355   GConverterInputStream *cstream;
356   GConverterInputStreamPrivate *priv;
357   gsize available, total_bytes_read;
358   gssize nread;
359   GConverterResult res;
360   gsize bytes_read;
361   gsize bytes_written;
362   GError *my_error;
363   GError *my_error2;
364
365   cstream = G_CONVERTER_INPUT_STREAM (stream);
366   priv = cstream->priv;
367
368   available = buffer_data_size (&priv->converted_buffer);
369
370   if (available > 0 &&
371       count <= available)
372     {
373       /* Converted data available, return that */
374       buffer_read (&priv->converted_buffer, buffer, count);
375       return count;
376     }
377
378   /* Full request not available, read all currently available and request
379      refill/conversion for more */
380
381   buffer_read (&priv->converted_buffer, buffer, available);
382
383   total_bytes_read = available;
384   count -= available;
385
386   /* If there is no data to convert, and no pre-converted data,
387      do some i/o for more input */
388   if (buffer_data_size (&priv->input_buffer) == 0 &&
389       total_bytes_read == 0 &&
390       !priv->at_input_end)
391     {
392       nread = fill_input_buffer (cstream, count, cancellable, error);
393       if (nread < 0)
394         return -1;
395       if (nread == 0)
396         priv->at_input_end = TRUE;
397     }
398
399   /* First try to convert any available data (or state) directly to the user buffer: */
400   if (!priv->finished)
401     {
402       my_error = NULL;
403       res = g_converter_convert (priv->converter,
404                                  buffer_data (&priv->input_buffer),
405                                  buffer_data_size (&priv->input_buffer),
406                                  buffer, count,
407                                  priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
408                                  &bytes_read,
409                                  &bytes_written,
410                                  &my_error);
411       if (res != G_CONVERTER_ERROR)
412         {
413           total_bytes_read += bytes_written;
414           buffer_consumed (&priv->input_buffer, bytes_read);
415           if (res == G_CONVERTER_FINISHED)
416             priv->finished = TRUE; /* We're done converting */
417         }
418       else if (total_bytes_read == 0 &&
419                !g_error_matches (my_error,
420                                  G_IO_ERROR,
421                                  G_IO_ERROR_PARTIAL_INPUT) &&
422                !g_error_matches (my_error,
423                                  G_IO_ERROR,
424                                  G_IO_ERROR_NO_SPACE))
425         {
426           /* No previously read data and no "special" error, return error */
427           g_propagate_error (error, my_error);
428           return -1;
429         }
430       else
431         g_error_free (my_error);
432     }
433
434   /* We had some pre-converted data and/or we converted directly to the
435      user buffer */
436   if (total_bytes_read > 0)
437     return total_bytes_read;
438
439   /* If there is no more to convert, return EOF */
440   if (priv->finished)
441     {
442       g_assert (buffer_data_size (&priv->converted_buffer) == 0);
443       return 0;
444     }
445
446   /* There was "complexity" in the straight-to-buffer conversion,
447    * convert to our own buffer and write from that.
448    * At this point we didn't produce any data into @buffer.
449    */
450
451   /* Ensure we have *some* initial target space */
452   buffer_ensure_space (&priv->converted_buffer, count);
453
454   while (TRUE)
455     {
456       g_assert (!priv->finished);
457
458       /* Try to convert to our buffer */
459       my_error = NULL;
460       res = g_converter_convert (priv->converter,
461                                  buffer_data (&priv->input_buffer),
462                                  buffer_data_size (&priv->input_buffer),
463                                  buffer_data (&priv->converted_buffer),
464                                  buffer_tailspace (&priv->converted_buffer),
465                                  priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
466                                  &bytes_read,
467                                  &bytes_written,
468                                  &my_error);
469       if (res != G_CONVERTER_ERROR)
470         {
471           priv->converted_buffer.end += bytes_written;
472           buffer_consumed (&priv->input_buffer, bytes_read);
473
474           /* Maybe we consumed without producing any output */
475           if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED)
476             continue; /* Convert more */
477
478           if (res == G_CONVERTER_FINISHED)
479             priv->finished = TRUE;
480
481           total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer));
482           buffer_read (&priv->converted_buffer, buffer, total_bytes_read);
483
484           g_assert (priv->finished || total_bytes_read > 0);
485
486           return total_bytes_read;
487         }
488
489       /* There was some kind of error filling our buffer */
490
491       if (g_error_matches (my_error,
492                            G_IO_ERROR,
493                            G_IO_ERROR_PARTIAL_INPUT) &&
494           !priv->at_input_end)
495         {
496           /* Need more data */
497           my_error2 = NULL;
498           nread = fill_input_buffer (cstream,
499                                      buffer_data_size (&priv->input_buffer) + 4096,
500                                      cancellable,
501                                      &my_error2);
502           if (nread < 0)
503             {
504               /* Can't read any more data, return that error */
505               g_error_free (my_error);
506               g_propagate_error (error, my_error2);
507               return -1;
508             }
509           else if (nread == 0)
510             {
511               /* End of file, try INPUT_AT_END */
512               priv->at_input_end = TRUE;
513             }
514           g_error_free (my_error);
515           continue;
516         }
517
518       if (g_error_matches (my_error,
519                            G_IO_ERROR,
520                            G_IO_ERROR_NO_SPACE))
521         {
522           /* Need more destination space, grow it
523            * Note: if we actually grow the buffer (as opposed to compacting it),
524            * this will double the size, not just add one byte. */
525           buffer_ensure_space (&priv->converted_buffer,
526                                priv->converted_buffer.size + 1);
527           g_error_free (my_error);
528           continue;
529         }
530
531       /* Any other random error, return it */
532       g_propagate_error (error, my_error);
533       return -1;
534     }
535
536   g_assert_not_reached ();
537 }
538
539 /**
540  * g_converter_input_stream_get_converter:
541  * @converter_stream: a #GConverterInputStream
542  *
543  * Gets the #GConverter that is used by @converter_stream.
544  *
545  * Returns: (transfer none): the converter of the converter input stream
546  *
547  * Since: 2.24
548  */
549 GConverter *
550 g_converter_input_stream_get_converter (GConverterInputStream *converter_stream)
551 {
552   return converter_stream->priv->converter;
553 }