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, see <http://www.gnu.org/licenses/>.
18 * Author: Alexander Larsson <alexl@redhat.com>
25 #include "gconverteroutputstream.h"
26 #include "gpollableoutputstream.h"
27 #include "gsimpleasyncresult.h"
28 #include "gcancellable.h"
29 #include "gioenumtypes.h"
35 * SECTION:gconverteroutputstream
36 * @short_description: Converter Output Stream
38 * @see_also: #GOutputStream, #GConverter
40 * Converter output stream implements #GOutputStream and allows
41 * conversion of data of various types during reading.
43 * As of GLib 2.34, #GConverterOutputStream implements
44 * #GPollableOutputStream.
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 static gboolean
g_converter_output_stream_can_poll (GPollableOutputStream
*stream
);
101 static gboolean
g_converter_output_stream_is_writable (GPollableOutputStream
*stream
);
102 static gssize
g_converter_output_stream_write_nonblocking (GPollableOutputStream
*stream
,
107 static GSource
*g_converter_output_stream_create_source (GPollableOutputStream
*stream
,
108 GCancellable
*cancellable
);
110 static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface
*iface
);
112 G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream
,
113 g_converter_output_stream
,
114 G_TYPE_FILTER_OUTPUT_STREAM
,
115 G_ADD_PRIVATE (GConverterOutputStream
)
116 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM
,
117 g_converter_output_stream_pollable_iface_init
))
120 g_converter_output_stream_class_init (GConverterOutputStreamClass
*klass
)
122 GObjectClass
*object_class
;
123 GOutputStreamClass
*istream_class
;
125 object_class
= G_OBJECT_CLASS (klass
);
126 object_class
->get_property
= g_converter_output_stream_get_property
;
127 object_class
->set_property
= g_converter_output_stream_set_property
;
128 object_class
->finalize
= g_converter_output_stream_finalize
;
130 istream_class
= G_OUTPUT_STREAM_CLASS (klass
);
131 istream_class
->write_fn
= g_converter_output_stream_write
;
132 istream_class
->flush
= g_converter_output_stream_flush
;
134 g_object_class_install_property (object_class
,
136 g_param_spec_object ("converter",
138 P_("The converter object"),
141 G_PARAM_CONSTRUCT_ONLY
|
142 G_PARAM_STATIC_STRINGS
));
147 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface
*iface
)
149 iface
->can_poll
= g_converter_output_stream_can_poll
;
150 iface
->is_writable
= g_converter_output_stream_is_writable
;
151 iface
->write_nonblocking
= g_converter_output_stream_write_nonblocking
;
152 iface
->create_source
= g_converter_output_stream_create_source
;
156 g_converter_output_stream_finalize (GObject
*object
)
158 GConverterOutputStreamPrivate
*priv
;
159 GConverterOutputStream
*stream
;
161 stream
= G_CONVERTER_OUTPUT_STREAM (object
);
164 g_free (priv
->output_buffer
.data
);
165 g_free (priv
->converted_buffer
.data
);
167 g_object_unref (priv
->converter
);
169 G_OBJECT_CLASS (g_converter_output_stream_parent_class
)->finalize (object
);
173 g_converter_output_stream_set_property (GObject
*object
,
178 GConverterOutputStream
*cstream
;
180 cstream
= G_CONVERTER_OUTPUT_STREAM (object
);
185 cstream
->priv
->converter
= g_value_dup_object (value
);
189 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
196 g_converter_output_stream_get_property (GObject
*object
,
201 GConverterOutputStreamPrivate
*priv
;
202 GConverterOutputStream
*cstream
;
204 cstream
= G_CONVERTER_OUTPUT_STREAM (object
);
205 priv
= cstream
->priv
;
210 g_value_set_object (value
, priv
->converter
);
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
220 g_converter_output_stream_init (GConverterOutputStream
*stream
)
222 stream
->priv
= g_converter_output_stream_get_instance_private (stream
);
226 * g_converter_output_stream_new:
227 * @base_stream: a #GOutputStream
228 * @converter: a #GConverter
230 * Creates a new converter output stream for the @base_stream.
232 * Returns: a new #GOutputStream.
235 g_converter_output_stream_new (GOutputStream
*base_stream
,
236 GConverter
*converter
)
238 GOutputStream
*stream
;
240 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream
), NULL
);
242 stream
= g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM
,
243 "base-stream", base_stream
,
244 "converter", converter
,
251 buffer_data_size (Buffer
*buffer
)
253 return buffer
->end
- buffer
->start
;
257 buffer_tailspace (Buffer
*buffer
)
259 return buffer
->size
- buffer
->end
;
263 buffer_data (Buffer
*buffer
)
265 return buffer
->data
+ buffer
->start
;
269 buffer_consumed (Buffer
*buffer
,
272 buffer
->start
+= count
;
273 if (buffer
->start
== buffer
->end
)
274 buffer
->start
= buffer
->end
= 0;
278 compact_buffer (Buffer
*buffer
)
282 in_buffer
= buffer_data_size (buffer
);
283 memmove (buffer
->data
,
284 buffer
->data
+ buffer
->start
,
286 buffer
->end
-= buffer
->start
;
291 grow_buffer (Buffer
*buffer
)
294 gsize size
, in_buffer
;
296 if (buffer
->size
== 0)
297 size
= INITIAL_BUFFER_SIZE
;
299 size
= buffer
->size
* 2;
301 data
= g_malloc (size
);
302 in_buffer
= buffer_data_size (buffer
);
305 buffer
->data
+ buffer
->start
,
307 g_free (buffer
->data
);
309 buffer
->end
-= buffer
->start
;
314 /* Ensures that the buffer can fit at_least_size bytes,
315 * *including* the current in-buffer data */
317 buffer_ensure_space (Buffer
*buffer
,
320 gsize in_buffer
, left_to_fill
;
322 in_buffer
= buffer_data_size (buffer
);
324 if (in_buffer
>= at_least_size
)
327 left_to_fill
= buffer_tailspace (buffer
);
329 if (in_buffer
+ left_to_fill
>= at_least_size
)
331 /* We fit in remaining space at end */
332 /* If the copy is small, compact now anyway so we can fill more */
334 compact_buffer (buffer
);
336 else if (buffer
->size
>= at_least_size
)
338 /* We fit, but only if we compact */
339 compact_buffer (buffer
);
343 /* Need to grow buffer */
344 while (buffer
->size
< at_least_size
)
345 grow_buffer (buffer
);
350 buffer_append (Buffer
*buffer
,
354 buffer_ensure_space (buffer
,
355 buffer_data_size (buffer
) + data_size
);
356 memcpy (buffer
->data
+ buffer
->end
, data
, data_size
);
357 buffer
->end
+= data_size
;
362 flush_buffer (GConverterOutputStream
*stream
,
364 GCancellable
*cancellable
,
367 GConverterOutputStreamPrivate
*priv
;
368 GOutputStream
*base_stream
;
375 base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
377 available
= buffer_data_size (&priv
->converted_buffer
);
380 res
= g_pollable_stream_write_all (base_stream
,
381 buffer_data (&priv
->converted_buffer
),
387 buffer_consumed (&priv
->converted_buffer
, nwritten
);
395 write_internal (GOutputStream
*stream
,
399 GCancellable
*cancellable
,
402 GConverterOutputStream
*cstream
;
403 GConverterOutputStreamPrivate
*priv
;
405 GConverterResult res
;
409 const char *to_convert
;
410 gsize to_convert_size
, converted_bytes
;
411 gboolean converting_from_buffer
;
413 cstream
= G_CONVERTER_OUTPUT_STREAM (stream
);
414 priv
= cstream
->priv
;
416 /* Write out all available pre-converted data and fail if
418 if (!flush_buffer (cstream
, blocking
, cancellable
, error
))
424 /* Convert as much as possible */
425 if (buffer_data_size (&priv
->output_buffer
) > 0)
427 converting_from_buffer
= TRUE
;
428 buffer_append (&priv
->output_buffer
, buffer
, count
);
429 to_convert
= buffer_data (&priv
->output_buffer
);
430 to_convert_size
= buffer_data_size (&priv
->output_buffer
);
434 converting_from_buffer
= FALSE
;
436 to_convert_size
= count
;
439 /* Ensure we have *some* initial target space */
440 buffer_ensure_space (&priv
->converted_buffer
, to_convert_size
);
443 while (!priv
->finished
&& converted_bytes
< to_convert_size
)
445 /* Ensure we have *some* target space */
446 if (buffer_tailspace (&priv
->converted_buffer
) == 0)
447 grow_buffer (&priv
->converted_buffer
);
449 /* Try to convert to our buffer */
451 res
= g_converter_convert (priv
->converter
,
452 to_convert
+ converted_bytes
,
453 to_convert_size
- converted_bytes
,
454 buffer_data (&priv
->converted_buffer
) + buffer_data_size (&priv
->converted_buffer
),
455 buffer_tailspace (&priv
->converted_buffer
),
461 if (res
!= G_CONVERTER_ERROR
)
463 priv
->converted_buffer
.end
+= bytes_written
;
464 converted_bytes
+= bytes_read
;
466 if (res
== G_CONVERTER_FINISHED
)
467 priv
->finished
= TRUE
;
471 /* No-space errors can be handled locally: */
472 if (g_error_matches (my_error
,
474 G_IO_ERROR_NO_SPACE
))
476 /* Need more destination space, grow it
477 * Note: if we actually grow the buffer (as opposed to compacting it),
478 * this will double the size, not just add one byte. */
479 buffer_ensure_space (&priv
->converted_buffer
,
480 priv
->converted_buffer
.size
+ 1);
481 g_error_free (my_error
);
485 if (converted_bytes
> 0)
487 /* We got an conversion error, but we did convert some bytes before
488 that, so handle those before reporting the error */
489 g_error_free (my_error
);
493 if (g_error_matches (my_error
,
495 G_IO_ERROR_PARTIAL_INPUT
))
497 /* Consume everything to buffer that we append to next time
499 if (!converting_from_buffer
)
500 buffer_append (&priv
->output_buffer
, buffer
, count
);
501 /* in the converting_from_buffer case we already appended this */
503 g_error_free (my_error
);
504 return count
; /* consume everything */
507 /* Converted no data and got an normal error, return it */
508 g_propagate_error (error
, my_error
);
513 if (converting_from_buffer
)
515 buffer_consumed (&priv
->output_buffer
, converted_bytes
);
519 retval
= converted_bytes
;
521 /* We now successfully consumed retval bytes, so we can't return an error,
522 even if writing this to the base stream fails. If it does we'll just
523 stop early and report this error when we try again on the next
525 flush_buffer (cstream
, blocking
, cancellable
, NULL
);
531 g_converter_output_stream_write (GOutputStream
*stream
,
534 GCancellable
*cancellable
,
537 return write_internal (stream
, buffer
, count
, TRUE
, cancellable
, error
);
541 g_converter_output_stream_flush (GOutputStream
*stream
,
542 GCancellable
*cancellable
,
545 GConverterOutputStream
*cstream
;
546 GConverterOutputStreamPrivate
*priv
;
547 GConverterResult res
;
554 cstream
= G_CONVERTER_OUTPUT_STREAM (stream
);
555 priv
= cstream
->priv
;
557 is_closing
= g_output_stream_is_closing (stream
);
559 /* Write out all available pre-converted data and fail if
561 if (!flush_buffer (cstream
, TRUE
, cancellable
, error
))
564 /* Ensure we have *some* initial target space */
565 buffer_ensure_space (&priv
->converted_buffer
, 1);
567 /* Convert whole buffer */
569 while (!priv
->finished
&& !flushed
)
571 /* Ensure we have *some* target space */
572 if (buffer_tailspace (&priv
->converted_buffer
) == 0)
573 grow_buffer (&priv
->converted_buffer
);
575 /* Try to convert to our buffer */
577 res
= g_converter_convert (priv
->converter
,
578 buffer_data (&priv
->output_buffer
),
579 buffer_data_size (&priv
->output_buffer
),
580 buffer_data (&priv
->converted_buffer
) + buffer_data_size (&priv
->converted_buffer
),
581 buffer_tailspace (&priv
->converted_buffer
),
582 is_closing
? G_CONVERTER_INPUT_AT_END
: G_CONVERTER_FLUSH
,
587 if (res
!= G_CONVERTER_ERROR
)
589 priv
->converted_buffer
.end
+= bytes_written
;
590 buffer_consumed (&priv
->output_buffer
, bytes_read
);
592 if (res
== G_CONVERTER_FINISHED
)
593 priv
->finished
= TRUE
;
595 res
== G_CONVERTER_FLUSHED
)
597 /* Should not have retured FLUSHED with input left */
598 g_assert (buffer_data_size (&priv
->output_buffer
) == 0);
604 /* No-space errors can be handled locally: */
605 if (g_error_matches (my_error
,
607 G_IO_ERROR_NO_SPACE
))
609 /* Need more destination space, grow it
610 * Note: if we actually grow the buffer (as opposed to compacting it),
611 * this will double the size, not just add one byte. */
612 buffer_ensure_space (&priv
->converted_buffer
,
613 priv
->converted_buffer
.size
+ 1);
614 g_error_free (my_error
);
618 /* Any other error, including PARTIAL_INPUT can't be fixed by now
620 g_propagate_error (error
, my_error
);
625 /* Now write all converted data to base stream */
626 if (!flush_buffer (cstream
, TRUE
, cancellable
, error
))
633 g_converter_output_stream_can_poll (GPollableOutputStream
*stream
)
635 GOutputStream
*base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
637 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream
) &&
638 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream
)));
642 g_converter_output_stream_is_writable (GPollableOutputStream
*stream
)
644 GOutputStream
*base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
646 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream
));
650 g_converter_output_stream_write_nonblocking (GPollableOutputStream
*stream
,
655 return write_internal (G_OUTPUT_STREAM (stream
), buffer
, count
, FALSE
,
660 g_converter_output_stream_create_source (GPollableOutputStream
*stream
,
661 GCancellable
*cancellable
)
663 GOutputStream
*base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
664 GSource
*base_source
, *pollable_source
;
666 base_source
= g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream
), NULL
);
667 pollable_source
= g_pollable_source_new_full (stream
, base_source
,
669 g_source_unref (base_source
);
671 return pollable_source
;
675 * g_converter_output_stream_get_converter:
676 * @converter_stream: a #GConverterOutputStream
678 * Gets the #GConverter that is used by @converter_stream.
680 * Returns: (transfer none): the converter of the converter output stream
685 g_converter_output_stream_get_converter (GConverterOutputStream
*converter_stream
)
687 return converter_stream
->priv
->converter
;