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.1 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 "gcancellable.h"
28 #include "gioenumtypes.h"
34 * SECTION:gconverteroutputstream
35 * @short_description: Converter Output Stream
37 * @see_also: #GOutputStream, #GConverter
39 * Converter output stream implements #GOutputStream and allows
40 * conversion of data of various types during reading.
42 * As of GLib 2.34, #GConverterOutputStream implements
43 * #GPollableOutputStream.
46 #define INITIAL_BUFFER_SIZE 4096
55 struct _GConverterOutputStreamPrivate
{
56 gboolean at_output_end
;
58 GConverter
*converter
;
59 Buffer output_buffer
; /* To be converted and written */
60 Buffer converted_buffer
; /* Already converted */
63 /* Buffering strategy:
65 * Each time we write we must at least consume some input, or
66 * return an error. Thus we start with writing all already
67 * converted data and *then* we start converting (reporting
68 * an error at any point in this).
70 * Its possible that what the user wrote is not enough data
71 * for the converter, so we must then buffer it in output_buffer
72 * and ask for more data, but we want to avoid this as much as
73 * possible, converting directly from the users buffer.
81 static void g_converter_output_stream_set_property (GObject
*object
,
85 static void g_converter_output_stream_get_property (GObject
*object
,
89 static void g_converter_output_stream_finalize (GObject
*object
);
90 static gssize
g_converter_output_stream_write (GOutputStream
*stream
,
93 GCancellable
*cancellable
,
95 static gboolean
g_converter_output_stream_flush (GOutputStream
*stream
,
96 GCancellable
*cancellable
,
99 static gboolean
g_converter_output_stream_can_poll (GPollableOutputStream
*stream
);
100 static gboolean
g_converter_output_stream_is_writable (GPollableOutputStream
*stream
);
101 static gssize
g_converter_output_stream_write_nonblocking (GPollableOutputStream
*stream
,
106 static GSource
*g_converter_output_stream_create_source (GPollableOutputStream
*stream
,
107 GCancellable
*cancellable
);
109 static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface
*iface
);
111 G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream
,
112 g_converter_output_stream
,
113 G_TYPE_FILTER_OUTPUT_STREAM
,
114 G_ADD_PRIVATE (GConverterOutputStream
)
115 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM
,
116 g_converter_output_stream_pollable_iface_init
))
119 g_converter_output_stream_class_init (GConverterOutputStreamClass
*klass
)
121 GObjectClass
*object_class
;
122 GOutputStreamClass
*istream_class
;
124 object_class
= G_OBJECT_CLASS (klass
);
125 object_class
->get_property
= g_converter_output_stream_get_property
;
126 object_class
->set_property
= g_converter_output_stream_set_property
;
127 object_class
->finalize
= g_converter_output_stream_finalize
;
129 istream_class
= G_OUTPUT_STREAM_CLASS (klass
);
130 istream_class
->write_fn
= g_converter_output_stream_write
;
131 istream_class
->flush
= g_converter_output_stream_flush
;
133 g_object_class_install_property (object_class
,
135 g_param_spec_object ("converter",
137 P_("The converter object"),
140 G_PARAM_CONSTRUCT_ONLY
|
141 G_PARAM_STATIC_STRINGS
));
146 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface
*iface
)
148 iface
->can_poll
= g_converter_output_stream_can_poll
;
149 iface
->is_writable
= g_converter_output_stream_is_writable
;
150 iface
->write_nonblocking
= g_converter_output_stream_write_nonblocking
;
151 iface
->create_source
= g_converter_output_stream_create_source
;
155 g_converter_output_stream_finalize (GObject
*object
)
157 GConverterOutputStreamPrivate
*priv
;
158 GConverterOutputStream
*stream
;
160 stream
= G_CONVERTER_OUTPUT_STREAM (object
);
163 g_free (priv
->output_buffer
.data
);
164 g_free (priv
->converted_buffer
.data
);
166 g_object_unref (priv
->converter
);
168 G_OBJECT_CLASS (g_converter_output_stream_parent_class
)->finalize (object
);
172 g_converter_output_stream_set_property (GObject
*object
,
177 GConverterOutputStream
*cstream
;
179 cstream
= G_CONVERTER_OUTPUT_STREAM (object
);
184 cstream
->priv
->converter
= g_value_dup_object (value
);
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
195 g_converter_output_stream_get_property (GObject
*object
,
200 GConverterOutputStreamPrivate
*priv
;
201 GConverterOutputStream
*cstream
;
203 cstream
= G_CONVERTER_OUTPUT_STREAM (object
);
204 priv
= cstream
->priv
;
209 g_value_set_object (value
, priv
->converter
);
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
219 g_converter_output_stream_init (GConverterOutputStream
*stream
)
221 stream
->priv
= g_converter_output_stream_get_instance_private (stream
);
225 * g_converter_output_stream_new:
226 * @base_stream: a #GOutputStream
227 * @converter: a #GConverter
229 * Creates a new converter output stream for the @base_stream.
231 * Returns: a new #GOutputStream.
234 g_converter_output_stream_new (GOutputStream
*base_stream
,
235 GConverter
*converter
)
237 GOutputStream
*stream
;
239 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream
), NULL
);
241 stream
= g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM
,
242 "base-stream", base_stream
,
243 "converter", converter
,
250 buffer_data_size (Buffer
*buffer
)
252 return buffer
->end
- buffer
->start
;
256 buffer_tailspace (Buffer
*buffer
)
258 return buffer
->size
- buffer
->end
;
262 buffer_data (Buffer
*buffer
)
264 return buffer
->data
+ buffer
->start
;
268 buffer_consumed (Buffer
*buffer
,
271 buffer
->start
+= count
;
272 if (buffer
->start
== buffer
->end
)
273 buffer
->start
= buffer
->end
= 0;
277 compact_buffer (Buffer
*buffer
)
281 in_buffer
= buffer_data_size (buffer
);
282 memmove (buffer
->data
,
283 buffer
->data
+ buffer
->start
,
285 buffer
->end
-= buffer
->start
;
290 grow_buffer (Buffer
*buffer
)
293 gsize size
, in_buffer
;
295 if (buffer
->size
== 0)
296 size
= INITIAL_BUFFER_SIZE
;
298 size
= buffer
->size
* 2;
300 data
= g_malloc (size
);
301 in_buffer
= buffer_data_size (buffer
);
305 buffer
->data
+ buffer
->start
,
308 g_free (buffer
->data
);
310 buffer
->end
-= buffer
->start
;
315 /* Ensures that the buffer can fit at_least_size bytes,
316 * *including* the current in-buffer data */
318 buffer_ensure_space (Buffer
*buffer
,
321 gsize in_buffer
, left_to_fill
;
323 in_buffer
= buffer_data_size (buffer
);
325 if (in_buffer
>= at_least_size
)
328 left_to_fill
= buffer_tailspace (buffer
);
330 if (in_buffer
+ left_to_fill
>= at_least_size
)
332 /* We fit in remaining space at end */
333 /* If the copy is small, compact now anyway so we can fill more */
335 compact_buffer (buffer
);
337 else if (buffer
->size
>= at_least_size
)
339 /* We fit, but only if we compact */
340 compact_buffer (buffer
);
344 /* Need to grow buffer */
345 while (buffer
->size
< at_least_size
)
346 grow_buffer (buffer
);
351 buffer_append (Buffer
*buffer
,
355 buffer_ensure_space (buffer
,
356 buffer_data_size (buffer
) + data_size
);
357 memcpy (buffer
->data
+ buffer
->end
, data
, data_size
);
358 buffer
->end
+= data_size
;
363 flush_buffer (GConverterOutputStream
*stream
,
365 GCancellable
*cancellable
,
368 GConverterOutputStreamPrivate
*priv
;
369 GOutputStream
*base_stream
;
376 base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
378 available
= buffer_data_size (&priv
->converted_buffer
);
381 res
= g_pollable_stream_write_all (base_stream
,
382 buffer_data (&priv
->converted_buffer
),
388 buffer_consumed (&priv
->converted_buffer
, nwritten
);
396 write_internal (GOutputStream
*stream
,
400 GCancellable
*cancellable
,
403 GConverterOutputStream
*cstream
;
404 GConverterOutputStreamPrivate
*priv
;
406 GConverterResult res
;
410 const char *to_convert
;
411 gsize to_convert_size
, converted_bytes
;
412 gboolean converting_from_buffer
;
414 cstream
= G_CONVERTER_OUTPUT_STREAM (stream
);
415 priv
= cstream
->priv
;
417 /* Write out all available pre-converted data and fail if
419 if (!flush_buffer (cstream
, blocking
, cancellable
, error
))
425 /* Convert as much as possible */
426 if (buffer_data_size (&priv
->output_buffer
) > 0)
428 converting_from_buffer
= TRUE
;
429 buffer_append (&priv
->output_buffer
, buffer
, count
);
430 to_convert
= buffer_data (&priv
->output_buffer
);
431 to_convert_size
= buffer_data_size (&priv
->output_buffer
);
435 converting_from_buffer
= FALSE
;
437 to_convert_size
= count
;
440 /* Ensure we have *some* initial target space */
441 buffer_ensure_space (&priv
->converted_buffer
, to_convert_size
);
444 while (!priv
->finished
&& converted_bytes
< to_convert_size
)
446 /* Ensure we have *some* target space */
447 if (buffer_tailspace (&priv
->converted_buffer
) == 0)
448 grow_buffer (&priv
->converted_buffer
);
450 /* Try to convert to our buffer */
452 res
= g_converter_convert (priv
->converter
,
453 to_convert
+ converted_bytes
,
454 to_convert_size
- converted_bytes
,
455 buffer_data (&priv
->converted_buffer
) + buffer_data_size (&priv
->converted_buffer
),
456 buffer_tailspace (&priv
->converted_buffer
),
462 if (res
!= G_CONVERTER_ERROR
)
464 priv
->converted_buffer
.end
+= bytes_written
;
465 converted_bytes
+= bytes_read
;
467 if (res
== G_CONVERTER_FINISHED
)
468 priv
->finished
= TRUE
;
472 /* No-space errors can be handled locally: */
473 if (g_error_matches (my_error
,
475 G_IO_ERROR_NO_SPACE
))
477 /* Need more destination space, grow it
478 * Note: if we actually grow the buffer (as opposed to compacting it),
479 * this will double the size, not just add one byte. */
480 buffer_ensure_space (&priv
->converted_buffer
,
481 priv
->converted_buffer
.size
+ 1);
482 g_error_free (my_error
);
486 if (converted_bytes
> 0)
488 /* We got an conversion error, but we did convert some bytes before
489 that, so handle those before reporting the error */
490 g_error_free (my_error
);
494 if (g_error_matches (my_error
,
496 G_IO_ERROR_PARTIAL_INPUT
))
498 /* Consume everything to buffer that we append to next time
500 if (!converting_from_buffer
)
501 buffer_append (&priv
->output_buffer
, buffer
, count
);
502 /* in the converting_from_buffer case we already appended this */
504 g_error_free (my_error
);
505 return count
; /* consume everything */
508 /* Converted no data and got an normal error, return it */
509 g_propagate_error (error
, my_error
);
514 if (converting_from_buffer
)
516 buffer_consumed (&priv
->output_buffer
, converted_bytes
);
520 retval
= converted_bytes
;
522 /* We now successfully consumed retval bytes, so we can't return an error,
523 even if writing this to the base stream fails. If it does we'll just
524 stop early and report this error when we try again on the next
526 flush_buffer (cstream
, blocking
, cancellable
, NULL
);
532 g_converter_output_stream_write (GOutputStream
*stream
,
535 GCancellable
*cancellable
,
538 return write_internal (stream
, buffer
, count
, TRUE
, cancellable
, error
);
542 g_converter_output_stream_flush (GOutputStream
*stream
,
543 GCancellable
*cancellable
,
546 GConverterOutputStream
*cstream
;
547 GConverterOutputStreamPrivate
*priv
;
548 GConverterResult res
;
555 cstream
= G_CONVERTER_OUTPUT_STREAM (stream
);
556 priv
= cstream
->priv
;
558 is_closing
= g_output_stream_is_closing (stream
);
560 /* Write out all available pre-converted data and fail if
562 if (!flush_buffer (cstream
, TRUE
, cancellable
, error
))
565 /* Ensure we have *some* initial target space */
566 buffer_ensure_space (&priv
->converted_buffer
, 1);
568 /* Convert whole buffer */
570 while (!priv
->finished
&& !flushed
)
572 /* Ensure we have *some* target space */
573 if (buffer_tailspace (&priv
->converted_buffer
) == 0)
574 grow_buffer (&priv
->converted_buffer
);
576 /* Try to convert to our buffer */
578 res
= g_converter_convert (priv
->converter
,
579 buffer_data (&priv
->output_buffer
),
580 buffer_data_size (&priv
->output_buffer
),
581 buffer_data (&priv
->converted_buffer
) + buffer_data_size (&priv
->converted_buffer
),
582 buffer_tailspace (&priv
->converted_buffer
),
583 is_closing
? G_CONVERTER_INPUT_AT_END
: G_CONVERTER_FLUSH
,
588 if (res
!= G_CONVERTER_ERROR
)
590 priv
->converted_buffer
.end
+= bytes_written
;
591 buffer_consumed (&priv
->output_buffer
, bytes_read
);
593 if (res
== G_CONVERTER_FINISHED
)
594 priv
->finished
= TRUE
;
596 res
== G_CONVERTER_FLUSHED
)
598 /* Should not have retured FLUSHED with input left */
599 g_assert (buffer_data_size (&priv
->output_buffer
) == 0);
605 /* No-space errors can be handled locally: */
606 if (g_error_matches (my_error
,
608 G_IO_ERROR_NO_SPACE
))
610 /* Need more destination space, grow it
611 * Note: if we actually grow the buffer (as opposed to compacting it),
612 * this will double the size, not just add one byte. */
613 buffer_ensure_space (&priv
->converted_buffer
,
614 priv
->converted_buffer
.size
+ 1);
615 g_error_free (my_error
);
619 /* Any other error, including PARTIAL_INPUT can't be fixed by now
621 g_propagate_error (error
, my_error
);
626 /* Now write all converted data to base stream */
627 if (!flush_buffer (cstream
, TRUE
, cancellable
, error
))
634 g_converter_output_stream_can_poll (GPollableOutputStream
*stream
)
636 GOutputStream
*base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
638 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream
) &&
639 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream
)));
643 g_converter_output_stream_is_writable (GPollableOutputStream
*stream
)
645 GOutputStream
*base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
647 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream
));
651 g_converter_output_stream_write_nonblocking (GPollableOutputStream
*stream
,
656 return write_internal (G_OUTPUT_STREAM (stream
), buffer
, count
, FALSE
,
661 g_converter_output_stream_create_source (GPollableOutputStream
*stream
,
662 GCancellable
*cancellable
)
664 GOutputStream
*base_stream
= G_FILTER_OUTPUT_STREAM (stream
)->base_stream
;
665 GSource
*base_source
, *pollable_source
;
667 base_source
= g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream
), NULL
);
668 pollable_source
= g_pollable_source_new_full (stream
, base_source
,
670 g_source_unref (base_source
);
672 return pollable_source
;
676 * g_converter_output_stream_get_converter:
677 * @converter_stream: a #GConverterOutputStream
679 * Gets the #GConverter that is used by @converter_stream.
681 * Returns: (transfer none): the converter of the converter output stream
686 g_converter_output_stream_get_converter (GConverterOutputStream
*converter_stream
)
688 return converter_stream
->priv
->converter
;