Initial version of GResource
[platform/upstream/glib.git] / gio / gconverteroutputstream.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 "gconverteroutputstream.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:gconverteroutputstream
37  * @short_description: Converter Output Stream
38  * @include: gio/gio.h
39  * @see_also: #GOutputStream, #GConverter
40  *
41  * Converter output stream implements #GOutputStream 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 _GConverterOutputStreamPrivate {
56   gboolean at_output_end;
57   gboolean finished;
58   GConverter *converter;
59   Buffer output_buffer; /* To be converted and written */
60   Buffer converted_buffer; /* Already converted */
61 };
62
63 /* Buffering strategy:
64  *
65  * Each time we write we must at least consume some input, or
66  * return an error. Thus we start with writing all already
67  * converted data and *then* we start converting (reporting
68  * an error at any point in this).
69  *
70  * Its possible that what the user wrote is not enough data
71  * for the converter, so we must then buffer it in output_buffer
72  * and ask for more data, but we want to avoid this as much as
73  * possible, converting directly from the users buffer.
74  */
75
76 enum {
77   PROP_0,
78   PROP_CONVERTER
79 };
80
81 static void   g_converter_output_stream_set_property (GObject        *object,
82                                                       guint           prop_id,
83                                                       const GValue   *value,
84                                                       GParamSpec     *pspec);
85 static void   g_converter_output_stream_get_property (GObject        *object,
86                                                       guint           prop_id,
87                                                       GValue         *value,
88                                                       GParamSpec     *pspec);
89 static void   g_converter_output_stream_finalize     (GObject        *object);
90 static gssize g_converter_output_stream_write        (GOutputStream  *stream,
91                                                       const void     *buffer,
92                                                       gsize           count,
93                                                       GCancellable   *cancellable,
94                                                       GError        **error);
95 static gboolean g_converter_output_stream_flush      (GOutputStream  *stream,
96                                                       GCancellable   *cancellable,
97                                                       GError        **error);
98
99 G_DEFINE_TYPE (GConverterOutputStream,
100                g_converter_output_stream,
101                G_TYPE_FILTER_OUTPUT_STREAM)
102
103 static void
104 g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
105 {
106   GObjectClass *object_class;
107   GOutputStreamClass *istream_class;
108
109   g_type_class_add_private (klass, sizeof (GConverterOutputStreamPrivate));
110
111   object_class = G_OBJECT_CLASS (klass);
112   object_class->get_property = g_converter_output_stream_get_property;
113   object_class->set_property = g_converter_output_stream_set_property;
114   object_class->finalize     = g_converter_output_stream_finalize;
115
116   istream_class = G_OUTPUT_STREAM_CLASS (klass);
117   istream_class->write_fn = g_converter_output_stream_write;
118   istream_class->flush = g_converter_output_stream_flush;
119
120   g_object_class_install_property (object_class,
121                                    PROP_CONVERTER,
122                                    g_param_spec_object ("converter",
123                                                         P_("Converter"),
124                                                         P_("The converter object"),
125                                                         G_TYPE_CONVERTER,
126                                                         G_PARAM_READWRITE|
127                                                         G_PARAM_CONSTRUCT_ONLY|
128                                                         G_PARAM_STATIC_STRINGS));
129
130 }
131
132 static void
133 g_converter_output_stream_finalize (GObject *object)
134 {
135   GConverterOutputStreamPrivate *priv;
136   GConverterOutputStream        *stream;
137
138   stream = G_CONVERTER_OUTPUT_STREAM (object);
139   priv = stream->priv;
140
141   g_free (priv->output_buffer.data);
142   g_free (priv->converted_buffer.data);
143   if (priv->converter)
144     g_object_unref (priv->converter);
145
146   G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
147 }
148
149 static void
150 g_converter_output_stream_set_property (GObject      *object,
151                                        guint         prop_id,
152                                        const GValue *value,
153                                        GParamSpec   *pspec)
154 {
155   GConverterOutputStream *cstream;
156
157   cstream = G_CONVERTER_OUTPUT_STREAM (object);
158
159    switch (prop_id)
160     {
161     case PROP_CONVERTER:
162       cstream->priv->converter = g_value_dup_object (value);
163       break;
164
165     default:
166       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
167       break;
168     }
169
170 }
171
172 static void
173 g_converter_output_stream_get_property (GObject    *object,
174                                        guint       prop_id,
175                                        GValue     *value,
176                                        GParamSpec *pspec)
177 {
178   GConverterOutputStreamPrivate *priv;
179   GConverterOutputStream        *cstream;
180
181   cstream = G_CONVERTER_OUTPUT_STREAM (object);
182   priv = cstream->priv;
183
184   switch (prop_id)
185     {
186     case PROP_CONVERTER:
187       g_value_set_object (value, priv->converter);
188       break;
189
190     default:
191       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
192       break;
193     }
194 }
195
196 static void
197 g_converter_output_stream_init (GConverterOutputStream *stream)
198 {
199   stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
200                                               G_TYPE_CONVERTER_OUTPUT_STREAM,
201                                               GConverterOutputStreamPrivate);
202 }
203
204 /**
205  * g_converter_output_stream_new:
206  * @base_stream: a #GOutputStream
207  * @converter: a #GConverter
208  *
209  * Creates a new converter output stream for the @base_stream.
210  *
211  * Returns: a new #GOutputStream.
212  **/
213 GOutputStream *
214 g_converter_output_stream_new (GOutputStream *base_stream,
215                                GConverter    *converter)
216 {
217   GOutputStream *stream;
218
219   g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
220
221   stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
222                          "base-stream", base_stream,
223                          "converter", converter,
224                          NULL);
225
226   return stream;
227 }
228
229 static gsize
230 buffer_data_size (Buffer *buffer)
231 {
232   return buffer->end - buffer->start;
233 }
234
235 static gsize
236 buffer_tailspace (Buffer *buffer)
237 {
238   return buffer->size - buffer->end;
239 }
240
241 static char *
242 buffer_data (Buffer *buffer)
243 {
244   return buffer->data + buffer->start;
245 }
246
247 static void
248 buffer_consumed (Buffer *buffer,
249                  gsize count)
250 {
251   buffer->start += count;
252   if (buffer->start == buffer->end)
253     buffer->start = buffer->end = 0;
254 }
255
256 static void
257 compact_buffer (Buffer *buffer)
258 {
259   gsize in_buffer;
260
261   in_buffer = buffer_data_size (buffer);
262   memmove (buffer->data,
263            buffer->data + buffer->start,
264            in_buffer);
265   buffer->end -= buffer->start;
266   buffer->start = 0;
267 }
268
269 static void
270 grow_buffer (Buffer *buffer)
271 {
272   char *data;
273   gsize size, in_buffer;
274
275   if (buffer->size == 0)
276     size = INITIAL_BUFFER_SIZE;
277   else
278     size = buffer->size * 2;
279
280   data = g_malloc (size);
281   in_buffer = buffer_data_size (buffer);
282
283   memcpy (data,
284           buffer->data + buffer->start,
285           in_buffer);
286   g_free (buffer->data);
287   buffer->data = data;
288   buffer->end -= buffer->start;
289   buffer->start = 0;
290   buffer->size = size;
291 }
292
293 /* Ensures that the buffer can fit at_least_size bytes,
294  * *including* the current in-buffer data */
295 static void
296 buffer_ensure_space (Buffer *buffer,
297                      gsize at_least_size)
298 {
299   gsize in_buffer, left_to_fill;
300
301   in_buffer = buffer_data_size (buffer);
302
303   if (in_buffer >= at_least_size)
304     return;
305
306   left_to_fill = buffer_tailspace (buffer);
307
308   if (in_buffer + left_to_fill >= at_least_size)
309     {
310       /* We fit in remaining space at end */
311       /* If the copy is small, compact now anyway so we can fill more */
312       if (in_buffer < 256)
313         compact_buffer (buffer);
314     }
315   else if (buffer->size >= at_least_size)
316     {
317       /* We fit, but only if we compact */
318       compact_buffer (buffer);
319     }
320   else
321     {
322       /* Need to grow buffer */
323       while (buffer->size < at_least_size)
324         grow_buffer (buffer);
325     }
326 }
327
328 static void
329 buffer_append (Buffer *buffer,
330                const char *data,
331                gsize data_size)
332 {
333   buffer_ensure_space (buffer,
334                        buffer_data_size (buffer) + data_size);
335   memcpy (buffer->data + buffer->end, data, data_size);
336   buffer->end += data_size;
337 }
338
339
340 static gboolean
341 flush_buffer (GConverterOutputStream *stream,
342               Buffer                 *buffer,
343               GCancellable           *cancellable,
344               GError                **error)
345 {
346   GConverterOutputStreamPrivate *priv;
347   GOutputStream *base_stream;
348   gsize nwritten;
349   gsize available;
350   gboolean res;
351
352   priv = stream->priv;
353
354   base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
355
356   available = buffer_data_size (&priv->converted_buffer);
357   if (available > 0)
358     {
359       res = g_output_stream_write_all (base_stream,
360                                        buffer_data (&priv->converted_buffer),
361                                        available,
362                                        &nwritten,
363                                        cancellable,
364                                        error);
365       buffer_consumed (&priv->converted_buffer, nwritten);
366       return res;
367     }
368   return TRUE;
369 }
370
371
372 static gssize
373 g_converter_output_stream_write (GOutputStream *stream,
374                                  const void   *buffer,
375                                  gsize         count,
376                                  GCancellable *cancellable,
377                                  GError      **error)
378 {
379   GConverterOutputStream *cstream;
380   GConverterOutputStreamPrivate *priv;
381   gssize retval;
382   GConverterResult res;
383   gsize bytes_read;
384   gsize bytes_written;
385   GError *my_error;
386   const char *to_convert;
387   gsize to_convert_size, converted_bytes;
388   gboolean converting_from_buffer;
389
390   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
391   priv = cstream->priv;
392
393   /* Write out all available pre-converted data and fail if
394      not possible */
395   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
396     return -1;
397
398   if (priv->finished)
399     return 0;
400
401   /* Convert as much as possible */
402   if (buffer_data_size (&priv->output_buffer) > 0)
403     {
404       converting_from_buffer = TRUE;
405       buffer_append (&priv->output_buffer, buffer, count);
406       to_convert = buffer_data (&priv->output_buffer);
407       to_convert_size = buffer_data_size (&priv->output_buffer);
408     }
409   else
410     {
411       converting_from_buffer = FALSE;
412       to_convert = buffer;
413       to_convert_size = count;
414     }
415
416   /* Ensure we have *some* initial target space */
417   buffer_ensure_space (&priv->converted_buffer, to_convert_size);
418
419   converted_bytes = 0;
420   while (!priv->finished && converted_bytes < to_convert_size)
421     {
422       /* Ensure we have *some* target space */
423       if (buffer_tailspace (&priv->converted_buffer) == 0)
424         grow_buffer (&priv->converted_buffer);
425
426       /* Try to convert to our buffer */
427       my_error = NULL;
428       res = g_converter_convert (priv->converter,
429                                  to_convert + converted_bytes,
430                                  to_convert_size - converted_bytes,
431                                  buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
432                                  buffer_tailspace (&priv->converted_buffer),
433                                  0,
434                                  &bytes_read,
435                                  &bytes_written,
436                                  &my_error);
437
438       if (res != G_CONVERTER_ERROR)
439         {
440           priv->converted_buffer.end += bytes_written;
441           converted_bytes += bytes_read;
442
443           if (res == G_CONVERTER_FINISHED)
444             priv->finished = TRUE;
445         }
446       else
447         {
448           /* No-space errors can be handled locally: */
449           if (g_error_matches (my_error,
450                                G_IO_ERROR,
451                                G_IO_ERROR_NO_SPACE))
452             {
453               /* Need more destination space, grow it
454                * Note: if we actually grow the buffer (as opposed to compacting it),
455                * this will double the size, not just add one byte. */
456               buffer_ensure_space (&priv->converted_buffer,
457                                    priv->converted_buffer.size + 1);
458               g_error_free (my_error);
459               continue;
460             }
461
462           if (converted_bytes > 0)
463             {
464               /* We got an conversion error, but we did convert some bytes before
465                  that, so handle those before reporting the error */
466               g_error_free (my_error);
467               break;
468             }
469
470           if (g_error_matches (my_error,
471                                G_IO_ERROR,
472                                G_IO_ERROR_PARTIAL_INPUT))
473             {
474               /* Consume everything to buffer that we append to next time
475                  we write */
476               if (!converting_from_buffer)
477                 buffer_append (&priv->output_buffer, buffer, count);
478               /* in the converting_from_buffer case we already appended this */
479
480               g_error_free (my_error);
481               return count; /* consume everything */
482             }
483
484           /* Converted no data and got an normal error, return it */
485           g_propagate_error (error, my_error);
486           return -1;
487         }
488     }
489
490   if (converting_from_buffer)
491     {
492       buffer_consumed (&priv->output_buffer, converted_bytes);
493       retval = count;
494     }
495   else
496     retval = converted_bytes;
497
498   /* We now successfully consumed retval bytes, so we can't return an error,
499      even if writing this to the base stream fails. If it does we'll just
500      stop early and report this error when we try again on the next
501      write call. */
502   flush_buffer (cstream, &priv->converted_buffer, cancellable, NULL);
503
504   return retval;
505 }
506
507 static gboolean
508 g_converter_output_stream_flush (GOutputStream  *stream,
509                                  GCancellable   *cancellable,
510                                  GError        **error)
511 {
512   GConverterOutputStream *cstream;
513   GConverterOutputStreamPrivate *priv;
514   GConverterResult res;
515   GError *my_error;
516   gboolean is_closing;
517   gboolean flushed;
518   gsize bytes_read;
519   gsize bytes_written;
520
521   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
522   priv = cstream->priv;
523
524   is_closing = g_output_stream_is_closing (stream);
525
526   /* Write out all available pre-converted data and fail if
527      not possible */
528   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
529     return FALSE;
530
531   /* Ensure we have *some* initial target space */
532   buffer_ensure_space (&priv->converted_buffer, 1);
533
534   /* Convert whole buffer */
535   flushed = FALSE;
536   while (!priv->finished && !flushed)
537     {
538       /* Ensure we have *some* target space */
539       if (buffer_tailspace (&priv->converted_buffer) == 0)
540         grow_buffer (&priv->converted_buffer);
541
542       /* Try to convert to our buffer */
543       my_error = NULL;
544       res = g_converter_convert (priv->converter,
545                                  buffer_data (&priv->output_buffer),
546                                  buffer_data_size (&priv->output_buffer),
547                                  buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
548                                  buffer_tailspace (&priv->converted_buffer),
549                                  is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
550                                  &bytes_read,
551                                  &bytes_written,
552                                  &my_error);
553
554       if (res != G_CONVERTER_ERROR)
555         {
556           priv->converted_buffer.end += bytes_written;
557           buffer_consumed (&priv->output_buffer, bytes_read);
558
559           if (res == G_CONVERTER_FINISHED)
560             priv->finished = TRUE;
561           if (!is_closing &&
562               res == G_CONVERTER_FLUSHED)
563             {
564               /* Should not have retured FLUSHED with input left */
565               g_assert (buffer_data_size (&priv->output_buffer) == 0);
566               flushed = TRUE;
567             }
568         }
569       else
570         {
571           /* No-space errors can be handled locally: */
572           if (g_error_matches (my_error,
573                                G_IO_ERROR,
574                                G_IO_ERROR_NO_SPACE))
575             {
576               /* Need more destination space, grow it
577                * Note: if we actually grow the buffer (as opposed to compacting it),
578                * this will double the size, not just add one byte. */
579               buffer_ensure_space (&priv->converted_buffer,
580                                    priv->converted_buffer.size + 1);
581               g_error_free (my_error);
582               continue;
583             }
584
585           /* Any other error, including PARTIAL_INPUT can't be fixed by now
586              and is an error */
587           g_propagate_error (error, my_error);
588           return FALSE;
589         }
590     }
591
592   /* Now write all converted data to base stream */
593   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
594     return FALSE;
595
596   return TRUE;
597 }
598
599 /**
600  * g_converter_output_stream_get_converter:
601  * @converter_stream: a #GConverterOutputStream
602  *
603  * Gets the #GConverter that is used by @converter_stream.
604  *
605  * Returns: (transfer none): the converter of the converter output stream
606  *
607  * Since: 2.24
608  */
609 GConverter *
610 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
611 {
612   return converter_stream->priv->converter;
613 }