Introduce G_PARAM_DEPRECATED and G_ENABLE_DIAGNOSTIC
[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_data_size (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_data_size (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_data_size (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 /* Ensures that the buffer can fit at_least_size bytes,
295  * *including* the current in-buffer data */
296 static void
297 buffer_ensure_space (Buffer *buffer,
298                      gsize at_least_size)
299 {
300   gsize in_buffer, left_to_fill;
301
302   in_buffer = buffer_data_size (buffer);
303
304   if (in_buffer >= at_least_size)
305     return;
306
307   left_to_fill = buffer_tailspace (buffer);
308
309   if (in_buffer + left_to_fill >= at_least_size)
310     {
311       /* We fit in remaining space at end */
312       /* If the copy is small, compact now anyway so we can fill more */
313       if (in_buffer < 256)
314         compact_buffer (buffer);
315     }
316   else if (buffer->size >= at_least_size)
317     {
318       /* We fit, but only if we compact */
319       compact_buffer (buffer);
320     }
321   else
322     {
323       /* Need to grow buffer */
324       while (buffer->size < at_least_size)
325         grow_buffer (buffer);
326     }
327 }
328
329 static void
330 buffer_append (Buffer *buffer,
331                const char *data,
332                gsize data_size)
333 {
334   buffer_ensure_space (buffer,
335                        buffer_data_size (buffer) + data_size);
336   memcpy (buffer->data + buffer->end, data, data_size);
337   buffer->end += data_size;
338 }
339
340
341 static gboolean
342 flush_buffer (GConverterOutputStream *stream,
343               Buffer                 *buffer,
344               GCancellable           *cancellable,
345               GError                **error)
346 {
347   GConverterOutputStreamPrivate *priv;
348   GOutputStream *base_stream;
349   gsize nwritten;
350   gsize available;
351   gboolean res;
352
353   priv = stream->priv;
354
355   base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
356
357   available = buffer_data_size (&priv->converted_buffer);
358   if (available > 0)
359     {
360       res = g_output_stream_write_all (base_stream,
361                                        buffer_data (&priv->converted_buffer),
362                                        available,
363                                        &nwritten,
364                                        cancellable,
365                                        error);
366       buffer_consumed (&priv->converted_buffer, nwritten);
367       return res;
368     }
369   return TRUE;
370 }
371
372
373 static gssize
374 g_converter_output_stream_write (GOutputStream *stream,
375                                  const void   *buffer,
376                                  gsize         count,
377                                  GCancellable *cancellable,
378                                  GError      **error)
379 {
380   GConverterOutputStream *cstream;
381   GConverterOutputStreamPrivate *priv;
382   gssize retval;
383   GConverterResult res;
384   gsize bytes_read;
385   gsize bytes_written;
386   GError *my_error;
387   const char *to_convert;
388   gsize to_convert_size, converted_bytes;
389   gboolean converting_from_buffer;
390
391   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
392   priv = cstream->priv;
393
394   /* Write out all available pre-converted data and fail if
395      not possible */
396   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
397     return -1;
398
399   if (priv->finished)
400     return 0;
401
402   /* Convert as much as possible */
403   if (buffer_data_size (&priv->output_buffer) > 0)
404     {
405       converting_from_buffer = TRUE;
406       buffer_append (&priv->output_buffer, buffer, count);
407       to_convert = buffer_data (&priv->output_buffer);
408       to_convert_size = buffer_data_size (&priv->output_buffer);
409     }
410   else
411     {
412       converting_from_buffer = FALSE;
413       to_convert = buffer;
414       to_convert_size = count;
415     }
416
417   /* Ensure we have *some* initial target space */
418   buffer_ensure_space (&priv->converted_buffer, to_convert_size);
419
420   converted_bytes = 0;
421   while (!priv->finished && converted_bytes < to_convert_size)
422     {
423       /* Ensure we have *some* target space */
424       if (buffer_tailspace (&priv->converted_buffer) == 0)
425         grow_buffer (&priv->converted_buffer);
426
427       /* Try to convert to our buffer */
428       my_error = NULL;
429       res = g_converter_convert (priv->converter,
430                                  to_convert + converted_bytes,
431                                  to_convert_size - converted_bytes,
432                                  buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
433                                  buffer_tailspace (&priv->converted_buffer),
434                                  0,
435                                  &bytes_read,
436                                  &bytes_written,
437                                  &my_error);
438
439       if (res != G_CONVERTER_ERROR)
440         {
441           priv->converted_buffer.end += bytes_written;
442           converted_bytes += bytes_read;
443
444           if (res == G_CONVERTER_FINISHED)
445             priv->finished = TRUE;
446         }
447       else
448         {
449           /* No-space errors can be handled locally: */
450           if (g_error_matches (my_error,
451                                G_IO_ERROR,
452                                G_IO_ERROR_NO_SPACE))
453             {
454               /* Need more destination space, grow it
455                * Note: if we actually grow the buffer (as opposed to compacting it),
456                * this will double the size, not just add one byte. */
457               buffer_ensure_space (&priv->converted_buffer,
458                                    priv->converted_buffer.size + 1);
459               g_error_free (my_error);
460               continue;
461             }
462
463           if (converted_bytes > 0)
464             {
465               /* We got an conversion error, but we did convert some bytes before
466                  that, so handle those before reporting the error */
467               g_error_free (my_error);
468               break;
469             }
470
471           if (g_error_matches (my_error,
472                                G_IO_ERROR,
473                                G_IO_ERROR_PARTIAL_INPUT))
474             {
475               /* Consume everything to buffer that we append to next time
476                  we write */
477               if (!converting_from_buffer)
478                 buffer_append (&priv->output_buffer, buffer, count);
479               /* in the converting_from_buffer case we already appended this */
480
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 -1;
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: 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 }
614
615 #define __G_CONVERTER_OUTPUT_STREAM_C__
616 #include "gioaliasdef.c"