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_data_size (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_data_size (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_data_size (buffer);
285 buffer->data + buffer->start,
287 g_free (buffer->data);
289 buffer->end -= buffer->start;
294 /* Ensures that the buffer can fit at_least_size bytes,
295 * *including* the current in-buffer data */
297 buffer_ensure_space (Buffer *buffer,
300 gsize in_buffer, left_to_fill;
302 in_buffer = buffer_data_size (buffer);
304 if (in_buffer >= at_least_size)
307 left_to_fill = buffer_tailspace (buffer);
309 if (in_buffer + left_to_fill >= at_least_size)
311 /* We fit in remaining space at end */
312 /* If the copy is small, compact now anyway so we can fill more */
314 compact_buffer (buffer);
316 else if (buffer->size >= at_least_size)
318 /* We fit, but only if we compact */
319 compact_buffer (buffer);
323 /* Need to grow buffer */
324 while (buffer->size < at_least_size)
325 grow_buffer (buffer);
330 buffer_append (Buffer *buffer,
334 buffer_ensure_space (buffer,
335 buffer_data_size (buffer) + data_size);
336 memcpy (buffer->data + buffer->end, data, data_size);
337 buffer->end += data_size;
342 flush_buffer (GConverterOutputStream *stream,
344 GCancellable *cancellable,
347 GConverterOutputStreamPrivate *priv;
348 GOutputStream *base_stream;
355 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
357 available = buffer_data_size (&priv->converted_buffer);
360 res = g_output_stream_write_all (base_stream,
361 buffer_data (&priv->converted_buffer),
366 buffer_consumed (&priv->converted_buffer, nwritten);
374 g_converter_output_stream_write (GOutputStream *stream,
377 GCancellable *cancellable,
380 GConverterOutputStream *cstream;
381 GConverterOutputStreamPrivate *priv;
383 GConverterResult res;
387 const char *to_convert;
388 gsize to_convert_size, converted_bytes;
389 gboolean converting_from_buffer;
391 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
392 priv = cstream->priv;
394 /* Write out all available pre-converted data and fail if
396 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
402 /* Convert as much as possible */
403 if (buffer_data_size (&priv->output_buffer) > 0)
405 converting_from_buffer = TRUE;
406 buffer_append (&priv->output_buffer, buffer, count);
407 to_convert = buffer_data (&priv->output_buffer);
408 to_convert_size = buffer_data_size (&priv->output_buffer);
412 converting_from_buffer = FALSE;
414 to_convert_size = count;
417 /* Ensure we have *some* initial target space */
418 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
421 while (!priv->finished && converted_bytes < to_convert_size)
423 /* Ensure we have *some* target space */
424 if (buffer_tailspace (&priv->converted_buffer) == 0)
425 grow_buffer (&priv->converted_buffer);
427 /* Try to convert to our buffer */
429 res = g_converter_convert (priv->converter,
430 to_convert + converted_bytes,
431 to_convert_size - converted_bytes,
432 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
433 buffer_tailspace (&priv->converted_buffer),
439 if (res != G_CONVERTER_ERROR)
441 priv->converted_buffer.end += bytes_written;
442 converted_bytes += bytes_read;
444 if (res == G_CONVERTER_FINISHED)
445 priv->finished = TRUE;
449 /* No-space errors can be handled locally: */
450 if (g_error_matches (my_error,
452 G_IO_ERROR_NO_SPACE))
454 /* Need more destination space, grow it
455 * Note: if we actually grow the buffer (as opposed to compacting it),
456 * this will double the size, not just add one byte. */
457 buffer_ensure_space (&priv->converted_buffer,
458 priv->converted_buffer.size + 1);
459 g_error_free (my_error);
463 if (converted_bytes > 0)
465 /* We got an conversion error, but we did convert some bytes before
466 that, so handle those before reporting the error */
467 g_error_free (my_error);
471 if (g_error_matches (my_error,
473 G_IO_ERROR_PARTIAL_INPUT))
475 /* Consume everything to buffer that we append to next time
477 if (!converting_from_buffer)
478 buffer_append (&priv->output_buffer, buffer, count);
479 /* in the converting_from_buffer case we already appended this */
481 return count; /* consume everything */
484 /* Converted no data and got an normal error, return it */
485 g_propagate_error (error, my_error);
490 if (converting_from_buffer)
492 buffer_consumed (&priv->output_buffer, converted_bytes);
496 retval = converted_bytes;
498 /* We now successfully consumed retval bytes, so we can't return an error,
499 even if writing this to the base stream fails. If it does we'll just
500 stop early and report this error when we try again on the next
502 flush_buffer (cstream, &priv->converted_buffer, cancellable, NULL);
508 g_converter_output_stream_flush (GOutputStream *stream,
509 GCancellable *cancellable,
512 GConverterOutputStream *cstream;
513 GConverterOutputStreamPrivate *priv;
514 GConverterResult res;
521 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
522 priv = cstream->priv;
524 is_closing = g_output_stream_is_closing (stream);
526 /* Write out all available pre-converted data and fail if
528 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
531 /* Ensure we have *some* initial target space */
532 buffer_ensure_space (&priv->converted_buffer, 1);
534 /* Convert whole buffer */
536 while (!priv->finished && !flushed)
538 /* Ensure we have *some* target space */
539 if (buffer_tailspace (&priv->converted_buffer) == 0)
540 grow_buffer (&priv->converted_buffer);
542 /* Try to convert to our buffer */
544 res = g_converter_convert (priv->converter,
545 buffer_data (&priv->output_buffer),
546 buffer_data_size (&priv->output_buffer),
547 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
548 buffer_tailspace (&priv->converted_buffer),
549 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
554 if (res != G_CONVERTER_ERROR)
556 priv->converted_buffer.end += bytes_written;
557 buffer_consumed (&priv->output_buffer, bytes_read);
559 if (res == G_CONVERTER_FINISHED)
560 priv->finished = TRUE;
562 res == G_CONVERTER_FLUSHED)
564 /* Should not have retured FLUSHED with input left */
565 g_assert (buffer_data_size (&priv->output_buffer) == 0);
571 /* No-space errors can be handled locally: */
572 if (g_error_matches (my_error,
574 G_IO_ERROR_NO_SPACE))
576 /* Need more destination space, grow it
577 * Note: if we actually grow the buffer (as opposed to compacting it),
578 * this will double the size, not just add one byte. */
579 buffer_ensure_space (&priv->converted_buffer,
580 priv->converted_buffer.size + 1);
581 g_error_free (my_error);
585 /* Any other error, including PARTIAL_INPUT can't be fixed by now
587 g_propagate_error (error, my_error);
592 /* Now write all converted data to base stream */
593 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
600 * g_converter_output_stream_get_converter:
601 * @converter_stream: a #GConverterOutputStream
603 * Gets the #GConverter that is used by @converter_stream.
605 * Returns: the converter of the converter output stream
610 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
612 return converter_stream->priv->converter;
615 #define __G_CONVERTER_OUTPUT_STREAM_C__
616 #include "gioaliasdef.c"