1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2009 Red Hat, Inc.
5 * SPDX-License-Identifier: LGPL-2.1-or-later
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.
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.
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/>.
20 * Author: Alexander Larsson <alexl@redhat.com>
27 #include "gconverteroutputstream.h"
28 #include "gpollableoutputstream.h"
29 #include "gcancellable.h"
30 #include "gioenumtypes.h"
36 * SECTION:gconverteroutputstream
37 * @short_description: Converter Output Stream
39 * @see_also: #GOutputStream, #GConverter
41 * Converter output stream implements #GOutputStream and allows
42 * conversion of data of various types during reading.
44 * As of GLib 2.34, #GConverterOutputStream implements
45 * #GPollableOutputStream.
48 #define INITIAL_BUFFER_SIZE 4096
57 struct _GConverterOutputStreamPrivate {
58 gboolean at_output_end;
60 GConverter *converter;
61 Buffer output_buffer; /* To be converted and written */
62 Buffer converted_buffer; /* Already converted */
65 /* Buffering strategy:
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).
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.
83 static void g_converter_output_stream_set_property (GObject *object,
87 static void g_converter_output_stream_get_property (GObject *object,
91 static void g_converter_output_stream_finalize (GObject *object);
92 static gssize g_converter_output_stream_write (GOutputStream *stream,
95 GCancellable *cancellable,
97 static gboolean g_converter_output_stream_flush (GOutputStream *stream,
98 GCancellable *cancellable,
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,
108 static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
109 GCancellable *cancellable);
111 static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
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))
121 g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
123 GObjectClass *object_class;
124 GOutputStreamClass *istream_class;
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;
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;
135 g_object_class_install_property (object_class,
137 g_param_spec_object ("converter",
139 P_("The converter object"),
142 G_PARAM_CONSTRUCT_ONLY|
143 G_PARAM_STATIC_STRINGS));
148 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
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;
157 g_converter_output_stream_finalize (GObject *object)
159 GConverterOutputStreamPrivate *priv;
160 GConverterOutputStream *stream;
162 stream = G_CONVERTER_OUTPUT_STREAM (object);
165 g_free (priv->output_buffer.data);
166 g_free (priv->converted_buffer.data);
168 g_object_unref (priv->converter);
170 G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
174 g_converter_output_stream_set_property (GObject *object,
179 GConverterOutputStream *cstream;
181 cstream = G_CONVERTER_OUTPUT_STREAM (object);
186 cstream->priv->converter = g_value_dup_object (value);
190 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
197 g_converter_output_stream_get_property (GObject *object,
202 GConverterOutputStreamPrivate *priv;
203 GConverterOutputStream *cstream;
205 cstream = G_CONVERTER_OUTPUT_STREAM (object);
206 priv = cstream->priv;
211 g_value_set_object (value, priv->converter);
215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221 g_converter_output_stream_init (GConverterOutputStream *stream)
223 stream->priv = g_converter_output_stream_get_instance_private (stream);
227 * g_converter_output_stream_new:
228 * @base_stream: a #GOutputStream
229 * @converter: a #GConverter
231 * Creates a new converter output stream for the @base_stream.
233 * Returns: a new #GOutputStream.
236 g_converter_output_stream_new (GOutputStream *base_stream,
237 GConverter *converter)
239 GOutputStream *stream;
241 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
243 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
244 "base-stream", base_stream,
245 "converter", converter,
252 buffer_data_size (Buffer *buffer)
254 return buffer->end - buffer->start;
258 buffer_tailspace (Buffer *buffer)
260 return buffer->size - buffer->end;
264 buffer_data (Buffer *buffer)
266 return buffer->data + buffer->start;
270 buffer_consumed (Buffer *buffer,
273 buffer->start += count;
274 if (buffer->start == buffer->end)
275 buffer->start = buffer->end = 0;
279 compact_buffer (Buffer *buffer)
283 in_buffer = buffer_data_size (buffer);
284 memmove (buffer->data,
285 buffer->data + buffer->start,
287 buffer->end -= buffer->start;
292 grow_buffer (Buffer *buffer)
295 gsize size, in_buffer;
297 if (buffer->size == 0)
298 size = INITIAL_BUFFER_SIZE;
300 size = buffer->size * 2;
302 data = g_malloc (size);
303 in_buffer = buffer_data_size (buffer);
307 buffer->data + buffer->start,
310 g_free (buffer->data);
312 buffer->end -= buffer->start;
317 /* Ensures that the buffer can fit at_least_size bytes,
318 * *including* the current in-buffer data */
320 buffer_ensure_space (Buffer *buffer,
323 gsize in_buffer, left_to_fill;
325 in_buffer = buffer_data_size (buffer);
327 if (in_buffer >= at_least_size)
330 left_to_fill = buffer_tailspace (buffer);
332 if (in_buffer + left_to_fill >= at_least_size)
334 /* We fit in remaining space at end */
335 /* If the copy is small, compact now anyway so we can fill more */
337 compact_buffer (buffer);
339 else if (buffer->size >= at_least_size)
341 /* We fit, but only if we compact */
342 compact_buffer (buffer);
346 /* Need to grow buffer */
347 while (buffer->size < at_least_size)
348 grow_buffer (buffer);
353 buffer_append (Buffer *buffer,
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;
365 flush_buffer (GConverterOutputStream *stream,
367 GCancellable *cancellable,
370 GConverterOutputStreamPrivate *priv;
371 GOutputStream *base_stream;
378 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
380 available = buffer_data_size (&priv->converted_buffer);
383 res = g_pollable_stream_write_all (base_stream,
384 buffer_data (&priv->converted_buffer),
390 buffer_consumed (&priv->converted_buffer, nwritten);
398 write_internal (GOutputStream *stream,
402 GCancellable *cancellable,
405 GConverterOutputStream *cstream;
406 GConverterOutputStreamPrivate *priv;
408 GConverterResult res;
412 const char *to_convert;
413 gsize to_convert_size, converted_bytes;
414 gboolean converting_from_buffer;
416 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
417 priv = cstream->priv;
419 /* Write out all available pre-converted data and fail if
421 if (!flush_buffer (cstream, blocking, cancellable, error))
427 /* Convert as much as possible */
428 if (buffer_data_size (&priv->output_buffer) > 0)
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);
437 converting_from_buffer = FALSE;
439 to_convert_size = count;
442 /* Ensure we have *some* initial target space */
443 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
446 while (!priv->finished && converted_bytes < to_convert_size)
448 /* Ensure we have *some* target space */
449 if (buffer_tailspace (&priv->converted_buffer) == 0)
450 grow_buffer (&priv->converted_buffer);
452 /* Try to convert to our buffer */
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),
464 if (res != G_CONVERTER_ERROR)
466 priv->converted_buffer.end += bytes_written;
467 converted_bytes += bytes_read;
469 if (res == G_CONVERTER_FINISHED)
470 priv->finished = TRUE;
474 /* No-space errors can be handled locally: */
475 if (g_error_matches (my_error,
477 G_IO_ERROR_NO_SPACE))
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);
488 if (converted_bytes > 0)
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);
496 if (g_error_matches (my_error,
498 G_IO_ERROR_PARTIAL_INPUT))
500 /* Consume everything to buffer that we append to next time
502 if (!converting_from_buffer)
503 buffer_append (&priv->output_buffer, buffer, count);
504 /* in the converting_from_buffer case we already appended this */
506 g_error_free (my_error);
507 return count; /* consume everything */
510 /* Converted no data and got a normal error, return it */
511 g_propagate_error (error, my_error);
516 if (converting_from_buffer)
518 buffer_consumed (&priv->output_buffer, converted_bytes);
522 retval = converted_bytes;
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
528 flush_buffer (cstream, blocking, cancellable, NULL);
534 g_converter_output_stream_write (GOutputStream *stream,
537 GCancellable *cancellable,
540 return write_internal (stream, buffer, count, TRUE, cancellable, error);
544 g_converter_output_stream_flush (GOutputStream *stream,
545 GCancellable *cancellable,
548 GConverterOutputStream *cstream;
549 GConverterOutputStreamPrivate *priv;
550 GConverterResult res;
557 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
558 priv = cstream->priv;
560 is_closing = g_output_stream_is_closing (stream);
562 /* Write out all available pre-converted data and fail if
564 if (!flush_buffer (cstream, TRUE, cancellable, error))
567 /* Ensure we have *some* initial target space */
568 buffer_ensure_space (&priv->converted_buffer, 1);
570 /* Convert whole buffer */
572 while (!priv->finished && !flushed)
574 /* Ensure we have *some* target space */
575 if (buffer_tailspace (&priv->converted_buffer) == 0)
576 grow_buffer (&priv->converted_buffer);
578 /* Try to convert to our buffer */
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,
590 if (res != G_CONVERTER_ERROR)
592 priv->converted_buffer.end += bytes_written;
593 buffer_consumed (&priv->output_buffer, bytes_read);
595 if (res == G_CONVERTER_FINISHED)
596 priv->finished = TRUE;
598 res == G_CONVERTER_FLUSHED)
600 /* Should not have returned FLUSHED with input left */
601 g_assert (buffer_data_size (&priv->output_buffer) == 0);
607 /* No-space errors can be handled locally: */
608 if (g_error_matches (my_error,
610 G_IO_ERROR_NO_SPACE))
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);
621 /* Any other error, including PARTIAL_INPUT can't be fixed by now
623 g_propagate_error (error, my_error);
628 /* Now write all converted data to base stream */
629 if (!flush_buffer (cstream, TRUE, cancellable, error))
636 g_converter_output_stream_can_poll (GPollableOutputStream *stream)
638 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
640 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
641 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
645 g_converter_output_stream_is_writable (GPollableOutputStream *stream)
647 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
649 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
653 g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
658 return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
663 g_converter_output_stream_create_source (GPollableOutputStream *stream,
664 GCancellable *cancellable)
666 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
667 GSource *base_source, *pollable_source;
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,
672 g_source_unref (base_source);
674 return pollable_source;
678 * g_converter_output_stream_get_converter:
679 * @converter_stream: a #GConverterOutputStream
681 * Gets the #GConverter that is used by @converter_stream.
683 * Returns: (transfer none): the converter of the converter output stream
688 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
690 return converter_stream->priv->converter;