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_ADD_PRIVATE (GConverterOutputStream)
118 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
119 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 object_class = G_OBJECT_CLASS (klass);
128 object_class->get_property = g_converter_output_stream_get_property;
129 object_class->set_property = g_converter_output_stream_set_property;
130 object_class->finalize = g_converter_output_stream_finalize;
132 istream_class = G_OUTPUT_STREAM_CLASS (klass);
133 istream_class->write_fn = g_converter_output_stream_write;
134 istream_class->flush = g_converter_output_stream_flush;
136 g_object_class_install_property (object_class,
138 g_param_spec_object ("converter",
140 P_("The converter object"),
143 G_PARAM_CONSTRUCT_ONLY|
144 G_PARAM_STATIC_STRINGS));
149 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
151 iface->can_poll = g_converter_output_stream_can_poll;
152 iface->is_writable = g_converter_output_stream_is_writable;
153 iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
154 iface->create_source = g_converter_output_stream_create_source;
158 g_converter_output_stream_finalize (GObject *object)
160 GConverterOutputStreamPrivate *priv;
161 GConverterOutputStream *stream;
163 stream = G_CONVERTER_OUTPUT_STREAM (object);
166 g_free (priv->output_buffer.data);
167 g_free (priv->converted_buffer.data);
169 g_object_unref (priv->converter);
171 G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
175 g_converter_output_stream_set_property (GObject *object,
180 GConverterOutputStream *cstream;
182 cstream = G_CONVERTER_OUTPUT_STREAM (object);
187 cstream->priv->converter = g_value_dup_object (value);
191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198 g_converter_output_stream_get_property (GObject *object,
203 GConverterOutputStreamPrivate *priv;
204 GConverterOutputStream *cstream;
206 cstream = G_CONVERTER_OUTPUT_STREAM (object);
207 priv = cstream->priv;
212 g_value_set_object (value, priv->converter);
216 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222 g_converter_output_stream_init (GConverterOutputStream *stream)
224 stream->priv = g_converter_output_stream_get_instance_private (stream);
228 * g_converter_output_stream_new:
229 * @base_stream: a #GOutputStream
230 * @converter: a #GConverter
232 * Creates a new converter output stream for the @base_stream.
234 * Returns: a new #GOutputStream.
237 g_converter_output_stream_new (GOutputStream *base_stream,
238 GConverter *converter)
240 GOutputStream *stream;
242 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
244 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
245 "base-stream", base_stream,
246 "converter", converter,
253 buffer_data_size (Buffer *buffer)
255 return buffer->end - buffer->start;
259 buffer_tailspace (Buffer *buffer)
261 return buffer->size - buffer->end;
265 buffer_data (Buffer *buffer)
267 return buffer->data + buffer->start;
271 buffer_consumed (Buffer *buffer,
274 buffer->start += count;
275 if (buffer->start == buffer->end)
276 buffer->start = buffer->end = 0;
280 compact_buffer (Buffer *buffer)
284 in_buffer = buffer_data_size (buffer);
285 memmove (buffer->data,
286 buffer->data + buffer->start,
288 buffer->end -= buffer->start;
293 grow_buffer (Buffer *buffer)
296 gsize size, in_buffer;
298 if (buffer->size == 0)
299 size = INITIAL_BUFFER_SIZE;
301 size = buffer->size * 2;
303 data = g_malloc (size);
304 in_buffer = buffer_data_size (buffer);
307 buffer->data + buffer->start,
309 g_free (buffer->data);
311 buffer->end -= buffer->start;
316 /* Ensures that the buffer can fit at_least_size bytes,
317 * *including* the current in-buffer data */
319 buffer_ensure_space (Buffer *buffer,
322 gsize in_buffer, left_to_fill;
324 in_buffer = buffer_data_size (buffer);
326 if (in_buffer >= at_least_size)
329 left_to_fill = buffer_tailspace (buffer);
331 if (in_buffer + left_to_fill >= at_least_size)
333 /* We fit in remaining space at end */
334 /* If the copy is small, compact now anyway so we can fill more */
336 compact_buffer (buffer);
338 else if (buffer->size >= at_least_size)
340 /* We fit, but only if we compact */
341 compact_buffer (buffer);
345 /* Need to grow buffer */
346 while (buffer->size < at_least_size)
347 grow_buffer (buffer);
352 buffer_append (Buffer *buffer,
356 buffer_ensure_space (buffer,
357 buffer_data_size (buffer) + data_size);
358 memcpy (buffer->data + buffer->end, data, data_size);
359 buffer->end += data_size;
364 flush_buffer (GConverterOutputStream *stream,
366 GCancellable *cancellable,
369 GConverterOutputStreamPrivate *priv;
370 GOutputStream *base_stream;
377 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
379 available = buffer_data_size (&priv->converted_buffer);
382 res = g_pollable_stream_write_all (base_stream,
383 buffer_data (&priv->converted_buffer),
389 buffer_consumed (&priv->converted_buffer, nwritten);
397 write_internal (GOutputStream *stream,
401 GCancellable *cancellable,
404 GConverterOutputStream *cstream;
405 GConverterOutputStreamPrivate *priv;
407 GConverterResult res;
411 const char *to_convert;
412 gsize to_convert_size, converted_bytes;
413 gboolean converting_from_buffer;
415 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
416 priv = cstream->priv;
418 /* Write out all available pre-converted data and fail if
420 if (!flush_buffer (cstream, blocking, cancellable, error))
426 /* Convert as much as possible */
427 if (buffer_data_size (&priv->output_buffer) > 0)
429 converting_from_buffer = TRUE;
430 buffer_append (&priv->output_buffer, buffer, count);
431 to_convert = buffer_data (&priv->output_buffer);
432 to_convert_size = buffer_data_size (&priv->output_buffer);
436 converting_from_buffer = FALSE;
438 to_convert_size = count;
441 /* Ensure we have *some* initial target space */
442 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
445 while (!priv->finished && converted_bytes < to_convert_size)
447 /* Ensure we have *some* target space */
448 if (buffer_tailspace (&priv->converted_buffer) == 0)
449 grow_buffer (&priv->converted_buffer);
451 /* Try to convert to our buffer */
453 res = g_converter_convert (priv->converter,
454 to_convert + converted_bytes,
455 to_convert_size - converted_bytes,
456 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
457 buffer_tailspace (&priv->converted_buffer),
463 if (res != G_CONVERTER_ERROR)
465 priv->converted_buffer.end += bytes_written;
466 converted_bytes += bytes_read;
468 if (res == G_CONVERTER_FINISHED)
469 priv->finished = TRUE;
473 /* No-space errors can be handled locally: */
474 if (g_error_matches (my_error,
476 G_IO_ERROR_NO_SPACE))
478 /* Need more destination space, grow it
479 * Note: if we actually grow the buffer (as opposed to compacting it),
480 * this will double the size, not just add one byte. */
481 buffer_ensure_space (&priv->converted_buffer,
482 priv->converted_buffer.size + 1);
483 g_error_free (my_error);
487 if (converted_bytes > 0)
489 /* We got an conversion error, but we did convert some bytes before
490 that, so handle those before reporting the error */
491 g_error_free (my_error);
495 if (g_error_matches (my_error,
497 G_IO_ERROR_PARTIAL_INPUT))
499 /* Consume everything to buffer that we append to next time
501 if (!converting_from_buffer)
502 buffer_append (&priv->output_buffer, buffer, count);
503 /* in the converting_from_buffer case we already appended this */
505 g_error_free (my_error);
506 return count; /* consume everything */
509 /* Converted no data and got an normal error, return it */
510 g_propagate_error (error, my_error);
515 if (converting_from_buffer)
517 buffer_consumed (&priv->output_buffer, converted_bytes);
521 retval = converted_bytes;
523 /* We now successfully consumed retval bytes, so we can't return an error,
524 even if writing this to the base stream fails. If it does we'll just
525 stop early and report this error when we try again on the next
527 flush_buffer (cstream, blocking, cancellable, NULL);
533 g_converter_output_stream_write (GOutputStream *stream,
536 GCancellable *cancellable,
539 return write_internal (stream, buffer, count, TRUE, cancellable, error);
543 g_converter_output_stream_flush (GOutputStream *stream,
544 GCancellable *cancellable,
547 GConverterOutputStream *cstream;
548 GConverterOutputStreamPrivate *priv;
549 GConverterResult res;
556 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
557 priv = cstream->priv;
559 is_closing = g_output_stream_is_closing (stream);
561 /* Write out all available pre-converted data and fail if
563 if (!flush_buffer (cstream, TRUE, cancellable, error))
566 /* Ensure we have *some* initial target space */
567 buffer_ensure_space (&priv->converted_buffer, 1);
569 /* Convert whole buffer */
571 while (!priv->finished && !flushed)
573 /* Ensure we have *some* target space */
574 if (buffer_tailspace (&priv->converted_buffer) == 0)
575 grow_buffer (&priv->converted_buffer);
577 /* Try to convert to our buffer */
579 res = g_converter_convert (priv->converter,
580 buffer_data (&priv->output_buffer),
581 buffer_data_size (&priv->output_buffer),
582 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
583 buffer_tailspace (&priv->converted_buffer),
584 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
589 if (res != G_CONVERTER_ERROR)
591 priv->converted_buffer.end += bytes_written;
592 buffer_consumed (&priv->output_buffer, bytes_read);
594 if (res == G_CONVERTER_FINISHED)
595 priv->finished = TRUE;
597 res == G_CONVERTER_FLUSHED)
599 /* Should not have retured FLUSHED with input left */
600 g_assert (buffer_data_size (&priv->output_buffer) == 0);
606 /* No-space errors can be handled locally: */
607 if (g_error_matches (my_error,
609 G_IO_ERROR_NO_SPACE))
611 /* Need more destination space, grow it
612 * Note: if we actually grow the buffer (as opposed to compacting it),
613 * this will double the size, not just add one byte. */
614 buffer_ensure_space (&priv->converted_buffer,
615 priv->converted_buffer.size + 1);
616 g_error_free (my_error);
620 /* Any other error, including PARTIAL_INPUT can't be fixed by now
622 g_propagate_error (error, my_error);
627 /* Now write all converted data to base stream */
628 if (!flush_buffer (cstream, TRUE, cancellable, error))
635 g_converter_output_stream_can_poll (GPollableOutputStream *stream)
637 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
639 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
640 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
644 g_converter_output_stream_is_writable (GPollableOutputStream *stream)
646 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
648 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
652 g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
657 return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
662 g_converter_output_stream_create_source (GPollableOutputStream *stream,
663 GCancellable *cancellable)
665 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
666 GSource *base_source, *pollable_source;
668 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
669 pollable_source = g_pollable_source_new_full (stream, base_source,
671 g_source_unref (base_source);
673 return pollable_source;
677 * g_converter_output_stream_get_converter:
678 * @converter_stream: a #GConverterOutputStream
680 * Gets the #GConverter that is used by @converter_stream.
682 * Returns: (transfer none): the converter of the converter output stream
687 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
689 return converter_stream->priv->converter;