gkdbus: Fix underflow and unreachable code bug
[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  * SPDX-License-Identifier: LGPL-2.1-or-later
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
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 "gpollableoutputstream.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  * As of GLib 2.34, #GConverterOutputStream implements
45  * #GPollableOutputStream.
46  **/
47
48 #define INITIAL_BUFFER_SIZE 4096
49
50 typedef struct {
51   char *data;
52   gsize start;
53   gsize end;
54   gsize size;
55 } Buffer;
56
57 struct _GConverterOutputStreamPrivate {
58   gboolean at_output_end;
59   gboolean finished;
60   GConverter *converter;
61   Buffer output_buffer; /* To be converted and written */
62   Buffer converted_buffer; /* Already converted */
63 };
64
65 /* Buffering strategy:
66  *
67  * Each time we write we must at least consume some input, or
68  * return an error. Thus we start with writing all already
69  * converted data and *then* we start converting (reporting
70  * an error at any point in this).
71  *
72  * Its possible that what the user wrote is not enough data
73  * for the converter, so we must then buffer it in output_buffer
74  * and ask for more data, but we want to avoid this as much as
75  * possible, converting directly from the users buffer.
76  */
77
78 enum {
79   PROP_0,
80   PROP_CONVERTER
81 };
82
83 static void   g_converter_output_stream_set_property (GObject        *object,
84                                                       guint           prop_id,
85                                                       const GValue   *value,
86                                                       GParamSpec     *pspec);
87 static void   g_converter_output_stream_get_property (GObject        *object,
88                                                       guint           prop_id,
89                                                       GValue         *value,
90                                                       GParamSpec     *pspec);
91 static void   g_converter_output_stream_finalize     (GObject        *object);
92 static gssize g_converter_output_stream_write        (GOutputStream  *stream,
93                                                       const void     *buffer,
94                                                       gsize           count,
95                                                       GCancellable   *cancellable,
96                                                       GError        **error);
97 static gboolean g_converter_output_stream_flush      (GOutputStream  *stream,
98                                                       GCancellable   *cancellable,
99                                                       GError        **error);
100
101 static gboolean g_converter_output_stream_can_poll          (GPollableOutputStream *stream);
102 static gboolean g_converter_output_stream_is_writable       (GPollableOutputStream *stream);
103 static gssize   g_converter_output_stream_write_nonblocking (GPollableOutputStream  *stream,
104                                                              const void             *buffer,
105                                                              gsize                  size,
106                                                              GError               **error);
107
108 static GSource *g_converter_output_stream_create_source     (GPollableOutputStream *stream,
109                                                              GCancellable          *cancellable);
110
111 static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
112
113 G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
114                          g_converter_output_stream,
115                          G_TYPE_FILTER_OUTPUT_STREAM,
116                          G_ADD_PRIVATE (GConverterOutputStream)
117                          G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
118                                                 g_converter_output_stream_pollable_iface_init))
119
120 static void
121 g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
122 {
123   GObjectClass *object_class;
124   GOutputStreamClass *istream_class;
125
126   object_class = G_OBJECT_CLASS (klass);
127   object_class->get_property = g_converter_output_stream_get_property;
128   object_class->set_property = g_converter_output_stream_set_property;
129   object_class->finalize     = g_converter_output_stream_finalize;
130
131   istream_class = G_OUTPUT_STREAM_CLASS (klass);
132   istream_class->write_fn = g_converter_output_stream_write;
133   istream_class->flush = g_converter_output_stream_flush;
134
135   g_object_class_install_property (object_class,
136                                    PROP_CONVERTER,
137                                    g_param_spec_object ("converter",
138                                                         P_("Converter"),
139                                                         P_("The converter object"),
140                                                         G_TYPE_CONVERTER,
141                                                         G_PARAM_READWRITE|
142                                                         G_PARAM_CONSTRUCT_ONLY|
143                                                         G_PARAM_STATIC_STRINGS));
144
145 }
146
147 static void
148 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
149 {
150   iface->can_poll = g_converter_output_stream_can_poll;
151   iface->is_writable = g_converter_output_stream_is_writable;
152   iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
153   iface->create_source = g_converter_output_stream_create_source;
154 }
155
156 static void
157 g_converter_output_stream_finalize (GObject *object)
158 {
159   GConverterOutputStreamPrivate *priv;
160   GConverterOutputStream        *stream;
161
162   stream = G_CONVERTER_OUTPUT_STREAM (object);
163   priv = stream->priv;
164
165   g_free (priv->output_buffer.data);
166   g_free (priv->converted_buffer.data);
167   if (priv->converter)
168     g_object_unref (priv->converter);
169
170   G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
171 }
172
173 static void
174 g_converter_output_stream_set_property (GObject      *object,
175                                        guint         prop_id,
176                                        const GValue *value,
177                                        GParamSpec   *pspec)
178 {
179   GConverterOutputStream *cstream;
180
181   cstream = G_CONVERTER_OUTPUT_STREAM (object);
182
183    switch (prop_id)
184     {
185     case PROP_CONVERTER:
186       cstream->priv->converter = g_value_dup_object (value);
187       break;
188
189     default:
190       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
191       break;
192     }
193
194 }
195
196 static void
197 g_converter_output_stream_get_property (GObject    *object,
198                                        guint       prop_id,
199                                        GValue     *value,
200                                        GParamSpec *pspec)
201 {
202   GConverterOutputStreamPrivate *priv;
203   GConverterOutputStream        *cstream;
204
205   cstream = G_CONVERTER_OUTPUT_STREAM (object);
206   priv = cstream->priv;
207
208   switch (prop_id)
209     {
210     case PROP_CONVERTER:
211       g_value_set_object (value, priv->converter);
212       break;
213
214     default:
215       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216       break;
217     }
218 }
219
220 static void
221 g_converter_output_stream_init (GConverterOutputStream *stream)
222 {
223   stream->priv = g_converter_output_stream_get_instance_private (stream);
224 }
225
226 /**
227  * g_converter_output_stream_new:
228  * @base_stream: a #GOutputStream
229  * @converter: a #GConverter
230  *
231  * Creates a new converter output stream for the @base_stream.
232  *
233  * Returns: a new #GOutputStream.
234  **/
235 GOutputStream *
236 g_converter_output_stream_new (GOutputStream *base_stream,
237                                GConverter    *converter)
238 {
239   GOutputStream *stream;
240
241   g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
242
243   stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
244                          "base-stream", base_stream,
245                          "converter", converter,
246                          NULL);
247
248   return stream;
249 }
250
251 static gsize
252 buffer_data_size (Buffer *buffer)
253 {
254   return buffer->end - buffer->start;
255 }
256
257 static gsize
258 buffer_tailspace (Buffer *buffer)
259 {
260   return buffer->size - buffer->end;
261 }
262
263 static char *
264 buffer_data (Buffer *buffer)
265 {
266   return buffer->data + buffer->start;
267 }
268
269 static void
270 buffer_consumed (Buffer *buffer,
271                  gsize count)
272 {
273   buffer->start += count;
274   if (buffer->start == buffer->end)
275     buffer->start = buffer->end = 0;
276 }
277
278 static void
279 compact_buffer (Buffer *buffer)
280 {
281   gsize in_buffer;
282
283   in_buffer = buffer_data_size (buffer);
284   memmove (buffer->data,
285            buffer->data + buffer->start,
286            in_buffer);
287   buffer->end -= buffer->start;
288   buffer->start = 0;
289 }
290
291 static void
292 grow_buffer (Buffer *buffer)
293 {
294   char *data;
295   gsize size, in_buffer;
296
297   if (buffer->size == 0)
298     size = INITIAL_BUFFER_SIZE;
299   else
300     size = buffer->size * 2;
301
302   data = g_malloc (size);
303   in_buffer = buffer_data_size (buffer);
304
305   if (in_buffer != 0)
306     memcpy (data,
307             buffer->data + buffer->start,
308             in_buffer);
309
310   g_free (buffer->data);
311   buffer->data = data;
312   buffer->end -= buffer->start;
313   buffer->start = 0;
314   buffer->size = size;
315 }
316
317 /* Ensures that the buffer can fit at_least_size bytes,
318  * *including* the current in-buffer data */
319 static void
320 buffer_ensure_space (Buffer *buffer,
321                      gsize at_least_size)
322 {
323   gsize in_buffer, left_to_fill;
324
325   in_buffer = buffer_data_size (buffer);
326
327   if (in_buffer >= at_least_size)
328     return;
329
330   left_to_fill = buffer_tailspace (buffer);
331
332   if (in_buffer + left_to_fill >= at_least_size)
333     {
334       /* We fit in remaining space at end */
335       /* If the copy is small, compact now anyway so we can fill more */
336       if (in_buffer < 256)
337         compact_buffer (buffer);
338     }
339   else if (buffer->size >= at_least_size)
340     {
341       /* We fit, but only if we compact */
342       compact_buffer (buffer);
343     }
344   else
345     {
346       /* Need to grow buffer */
347       while (buffer->size < at_least_size)
348         grow_buffer (buffer);
349     }
350 }
351
352 static void
353 buffer_append (Buffer *buffer,
354                const char *data,
355                gsize data_size)
356 {
357   buffer_ensure_space (buffer,
358                        buffer_data_size (buffer) + data_size);
359   memcpy (buffer->data + buffer->end, data, data_size);
360   buffer->end += data_size;
361 }
362
363
364 static gboolean
365 flush_buffer (GConverterOutputStream *stream,
366               gboolean                blocking,
367               GCancellable           *cancellable,
368               GError                **error)
369 {
370   GConverterOutputStreamPrivate *priv;
371   GOutputStream *base_stream;
372   gsize nwritten;
373   gsize available;
374   gboolean res;
375
376   priv = stream->priv;
377
378   base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
379
380   available = buffer_data_size (&priv->converted_buffer);
381   if (available > 0)
382     {
383       res = g_pollable_stream_write_all (base_stream,
384                                          buffer_data (&priv->converted_buffer),
385                                          available,
386                                          blocking,
387                                          &nwritten,
388                                          cancellable,
389                                          error);
390       buffer_consumed (&priv->converted_buffer, nwritten);
391       return res;
392     }
393   return TRUE;
394 }
395
396
397 static gssize
398 write_internal (GOutputStream  *stream,
399                 const void     *buffer,
400                 gsize           count,
401                 gboolean        blocking,
402                 GCancellable   *cancellable,
403                 GError        **error)
404 {
405   GConverterOutputStream *cstream;
406   GConverterOutputStreamPrivate *priv;
407   gssize retval;
408   GConverterResult res;
409   gsize bytes_read;
410   gsize bytes_written;
411   GError *my_error;
412   const char *to_convert;
413   gsize to_convert_size, converted_bytes;
414   gboolean converting_from_buffer;
415
416   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
417   priv = cstream->priv;
418
419   /* Write out all available pre-converted data and fail if
420      not possible */
421   if (!flush_buffer (cstream, blocking, cancellable, error))
422     return -1;
423
424   if (priv->finished)
425     return 0;
426
427   /* Convert as much as possible */
428   if (buffer_data_size (&priv->output_buffer) > 0)
429     {
430       converting_from_buffer = TRUE;
431       buffer_append (&priv->output_buffer, buffer, count);
432       to_convert = buffer_data (&priv->output_buffer);
433       to_convert_size = buffer_data_size (&priv->output_buffer);
434     }
435   else
436     {
437       converting_from_buffer = FALSE;
438       to_convert = buffer;
439       to_convert_size = count;
440     }
441
442   /* Ensure we have *some* initial target space */
443   buffer_ensure_space (&priv->converted_buffer, to_convert_size);
444
445   converted_bytes = 0;
446   while (!priv->finished && converted_bytes < to_convert_size)
447     {
448       /* Ensure we have *some* target space */
449       if (buffer_tailspace (&priv->converted_buffer) == 0)
450         grow_buffer (&priv->converted_buffer);
451
452       /* Try to convert to our buffer */
453       my_error = NULL;
454       res = g_converter_convert (priv->converter,
455                                  to_convert + converted_bytes,
456                                  to_convert_size - converted_bytes,
457                                  buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
458                                  buffer_tailspace (&priv->converted_buffer),
459                                  0,
460                                  &bytes_read,
461                                  &bytes_written,
462                                  &my_error);
463
464       if (res != G_CONVERTER_ERROR)
465         {
466           priv->converted_buffer.end += bytes_written;
467           converted_bytes += bytes_read;
468
469           if (res == G_CONVERTER_FINISHED)
470             priv->finished = TRUE;
471         }
472       else
473         {
474           /* No-space errors can be handled locally: */
475           if (g_error_matches (my_error,
476                                G_IO_ERROR,
477                                G_IO_ERROR_NO_SPACE))
478             {
479               /* Need more destination space, grow it
480                * Note: if we actually grow the buffer (as opposed to compacting it),
481                * this will double the size, not just add one byte. */
482               buffer_ensure_space (&priv->converted_buffer,
483                                    priv->converted_buffer.size + 1);
484               g_error_free (my_error);
485               continue;
486             }
487
488           if (converted_bytes > 0)
489             {
490               /* We got a conversion error, but we did convert some bytes before
491                  that, so handle those before reporting the error */
492               g_error_free (my_error);
493               break;
494             }
495
496           if (g_error_matches (my_error,
497                                G_IO_ERROR,
498                                G_IO_ERROR_PARTIAL_INPUT))
499             {
500               /* Consume everything to buffer that we append to next time
501                  we write */
502               if (!converting_from_buffer)
503                 buffer_append (&priv->output_buffer, buffer, count);
504               /* in the converting_from_buffer case we already appended this */
505
506               g_error_free (my_error);
507               return count; /* consume everything */
508             }
509
510           /* Converted no data and got a normal error, return it */
511           g_propagate_error (error, my_error);
512           return -1;
513         }
514     }
515
516   if (converting_from_buffer)
517     {
518       buffer_consumed (&priv->output_buffer, converted_bytes);
519       retval = count;
520     }
521   else
522     retval = converted_bytes;
523
524   /* We now successfully consumed retval bytes, so we can't return an error,
525      even if writing this to the base stream fails. If it does we'll just
526      stop early and report this error when we try again on the next
527      write call. */
528   flush_buffer (cstream, blocking, cancellable, NULL);
529
530   return retval;
531 }
532
533 static gssize
534 g_converter_output_stream_write (GOutputStream  *stream,
535                                  const void     *buffer,
536                                  gsize           count,
537                                  GCancellable   *cancellable,
538                                  GError        **error)
539 {
540   return write_internal (stream, buffer, count, TRUE, cancellable, error);
541 }
542
543 static gboolean
544 g_converter_output_stream_flush (GOutputStream  *stream,
545                                  GCancellable   *cancellable,
546                                  GError        **error)
547 {
548   GConverterOutputStream *cstream;
549   GConverterOutputStreamPrivate *priv;
550   GConverterResult res;
551   GError *my_error;
552   gboolean is_closing;
553   gboolean flushed;
554   gsize bytes_read;
555   gsize bytes_written;
556
557   cstream = G_CONVERTER_OUTPUT_STREAM (stream);
558   priv = cstream->priv;
559
560   is_closing = g_output_stream_is_closing (stream);
561
562   /* Write out all available pre-converted data and fail if
563      not possible */
564   if (!flush_buffer (cstream, TRUE, cancellable, error))
565     return FALSE;
566
567   /* Ensure we have *some* initial target space */
568   buffer_ensure_space (&priv->converted_buffer, 1);
569
570   /* Convert whole buffer */
571   flushed = FALSE;
572   while (!priv->finished && !flushed)
573     {
574       /* Ensure we have *some* target space */
575       if (buffer_tailspace (&priv->converted_buffer) == 0)
576         grow_buffer (&priv->converted_buffer);
577
578       /* Try to convert to our buffer */
579       my_error = NULL;
580       res = g_converter_convert (priv->converter,
581                                  buffer_data (&priv->output_buffer),
582                                  buffer_data_size (&priv->output_buffer),
583                                  buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
584                                  buffer_tailspace (&priv->converted_buffer),
585                                  is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
586                                  &bytes_read,
587                                  &bytes_written,
588                                  &my_error);
589
590       if (res != G_CONVERTER_ERROR)
591         {
592           priv->converted_buffer.end += bytes_written;
593           buffer_consumed (&priv->output_buffer, bytes_read);
594
595           if (res == G_CONVERTER_FINISHED)
596             priv->finished = TRUE;
597           if (!is_closing &&
598               res == G_CONVERTER_FLUSHED)
599             {
600               /* Should not have returned FLUSHED with input left */
601               g_assert (buffer_data_size (&priv->output_buffer) == 0);
602               flushed = TRUE;
603             }
604         }
605       else
606         {
607           /* No-space errors can be handled locally: */
608           if (g_error_matches (my_error,
609                                G_IO_ERROR,
610                                G_IO_ERROR_NO_SPACE))
611             {
612               /* Need more destination space, grow it
613                * Note: if we actually grow the buffer (as opposed to compacting it),
614                * this will double the size, not just add one byte. */
615               buffer_ensure_space (&priv->converted_buffer,
616                                    priv->converted_buffer.size + 1);
617               g_error_free (my_error);
618               continue;
619             }
620
621           /* Any other error, including PARTIAL_INPUT can't be fixed by now
622              and is an error */
623           g_propagate_error (error, my_error);
624           return FALSE;
625         }
626     }
627
628   /* Now write all converted data to base stream */
629   if (!flush_buffer (cstream, TRUE, cancellable, error))
630     return FALSE;
631
632   return TRUE;
633 }
634
635 static gboolean
636 g_converter_output_stream_can_poll (GPollableOutputStream *stream)
637 {
638   GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
639
640   return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
641           g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
642 }
643
644 static gboolean
645 g_converter_output_stream_is_writable (GPollableOutputStream *stream)
646 {
647   GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
648
649   return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
650 }
651
652 static gssize
653 g_converter_output_stream_write_nonblocking (GPollableOutputStream  *stream,
654                                              const void             *buffer,
655                                              gsize                   count,
656                                              GError                **error)
657 {
658   return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
659                          NULL, error);
660 }
661
662 static GSource *
663 g_converter_output_stream_create_source (GPollableOutputStream *stream,
664                                          GCancellable          *cancellable)
665 {
666   GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
667   GSource *base_source, *pollable_source;
668
669   base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
670   pollable_source = g_pollable_source_new_full (stream, base_source,
671                                                 cancellable);
672   g_source_unref (base_source);
673
674   return pollable_source;
675 }
676
677 /**
678  * g_converter_output_stream_get_converter:
679  * @converter_stream: a #GConverterOutputStream
680  *
681  * Gets the #GConverter that is used by @converter_stream.
682  *
683  * Returns: (transfer none): the converter of the converter output stream
684  *
685  * Since: 2.24
686  */
687 GConverter *
688 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
689 {
690   return converter_stream->priv->converter;
691 }