Export g_charset_converter_get_num_fallbacks in header
[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  *
209  * Creates a new converter output stream for the @base_stream.
210  *
211  * Returns: a new #GConverterOutputStream.
212  **/
213 GConverterOutputStream *
214 g_converter_output_stream_new (GOutputStream *base_stream,
215                               GConverter *converter)
216 {
217   GConverterOutputStream *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_available (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_available (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_available (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 static void
294 buffer_ensure_space (Buffer *buffer,
295                      gsize at_least_size)
296 {
297   gsize in_buffer, left_to_fill;
298
299   in_buffer = buffer_available (buffer);
300
301   if (in_buffer >= at_least_size)
302     return;
303
304   left_to_fill = buffer_tailspace (buffer);
305
306   if (in_buffer + left_to_fill >= at_least_size)
307     {
308       /* We fit in remaining space at end */
309       /* If the copy is small, compact now anyway so we can fill more */
310       if (in_buffer < 256)
311         compact_buffer (buffer);
312     }
313   else if (buffer->size >= at_least_size)
314     {
315       /* We fit, but only if we compact */
316       compact_buffer (buffer);
317     }
318   else
319     {
320       /* Need to grow buffer */
321       while (buffer->size < at_least_size)
322         grow_buffer (buffer);
323     }
324 }
325
326 static void
327 buffer_append (Buffer *buffer,
328                const char *data,
329                gsize data_size)
330 {
331   buffer_ensure_space (buffer,
332                        buffer_available (buffer) + data_size);
333   memcpy (buffer->data + buffer->end, data, data_size);
334   buffer->end += data_size;
335 }
336
337
338 static gboolean
339 flush_buffer (GConverterOutputStream *stream,
340               Buffer                 *buffer,
341               GCancellable           *cancellable,
342               GError                **error)
343 {
344   GConverterOutputStreamPrivate *priv;
345   GOutputStream *base_stream;
346   gsize nwritten;
347   gsize available;
348   gboolean res;
349
350   priv = stream->priv;
351
352   base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
353
354   available = buffer_available (&priv->converted_buffer);
355   if (available > 0)
356     {
357       res = g_output_stream_write_all (base_stream,
358                                        buffer_data (&priv->converted_buffer),
359                                        available,
360                                        &nwritten,
361                                        cancellable,
362                                        error);
363       buffer_consumed (&priv->converted_buffer, nwritten);
364       return res;
365     }
366   return TRUE;
367 }
368
369
370 static gssize
371 g_converter_output_stream_write (GOutputStream *stream,
372                                  const void   *buffer,
373                                  gsize         count,
374                                  GCancellable *cancellable,
375                                  GError      **error)
376 {
377   GConverterOutputStream *cstream;
378   GConverterOutputStreamPrivate *priv;
379   gssize retval;
380   GConverterResult res;
381   gsize bytes_read;
382   gsize bytes_written;
383   GError *my_error;
384   const char *to_convert;
385   gsize to_convert_size, converted_bytes;
386   gboolean converting_from_buffer;
387
388   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
389   priv = cstream->priv;
390
391   /* Write out all available pre-converted data and fail if
392      not possible */
393   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
394     return -1;
395
396   if (priv->finished)
397     return 0;
398
399   /* Convert as much as possible */
400   if (buffer_available (&priv->output_buffer) > 0)
401     {
402       converting_from_buffer = TRUE;
403       buffer_append (&priv->output_buffer, buffer, count);
404       to_convert = buffer_data (&priv->output_buffer);
405       to_convert_size = buffer_available (&priv->output_buffer);
406     }
407   else
408     {
409       converting_from_buffer = FALSE;
410       to_convert = buffer;
411       to_convert_size = count;
412     }
413
414   /* Ensure we have *some* initial target space */
415   buffer_ensure_space (&priv->converted_buffer, to_convert_size);
416
417   converted_bytes = 0;
418   while (!priv->finished && converted_bytes < to_convert_size)
419     {
420       /* Try to convert to our buffer */
421       my_error = NULL;
422       res = g_converter_convert (priv->converter,
423                                  to_convert + converted_bytes,
424                                  to_convert_size - converted_bytes,
425                                  buffer_data (&priv->converted_buffer),
426                                  buffer_tailspace (&priv->converted_buffer),
427                                  0,
428                                  &bytes_read,
429                                  &bytes_written,
430                                  &my_error);
431
432       if (res != G_CONVERTER_ERROR)
433         {
434           priv->converted_buffer.end += bytes_written;
435           converted_bytes += bytes_read;
436
437           if (res == G_CONVERTER_FINISHED)
438             priv->finished = TRUE;
439         }
440       else
441         {
442           /* No-space errors can be handled locally: */
443           if (g_error_matches (my_error,
444                                G_IO_ERROR,
445                                G_IO_ERROR_NO_SPACE))
446             {
447               /* Need more destination space, grow it
448                * Note: if we actually grow the buffer (as opposed to compacting it),
449                * this will double the size, not just add one byte. */
450               buffer_ensure_space (&priv->converted_buffer,
451                                    priv->converted_buffer.size + 1);
452               g_error_free (my_error);
453               continue;
454             }
455
456           if (converted_bytes > 0)
457             {
458               /* We got an conversion error, but we did convert some bytes before
459                  that, so handle those before reporting the error */
460               g_error_free (my_error);
461               break;
462             }
463
464           if (g_error_matches (my_error,
465                                G_IO_ERROR,
466                                G_IO_ERROR_PARTIAL_INPUT))
467             {
468               /* Consume everything to buffer that we append to next time
469                  we write */
470               if (!converting_from_buffer)
471                 buffer_append (&priv->output_buffer, buffer, count);
472               /* in the converting_from_buffer case we already appended this */
473
474               return count; /* consume everything */
475             }
476
477           /* Converted no data and got an normal error, return it */
478           g_propagate_error (error, my_error);
479           return -1;
480         }
481     }
482
483   if (converting_from_buffer)
484     {
485       buffer_consumed (&priv->output_buffer, converted_bytes);
486       retval = count;
487     }
488   else
489     retval = converted_bytes;
490
491   /* We now successfully consumed retval bytes, so we can't return an error,
492      even if writing this to the base stream fails. If it does we'll just
493      stop early and report this error when we try again on the next
494      write call. */
495   flush_buffer (cstream, &priv->converted_buffer, cancellable, NULL);
496
497   return retval;
498 }
499
500 static gboolean
501 g_converter_output_stream_flush (GOutputStream  *stream,
502                                  GCancellable   *cancellable,
503                                  GError        **error)
504 {
505   GConverterOutputStream *cstream;
506   GConverterOutputStreamPrivate *priv;
507   GConverterResult res;
508   GError *my_error;
509   gboolean is_closing;
510   gboolean flushed;
511   gsize bytes_read;
512   gsize bytes_written;
513
514   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
515   priv = cstream->priv;
516
517   is_closing = g_output_stream_is_closing (stream);
518
519   /* Write out all available pre-converted data and fail if
520      not possible */
521   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
522     return FALSE;
523
524   /* Ensure we have *some* initial target space */
525   buffer_ensure_space (&priv->converted_buffer, 1);
526
527   /* Convert whole buffer */
528   flushed = FALSE;
529   while (!priv->finished && !flushed)
530     {
531       /* Try to convert to our buffer */
532       my_error = NULL;
533       res = g_converter_convert (priv->converter,
534                                  buffer_data (&priv->output_buffer),
535                                  buffer_available (&priv->output_buffer),
536                                  buffer_data (&priv->converted_buffer),
537                                  buffer_tailspace (&priv->converted_buffer),
538                                  is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
539                                  &bytes_read,
540                                  &bytes_written,
541                                  &my_error);
542
543       if (res != G_CONVERTER_ERROR)
544         {
545           priv->converted_buffer.end += bytes_written;
546           buffer_consumed (&priv->output_buffer, bytes_read);
547
548           if (res == G_CONVERTER_FINISHED)
549             priv->finished = TRUE;
550           if (!is_closing &&
551               res == G_CONVERTER_FLUSHED)
552             {
553               /* Should not have retured FLUSHED with input left */
554               g_assert (buffer_available (&priv->output_buffer) == 0);
555               flushed = TRUE;
556             }
557         }
558       else
559         {
560           /* No-space errors can be handled locally: */
561           if (g_error_matches (my_error,
562                                G_IO_ERROR,
563                                G_IO_ERROR_NO_SPACE))
564             {
565               /* Need more destination space, grow it
566                * Note: if we actually grow the buffer (as opposed to compacting it),
567                * this will double the size, not just add one byte. */
568               buffer_ensure_space (&priv->converted_buffer,
569                                    priv->converted_buffer.size + 1);
570               g_error_free (my_error);
571               continue;
572             }
573
574           /* Any other error, including PARTIAL_INPUT can't be fixed by now
575              and is an error */
576           g_propagate_error (error, my_error);
577           return -1;
578         }
579     }
580
581   /* Now write all converted data to base stream */
582   if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
583     return FALSE;
584
585   return TRUE;
586 }
587
588 GConverter *
589 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
590 {
591   return converter_stream->priv->converter;
592 }
593
594 #define __G_CONVERTER_OUTPUT_STREAM_C__
595 #include "gioaliasdef.c"