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.
209 * Creates a new converter output stream for the @base_stream.
211 * Returns: a new #GOutputStream.
214 g_converter_output_stream_new (GOutputStream *base_stream,
215 GConverter *converter)
217 GOutputStream *stream;
219 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
221 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
222 "base-stream", base_stream,
223 "converter", converter,
230 buffer_available (Buffer *buffer)
232 return buffer->end - buffer->start;
236 buffer_tailspace (Buffer *buffer)
238 return buffer->size - buffer->end;
242 buffer_data (Buffer *buffer)
244 return buffer->data + buffer->start;
248 buffer_consumed (Buffer *buffer,
251 buffer->start += count;
252 if (buffer->start == buffer->end)
253 buffer->start = buffer->end = 0;
257 compact_buffer (Buffer *buffer)
261 in_buffer = buffer_available (buffer);
262 memmove (buffer->data,
263 buffer->data + buffer->start,
265 buffer->end -= buffer->start;
270 grow_buffer (Buffer *buffer)
273 gsize size, in_buffer;
275 if (buffer->size == 0)
276 size = INITIAL_BUFFER_SIZE;
278 size = buffer->size * 2;
280 data = g_malloc (size);
281 in_buffer = buffer_available (buffer);
284 buffer->data + buffer->start,
286 g_free (buffer->data);
288 buffer->end -= buffer->start;
294 buffer_ensure_space (Buffer *buffer,
297 gsize in_buffer, left_to_fill;
299 in_buffer = buffer_available (buffer);
301 if (in_buffer >= at_least_size)
304 left_to_fill = buffer_tailspace (buffer);
306 if (in_buffer + left_to_fill >= at_least_size)
308 /* We fit in remaining space at end */
309 /* If the copy is small, compact now anyway so we can fill more */
311 compact_buffer (buffer);
313 else if (buffer->size >= at_least_size)
315 /* We fit, but only if we compact */
316 compact_buffer (buffer);
320 /* Need to grow buffer */
321 while (buffer->size < at_least_size)
322 grow_buffer (buffer);
327 buffer_append (Buffer *buffer,
331 buffer_ensure_space (buffer,
332 buffer_available (buffer) + data_size);
333 memcpy (buffer->data + buffer->end, data, data_size);
334 buffer->end += data_size;
339 flush_buffer (GConverterOutputStream *stream,
341 GCancellable *cancellable,
344 GConverterOutputStreamPrivate *priv;
345 GOutputStream *base_stream;
352 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
354 available = buffer_available (&priv->converted_buffer);
357 res = g_output_stream_write_all (base_stream,
358 buffer_data (&priv->converted_buffer),
363 buffer_consumed (&priv->converted_buffer, nwritten);
371 g_converter_output_stream_write (GOutputStream *stream,
374 GCancellable *cancellable,
377 GConverterOutputStream *cstream;
378 GConverterOutputStreamPrivate *priv;
380 GConverterResult res;
384 const char *to_convert;
385 gsize to_convert_size, converted_bytes;
386 gboolean converting_from_buffer;
388 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
389 priv = cstream->priv;
391 /* Write out all available pre-converted data and fail if
393 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
399 /* Convert as much as possible */
400 if (buffer_available (&priv->output_buffer) > 0)
402 converting_from_buffer = TRUE;
403 buffer_append (&priv->output_buffer, buffer, count);
404 to_convert = buffer_data (&priv->output_buffer);
405 to_convert_size = buffer_available (&priv->output_buffer);
409 converting_from_buffer = FALSE;
411 to_convert_size = count;
414 /* Ensure we have *some* initial target space */
415 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
418 while (!priv->finished && converted_bytes < to_convert_size)
420 /* Try to convert to our buffer */
422 res = g_converter_convert (priv->converter,
423 to_convert + converted_bytes,
424 to_convert_size - converted_bytes,
425 buffer_data (&priv->converted_buffer),
426 buffer_tailspace (&priv->converted_buffer),
432 if (res != G_CONVERTER_ERROR)
434 priv->converted_buffer.end += bytes_written;
435 converted_bytes += bytes_read;
437 if (res == G_CONVERTER_FINISHED)
438 priv->finished = TRUE;
442 /* No-space errors can be handled locally: */
443 if (g_error_matches (my_error,
445 G_IO_ERROR_NO_SPACE))
447 /* Need more destination space, grow it
448 * Note: if we actually grow the buffer (as opposed to compacting it),
449 * this will double the size, not just add one byte. */
450 buffer_ensure_space (&priv->converted_buffer,
451 priv->converted_buffer.size + 1);
452 g_error_free (my_error);
456 if (converted_bytes > 0)
458 /* We got an conversion error, but we did convert some bytes before
459 that, so handle those before reporting the error */
460 g_error_free (my_error);
464 if (g_error_matches (my_error,
466 G_IO_ERROR_PARTIAL_INPUT))
468 /* Consume everything to buffer that we append to next time
470 if (!converting_from_buffer)
471 buffer_append (&priv->output_buffer, buffer, count);
472 /* in the converting_from_buffer case we already appended this */
474 return count; /* consume everything */
477 /* Converted no data and got an normal error, return it */
478 g_propagate_error (error, my_error);
483 if (converting_from_buffer)
485 buffer_consumed (&priv->output_buffer, converted_bytes);
489 retval = converted_bytes;
491 /* We now successfully consumed retval bytes, so we can't return an error,
492 even if writing this to the base stream fails. If it does we'll just
493 stop early and report this error when we try again on the next
495 flush_buffer (cstream, &priv->converted_buffer, cancellable, NULL);
501 g_converter_output_stream_flush (GOutputStream *stream,
502 GCancellable *cancellable,
505 GConverterOutputStream *cstream;
506 GConverterOutputStreamPrivate *priv;
507 GConverterResult res;
514 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
515 priv = cstream->priv;
517 is_closing = g_output_stream_is_closing (stream);
519 /* Write out all available pre-converted data and fail if
521 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
524 /* Ensure we have *some* initial target space */
525 buffer_ensure_space (&priv->converted_buffer, 1);
527 /* Convert whole buffer */
529 while (!priv->finished && !flushed)
531 /* Try to convert to our buffer */
533 res = g_converter_convert (priv->converter,
534 buffer_data (&priv->output_buffer),
535 buffer_available (&priv->output_buffer),
536 buffer_data (&priv->converted_buffer),
537 buffer_tailspace (&priv->converted_buffer),
538 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
543 if (res != G_CONVERTER_ERROR)
545 priv->converted_buffer.end += bytes_written;
546 buffer_consumed (&priv->output_buffer, bytes_read);
548 if (res == G_CONVERTER_FINISHED)
549 priv->finished = TRUE;
551 res == G_CONVERTER_FLUSHED)
553 /* Should not have retured FLUSHED with input left */
554 g_assert (buffer_available (&priv->output_buffer) == 0);
560 /* No-space errors can be handled locally: */
561 if (g_error_matches (my_error,
563 G_IO_ERROR_NO_SPACE))
565 /* Need more destination space, grow it
566 * Note: if we actually grow the buffer (as opposed to compacting it),
567 * this will double the size, not just add one byte. */
568 buffer_ensure_space (&priv->converted_buffer,
569 priv->converted_buffer.size + 1);
570 g_error_free (my_error);
574 /* Any other error, including PARTIAL_INPUT can't be fixed by now
576 g_propagate_error (error, my_error);
581 /* Now write all converted data to base stream */
582 if (!flush_buffer (cstream, &priv->converted_buffer, cancellable, error))
589 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
591 return converter_stream->priv->converter;
594 #define __G_CONVERTER_OUTPUT_STREAM_C__
595 #include "gioaliasdef.c"