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