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 "gsimpleasyncresult.h"
29 #include "gcancellable.h"
30 #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.
47 #define INITIAL_BUFFER_SIZE 4096
56 struct _GConverterOutputStreamPrivate {
57 gboolean at_output_end;
59 GConverter *converter;
60 Buffer output_buffer; /* To be converted and written */
61 Buffer converted_buffer; /* Already converted */
64 /* Buffering strategy:
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).
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.
82 static void g_converter_output_stream_set_property (GObject *object,
86 static void g_converter_output_stream_get_property (GObject *object,
90 static void g_converter_output_stream_finalize (GObject *object);
91 static gssize g_converter_output_stream_write (GOutputStream *stream,
94 GCancellable *cancellable,
96 static gboolean g_converter_output_stream_flush (GOutputStream *stream,
97 GCancellable *cancellable,
100 G_DEFINE_TYPE (GConverterOutputStream,
101 g_converter_output_stream,
102 G_TYPE_FILTER_OUTPUT_STREAM)
105 g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
107 GObjectClass *object_class;
108 GOutputStreamClass *istream_class;
110 g_type_class_add_private (klass, sizeof (GConverterOutputStreamPrivate));
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;
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;
121 g_object_class_install_property (object_class,
123 g_param_spec_object ("converter",
125 P_("The converter object"),
128 G_PARAM_CONSTRUCT_ONLY|
129 G_PARAM_STATIC_STRINGS));
134 g_converter_output_stream_finalize (GObject *object)
136 GConverterOutputStreamPrivate *priv;
137 GConverterOutputStream *stream;
139 stream = G_CONVERTER_OUTPUT_STREAM (object);
142 g_free (priv->output_buffer.data);
143 g_free (priv->converted_buffer.data);
145 g_object_unref (priv->converter);
147 G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
151 g_converter_output_stream_set_property (GObject *object,
156 GConverterOutputStream *cstream;
158 cstream = G_CONVERTER_OUTPUT_STREAM (object);
163 cstream->priv->converter = g_value_dup_object (value);
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
174 g_converter_output_stream_get_property (GObject *object,
179 GConverterOutputStreamPrivate *priv;
180 GConverterOutputStream *cstream;
182 cstream = G_CONVERTER_OUTPUT_STREAM (object);
183 priv = cstream->priv;
188 g_value_set_object (value, priv->converter);
192 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198 g_converter_output_stream_init (GConverterOutputStream *stream)
200 stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
201 G_TYPE_CONVERTER_OUTPUT_STREAM,
202 GConverterOutputStreamPrivate);
206 * g_converter_output_stream_new:
207 * @base_stream: a #GOutputStream
208 * @converter: a #GConverter
210 * Creates a new converter output stream for the @base_stream.
212 * Returns: a new #GOutputStream.
215 g_converter_output_stream_new (GOutputStream *base_stream,
216 GConverter *converter)
218 GOutputStream *stream;
220 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
222 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
223 "base-stream", base_stream,
224 "converter", converter,
231 buffer_available (Buffer *buffer)
233 return buffer->end - buffer->start;
237 buffer_tailspace (Buffer *buffer)
239 return buffer->size - buffer->end;
243 buffer_data (Buffer *buffer)
245 return buffer->data + buffer->start;
249 buffer_consumed (Buffer *buffer,
252 buffer->start += count;
253 if (buffer->start == buffer->end)
254 buffer->start = buffer->end = 0;
258 compact_buffer (Buffer *buffer)
262 in_buffer = buffer_available (buffer);
263 memmove (buffer->data,
264 buffer->data + buffer->start,
266 buffer->end -= buffer->start;
271 grow_buffer (Buffer *buffer)
274 gsize size, in_buffer;
276 if (buffer->size == 0)
277 size = INITIAL_BUFFER_SIZE;
279 size = buffer->size * 2;
281 data = g_malloc (size);
282 in_buffer = buffer_available (buffer);
285 buffer->data + buffer->start,
287 g_free (buffer->data);
289 buffer->end -= buffer->start;
295 buffer_ensure_space (Buffer *buffer,
298 gsize in_buffer, left_to_fill;
300 in_buffer = buffer_available (buffer);
302 if (in_buffer >= at_least_size)
305 left_to_fill = buffer_tailspace (buffer);
307 if (in_buffer + left_to_fill >= at_least_size)
309 /* We fit in remaining space at end */
310 /* If the copy is small, compact now anyway so we can fill more */
312 compact_buffer (buffer);
314 else if (buffer->size >= at_least_size)
316 /* We fit, but only if we compact */
317 compact_buffer (buffer);
321 /* Need to grow buffer */
322 while (buffer->size < at_least_size)
323 grow_buffer (buffer);
328 buffer_append (Buffer *buffer,
332 buffer_ensure_space (buffer,
333 buffer_available (buffer) + data_size);
334 memcpy (buffer->data + buffer->end, data, data_size);
335 buffer->end += data_size;
340 flush_buffer (GConverterOutputStream *stream,
342 GCancellable *cancellable,
345 GConverterOutputStreamPrivate *priv;
346 GOutputStream *base_stream;
353 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
355 available = buffer_available (&priv->converted_buffer);
358 res = g_output_stream_write_all (base_stream,
359 buffer_data (&priv->converted_buffer),
364 buffer_consumed (&priv->converted_buffer, nwritten);
372 g_converter_output_stream_write (GOutputStream *stream,
375 GCancellable *cancellable,
378 GConverterOutputStream *cstream;
379 GConverterOutputStreamPrivate *priv;
381 GConverterResult res;
385 const char *to_convert;
386 gsize to_convert_size, converted_bytes;
387 gboolean converting_from_buffer;
389 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
390 priv = cstream->priv;
392 /* Write out all available pre-converted data and fail if
394 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
400 /* Convert as much as possible */
401 if (buffer_available (&priv->output_buffer) > 0)
403 converting_from_buffer = TRUE;
404 buffer_append (&priv->output_buffer, buffer, count);
405 to_convert = buffer_data (&priv->output_buffer);
406 to_convert_size = buffer_available (&priv->output_buffer);
410 converting_from_buffer = FALSE;
412 to_convert_size = count;
415 /* Ensure we have *some* initial target space */
416 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
419 while (!priv->finished && converted_bytes < to_convert_size)
421 /* Try to convert to our buffer */
423 res = g_converter_convert (priv->converter,
424 to_convert + converted_bytes,
425 to_convert_size - converted_bytes,
426 buffer_data (&priv->converted_buffer),
427 buffer_tailspace (&priv->converted_buffer),
433 if (res != G_CONVERTER_ERROR)
435 priv->converted_buffer.end += bytes_written;
436 converted_bytes += bytes_read;
438 if (res == G_CONVERTER_FINISHED)
439 priv->finished = TRUE;
443 /* No-space errors can be handled locally: */
444 if (g_error_matches (my_error,
446 G_IO_ERROR_NO_SPACE))
448 /* Need more destination space, grow it
449 * Note: if we actually grow the buffer (as opposed to compacting it),
450 * this will double the size, not just add one byte. */
451 buffer_ensure_space (&priv->converted_buffer,
452 priv->converted_buffer.size + 1);
453 g_error_free (my_error);
457 if (converted_bytes > 0)
459 /* We got an conversion error, but we did convert some bytes before
460 that, so handle those before reporting the error */
461 g_error_free (my_error);
465 if (g_error_matches (my_error,
467 G_IO_ERROR_PARTIAL_INPUT))
469 /* Consume everything to buffer that we append to next time
471 if (!converting_from_buffer)
472 buffer_append (&priv->output_buffer, buffer, count);
473 /* in the converting_from_buffer case we already appended this */
475 return count; /* consume everything */
478 /* Converted no data and got an normal error, return it */
479 g_propagate_error (error, my_error);
484 if (converting_from_buffer)
486 buffer_consumed (&priv->output_buffer, converted_bytes);
490 retval = converted_bytes;
492 /* We now successfully consumed retval bytes, so we can't return an error,
493 even if writing this to the base stream fails. If it does we'll just
494 stop early and report this error when we try again on the next
496 flush_buffer (cstream, &priv->converted_buffer, cancellable, NULL);
502 g_converter_output_stream_flush (GOutputStream *stream,
503 GCancellable *cancellable,
506 GConverterOutputStream *cstream;
507 GConverterOutputStreamPrivate *priv;
508 GConverterResult res;
515 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
516 priv = cstream->priv;
518 is_closing = g_output_stream_is_closing (stream);
520 /* Write out all available pre-converted data and fail if
522 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
525 /* Ensure we have *some* initial target space */
526 buffer_ensure_space (&priv->converted_buffer, 1);
528 /* Convert whole buffer */
530 while (!priv->finished && !flushed)
532 /* Try to convert to our buffer */
534 res = g_converter_convert (priv->converter,
535 buffer_data (&priv->output_buffer),
536 buffer_available (&priv->output_buffer),
537 buffer_data (&priv->converted_buffer),
538 buffer_tailspace (&priv->converted_buffer),
539 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
544 if (res != G_CONVERTER_ERROR)
546 priv->converted_buffer.end += bytes_written;
547 buffer_consumed (&priv->output_buffer, bytes_read);
549 if (res == G_CONVERTER_FINISHED)
550 priv->finished = TRUE;
552 res == G_CONVERTER_FLUSHED)
554 /* Should not have retured FLUSHED with input left */
555 g_assert (buffer_available (&priv->output_buffer) == 0);
561 /* No-space errors can be handled locally: */
562 if (g_error_matches (my_error,
564 G_IO_ERROR_NO_SPACE))
566 /* Need more destination space, grow it
567 * Note: if we actually grow the buffer (as opposed to compacting it),
568 * this will double the size, not just add one byte. */
569 buffer_ensure_space (&priv->converted_buffer,
570 priv->converted_buffer.size + 1);
571 g_error_free (my_error);
575 /* Any other error, including PARTIAL_INPUT can't be fixed by now
577 g_propagate_error (error, my_error);
582 /* Now write all converted data to base stream */
583 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
590 * g_converter_output_stream_get_converter:
591 * @converter_stream: a #GConverterOutputStream
593 * Gets the #GConverter that is used by @converter_stream.
595 * Returns: the converter of the converter output stream
600 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
602 return converter_stream->priv->converter;
605 #define __G_CONVERTER_OUTPUT_STREAM_C__
606 #include "gioaliasdef.c"