1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2009 Red Hat, Inc.
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.
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.
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.
20 * Author: Alexander Larsson <alexl@redhat.com>
27 #include "gconverteroutputstream.h"
28 #include "gpollableoutputstream.h"
29 #include "gsimpleasyncresult.h"
30 #include "gcancellable.h"
31 #include "gioenumtypes.h"
37 * SECTION:gconverteroutputstream
38 * @short_description: Converter Output Stream
40 * @see_also: #GOutputStream, #GConverter
42 * Converter output stream implements #GOutputStream and allows
43 * conversion of data of various types during reading.
45 * As of GLib 2.34, #GConverterOutputStream implements
46 * #GPollableOutputStream.
49 #define INITIAL_BUFFER_SIZE 4096
58 struct _GConverterOutputStreamPrivate {
59 gboolean at_output_end;
61 GConverter *converter;
62 Buffer output_buffer; /* To be converted and written */
63 Buffer converted_buffer; /* Already converted */
66 /* Buffering strategy:
68 * Each time we write we must at least consume some input, or
69 * return an error. Thus we start with writing all already
70 * converted data and *then* we start converting (reporting
71 * an error at any point in this).
73 * Its possible that what the user wrote is not enough data
74 * for the converter, so we must then buffer it in output_buffer
75 * and ask for more data, but we want to avoid this as much as
76 * possible, converting directly from the users buffer.
84 static void g_converter_output_stream_set_property (GObject *object,
88 static void g_converter_output_stream_get_property (GObject *object,
92 static void g_converter_output_stream_finalize (GObject *object);
93 static gssize g_converter_output_stream_write (GOutputStream *stream,
96 GCancellable *cancellable,
98 static gboolean g_converter_output_stream_flush (GOutputStream *stream,
99 GCancellable *cancellable,
102 static gboolean g_converter_output_stream_can_poll (GPollableOutputStream *stream);
103 static gboolean g_converter_output_stream_is_writable (GPollableOutputStream *stream);
104 static gssize g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
109 static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
110 GCancellable *cancellable);
112 static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
114 G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
115 g_converter_output_stream,
116 G_TYPE_FILTER_OUTPUT_STREAM,
117 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
118 g_converter_output_stream_pollable_iface_init);
122 g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
124 GObjectClass *object_class;
125 GOutputStreamClass *istream_class;
127 g_type_class_add_private (klass, sizeof (GConverterOutputStreamPrivate));
129 object_class = G_OBJECT_CLASS (klass);
130 object_class->get_property = g_converter_output_stream_get_property;
131 object_class->set_property = g_converter_output_stream_set_property;
132 object_class->finalize = g_converter_output_stream_finalize;
134 istream_class = G_OUTPUT_STREAM_CLASS (klass);
135 istream_class->write_fn = g_converter_output_stream_write;
136 istream_class->flush = g_converter_output_stream_flush;
138 g_object_class_install_property (object_class,
140 g_param_spec_object ("converter",
142 P_("The converter object"),
145 G_PARAM_CONSTRUCT_ONLY|
146 G_PARAM_STATIC_STRINGS));
151 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
153 iface->can_poll = g_converter_output_stream_can_poll;
154 iface->is_writable = g_converter_output_stream_is_writable;
155 iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
156 iface->create_source = g_converter_output_stream_create_source;
160 g_converter_output_stream_finalize (GObject *object)
162 GConverterOutputStreamPrivate *priv;
163 GConverterOutputStream *stream;
165 stream = G_CONVERTER_OUTPUT_STREAM (object);
168 g_free (priv->output_buffer.data);
169 g_free (priv->converted_buffer.data);
171 g_object_unref (priv->converter);
173 G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
177 g_converter_output_stream_set_property (GObject *object,
182 GConverterOutputStream *cstream;
184 cstream = G_CONVERTER_OUTPUT_STREAM (object);
189 cstream->priv->converter = g_value_dup_object (value);
193 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200 g_converter_output_stream_get_property (GObject *object,
205 GConverterOutputStreamPrivate *priv;
206 GConverterOutputStream *cstream;
208 cstream = G_CONVERTER_OUTPUT_STREAM (object);
209 priv = cstream->priv;
214 g_value_set_object (value, priv->converter);
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 g_converter_output_stream_init (GConverterOutputStream *stream)
226 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
227 G_TYPE_CONVERTER_OUTPUT_STREAM,
228 GConverterOutputStreamPrivate);
232 * g_converter_output_stream_new:
233 * @base_stream: a #GOutputStream
234 * @converter: a #GConverter
236 * Creates a new converter output stream for the @base_stream.
238 * Returns: a new #GOutputStream.
241 g_converter_output_stream_new (GOutputStream *base_stream,
242 GConverter *converter)
244 GOutputStream *stream;
246 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
248 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
249 "base-stream", base_stream,
250 "converter", converter,
257 buffer_data_size (Buffer *buffer)
259 return buffer->end - buffer->start;
263 buffer_tailspace (Buffer *buffer)
265 return buffer->size - buffer->end;
269 buffer_data (Buffer *buffer)
271 return buffer->data + buffer->start;
275 buffer_consumed (Buffer *buffer,
278 buffer->start += count;
279 if (buffer->start == buffer->end)
280 buffer->start = buffer->end = 0;
284 compact_buffer (Buffer *buffer)
288 in_buffer = buffer_data_size (buffer);
289 memmove (buffer->data,
290 buffer->data + buffer->start,
292 buffer->end -= buffer->start;
297 grow_buffer (Buffer *buffer)
300 gsize size, in_buffer;
302 if (buffer->size == 0)
303 size = INITIAL_BUFFER_SIZE;
305 size = buffer->size * 2;
307 data = g_malloc (size);
308 in_buffer = buffer_data_size (buffer);
311 buffer->data + buffer->start,
313 g_free (buffer->data);
315 buffer->end -= buffer->start;
320 /* Ensures that the buffer can fit at_least_size bytes,
321 * *including* the current in-buffer data */
323 buffer_ensure_space (Buffer *buffer,
326 gsize in_buffer, left_to_fill;
328 in_buffer = buffer_data_size (buffer);
330 if (in_buffer >= at_least_size)
333 left_to_fill = buffer_tailspace (buffer);
335 if (in_buffer + left_to_fill >= at_least_size)
337 /* We fit in remaining space at end */
338 /* If the copy is small, compact now anyway so we can fill more */
340 compact_buffer (buffer);
342 else if (buffer->size >= at_least_size)
344 /* We fit, but only if we compact */
345 compact_buffer (buffer);
349 /* Need to grow buffer */
350 while (buffer->size < at_least_size)
351 grow_buffer (buffer);
356 buffer_append (Buffer *buffer,
360 buffer_ensure_space (buffer,
361 buffer_data_size (buffer) + data_size);
362 memcpy (buffer->data + buffer->end, data, data_size);
363 buffer->end += data_size;
368 flush_buffer (GConverterOutputStream *stream,
370 GCancellable *cancellable,
373 GConverterOutputStreamPrivate *priv;
374 GOutputStream *base_stream;
381 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
383 available = buffer_data_size (&priv->converted_buffer);
386 res = g_pollable_stream_write_all (base_stream,
387 buffer_data (&priv->converted_buffer),
393 buffer_consumed (&priv->converted_buffer, nwritten);
401 write_internal (GOutputStream *stream,
405 GCancellable *cancellable,
408 GConverterOutputStream *cstream;
409 GConverterOutputStreamPrivate *priv;
411 GConverterResult res;
415 const char *to_convert;
416 gsize to_convert_size, converted_bytes;
417 gboolean converting_from_buffer;
419 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
420 priv = cstream->priv;
422 /* Write out all available pre-converted data and fail if
424 if (!flush_buffer (cstream, blocking, cancellable, error))
430 /* Convert as much as possible */
431 if (buffer_data_size (&priv->output_buffer) > 0)
433 converting_from_buffer = TRUE;
434 buffer_append (&priv->output_buffer, buffer, count);
435 to_convert = buffer_data (&priv->output_buffer);
436 to_convert_size = buffer_data_size (&priv->output_buffer);
440 converting_from_buffer = FALSE;
442 to_convert_size = count;
445 /* Ensure we have *some* initial target space */
446 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
449 while (!priv->finished && converted_bytes < to_convert_size)
451 /* Ensure we have *some* target space */
452 if (buffer_tailspace (&priv->converted_buffer) == 0)
453 grow_buffer (&priv->converted_buffer);
455 /* Try to convert to our buffer */
457 res = g_converter_convert (priv->converter,
458 to_convert + converted_bytes,
459 to_convert_size - converted_bytes,
460 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
461 buffer_tailspace (&priv->converted_buffer),
467 if (res != G_CONVERTER_ERROR)
469 priv->converted_buffer.end += bytes_written;
470 converted_bytes += bytes_read;
472 if (res == G_CONVERTER_FINISHED)
473 priv->finished = TRUE;
477 /* No-space errors can be handled locally: */
478 if (g_error_matches (my_error,
480 G_IO_ERROR_NO_SPACE))
482 /* Need more destination space, grow it
483 * Note: if we actually grow the buffer (as opposed to compacting it),
484 * this will double the size, not just add one byte. */
485 buffer_ensure_space (&priv->converted_buffer,
486 priv->converted_buffer.size + 1);
487 g_error_free (my_error);
491 if (converted_bytes > 0)
493 /* We got an conversion error, but we did convert some bytes before
494 that, so handle those before reporting the error */
495 g_error_free (my_error);
499 if (g_error_matches (my_error,
501 G_IO_ERROR_PARTIAL_INPUT))
503 /* Consume everything to buffer that we append to next time
505 if (!converting_from_buffer)
506 buffer_append (&priv->output_buffer, buffer, count);
507 /* in the converting_from_buffer case we already appended this */
509 g_error_free (my_error);
510 return count; /* consume everything */
513 /* Converted no data and got an normal error, return it */
514 g_propagate_error (error, my_error);
519 if (converting_from_buffer)
521 buffer_consumed (&priv->output_buffer, converted_bytes);
525 retval = converted_bytes;
527 /* We now successfully consumed retval bytes, so we can't return an error,
528 even if writing this to the base stream fails. If it does we'll just
529 stop early and report this error when we try again on the next
531 flush_buffer (cstream, blocking, cancellable, NULL);
537 g_converter_output_stream_write (GOutputStream *stream,
540 GCancellable *cancellable,
543 return write_internal (stream, buffer, count, TRUE, cancellable, error);
547 g_converter_output_stream_flush (GOutputStream *stream,
548 GCancellable *cancellable,
551 GConverterOutputStream *cstream;
552 GConverterOutputStreamPrivate *priv;
553 GConverterResult res;
560 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
561 priv = cstream->priv;
563 is_closing = g_output_stream_is_closing (stream);
565 /* Write out all available pre-converted data and fail if
567 if (!flush_buffer (cstream, TRUE, cancellable, error))
570 /* Ensure we have *some* initial target space */
571 buffer_ensure_space (&priv->converted_buffer, 1);
573 /* Convert whole buffer */
575 while (!priv->finished && !flushed)
577 /* Ensure we have *some* target space */
578 if (buffer_tailspace (&priv->converted_buffer) == 0)
579 grow_buffer (&priv->converted_buffer);
581 /* Try to convert to our buffer */
583 res = g_converter_convert (priv->converter,
584 buffer_data (&priv->output_buffer),
585 buffer_data_size (&priv->output_buffer),
586 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
587 buffer_tailspace (&priv->converted_buffer),
588 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
593 if (res != G_CONVERTER_ERROR)
595 priv->converted_buffer.end += bytes_written;
596 buffer_consumed (&priv->output_buffer, bytes_read);
598 if (res == G_CONVERTER_FINISHED)
599 priv->finished = TRUE;
601 res == G_CONVERTER_FLUSHED)
603 /* Should not have retured FLUSHED with input left */
604 g_assert (buffer_data_size (&priv->output_buffer) == 0);
610 /* No-space errors can be handled locally: */
611 if (g_error_matches (my_error,
613 G_IO_ERROR_NO_SPACE))
615 /* Need more destination space, grow it
616 * Note: if we actually grow the buffer (as opposed to compacting it),
617 * this will double the size, not just add one byte. */
618 buffer_ensure_space (&priv->converted_buffer,
619 priv->converted_buffer.size + 1);
620 g_error_free (my_error);
624 /* Any other error, including PARTIAL_INPUT can't be fixed by now
626 g_propagate_error (error, my_error);
631 /* Now write all converted data to base stream */
632 if (!flush_buffer (cstream, TRUE, cancellable, error))
639 g_converter_output_stream_can_poll (GPollableOutputStream *stream)
641 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
643 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
644 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
648 g_converter_output_stream_is_writable (GPollableOutputStream *stream)
650 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
652 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
656 g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
661 return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
666 g_converter_output_stream_create_source (GPollableOutputStream *stream,
667 GCancellable *cancellable)
669 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
670 GSource *base_source, *pollable_source;
672 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
673 pollable_source = g_pollable_source_new_full (stream, base_source,
675 g_source_unref (base_source);
677 return pollable_source;
681 * g_converter_output_stream_get_converter:
682 * @converter_stream: a #GConverterOutputStream
684 * Gets the #GConverter that is used by @converter_stream.
686 * Returns: (transfer none): the converter of the converter output stream
691 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
693 return converter_stream->priv->converter;