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 "gconverterinputstream.h"
26 #include "gpollableinputstream.h"
27 #include "gcancellable.h"
28 #include "gioenumtypes.h"
34 * SECTION:gconverterinputstream
35 * @short_description: Converter Input Stream
37 * @see_also: #GInputStream, #GConverter
39 * Converter input stream implements #GInputStream and allows
40 * conversion of data of various types during reading.
42 * As of GLib 2.34, #GConverterInputStream implements
43 * #GPollableInputStream.
46 #define INITIAL_BUFFER_SIZE 4096
55 struct _GConverterInputStreamPrivate
{
56 gboolean at_input_end
;
59 GConverter
*converter
;
61 Buffer converted_buffer
;
69 static void g_converter_input_stream_set_property (GObject
*object
,
73 static void g_converter_input_stream_get_property (GObject
*object
,
77 static void g_converter_input_stream_finalize (GObject
*object
);
78 static gssize
g_converter_input_stream_read (GInputStream
*stream
,
81 GCancellable
*cancellable
,
84 static gboolean
g_converter_input_stream_can_poll (GPollableInputStream
*stream
);
85 static gboolean
g_converter_input_stream_is_readable (GPollableInputStream
*stream
);
86 static gssize
g_converter_input_stream_read_nonblocking (GPollableInputStream
*stream
,
91 static GSource
*g_converter_input_stream_create_source (GPollableInputStream
*stream
,
92 GCancellable
*cancellable
);
94 static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface
*iface
);
96 G_DEFINE_TYPE_WITH_CODE (GConverterInputStream
,
97 g_converter_input_stream
,
98 G_TYPE_FILTER_INPUT_STREAM
,
99 G_ADD_PRIVATE (GConverterInputStream
)
100 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM
,
101 g_converter_input_stream_pollable_iface_init
))
104 g_converter_input_stream_class_init (GConverterInputStreamClass
*klass
)
106 GObjectClass
*object_class
;
107 GInputStreamClass
*istream_class
;
109 object_class
= G_OBJECT_CLASS (klass
);
110 object_class
->get_property
= g_converter_input_stream_get_property
;
111 object_class
->set_property
= g_converter_input_stream_set_property
;
112 object_class
->finalize
= g_converter_input_stream_finalize
;
114 istream_class
= G_INPUT_STREAM_CLASS (klass
);
115 istream_class
->read_fn
= g_converter_input_stream_read
;
117 g_object_class_install_property (object_class
,
119 g_param_spec_object ("converter",
121 P_("The converter object"),
124 G_PARAM_CONSTRUCT_ONLY
|
125 G_PARAM_STATIC_STRINGS
));
130 g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface
*iface
)
132 iface
->can_poll
= g_converter_input_stream_can_poll
;
133 iface
->is_readable
= g_converter_input_stream_is_readable
;
134 iface
->read_nonblocking
= g_converter_input_stream_read_nonblocking
;
135 iface
->create_source
= g_converter_input_stream_create_source
;
139 g_converter_input_stream_finalize (GObject
*object
)
141 GConverterInputStreamPrivate
*priv
;
142 GConverterInputStream
*stream
;
144 stream
= G_CONVERTER_INPUT_STREAM (object
);
147 g_free (priv
->input_buffer
.data
);
148 g_free (priv
->converted_buffer
.data
);
150 g_object_unref (priv
->converter
);
152 G_OBJECT_CLASS (g_converter_input_stream_parent_class
)->finalize (object
);
156 g_converter_input_stream_set_property (GObject
*object
,
161 GConverterInputStream
*cstream
;
163 cstream
= G_CONVERTER_INPUT_STREAM (object
);
168 cstream
->priv
->converter
= g_value_dup_object (value
);
172 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
179 g_converter_input_stream_get_property (GObject
*object
,
184 GConverterInputStreamPrivate
*priv
;
185 GConverterInputStream
*cstream
;
187 cstream
= G_CONVERTER_INPUT_STREAM (object
);
188 priv
= cstream
->priv
;
193 g_value_set_object (value
, priv
->converter
);
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
203 g_converter_input_stream_init (GConverterInputStream
*stream
)
205 stream
->priv
= g_converter_input_stream_get_instance_private (stream
);
209 * g_converter_input_stream_new:
210 * @base_stream: a #GInputStream
211 * @converter: a #GConverter
213 * Creates a new converter input stream for the @base_stream.
215 * Returns: a new #GInputStream.
218 g_converter_input_stream_new (GInputStream
*base_stream
,
219 GConverter
*converter
)
221 GInputStream
*stream
;
223 g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream
), NULL
);
225 stream
= g_object_new (G_TYPE_CONVERTER_INPUT_STREAM
,
226 "base-stream", base_stream
,
227 "converter", converter
,
234 buffer_data_size (Buffer
*buffer
)
236 return buffer
->end
- buffer
->start
;
240 buffer_tailspace (Buffer
*buffer
)
242 return buffer
->size
- buffer
->end
;
246 buffer_data (Buffer
*buffer
)
248 return buffer
->data
+ buffer
->start
;
252 buffer_consumed (Buffer
*buffer
,
255 buffer
->start
+= count
;
256 if (buffer
->start
== buffer
->end
)
257 buffer
->start
= buffer
->end
= 0;
261 buffer_read (Buffer
*buffer
,
266 memcpy (dest
, buffer
->data
+ buffer
->start
, count
);
268 buffer_consumed (buffer
, count
);
272 compact_buffer (Buffer
*buffer
)
276 in_buffer
= buffer_data_size (buffer
);
277 memmove (buffer
->data
,
278 buffer
->data
+ buffer
->start
,
280 buffer
->end
-= buffer
->start
;
285 grow_buffer (Buffer
*buffer
)
288 gsize size
, in_buffer
;
290 if (buffer
->size
== 0)
291 size
= INITIAL_BUFFER_SIZE
;
293 size
= buffer
->size
* 2;
295 data
= g_malloc (size
);
296 in_buffer
= buffer_data_size (buffer
);
300 buffer
->data
+ buffer
->start
,
303 g_free (buffer
->data
);
305 buffer
->end
-= buffer
->start
;
310 /* Ensures that the buffer can fit at_least_size bytes,
311 * *including* the current in-buffer data */
313 buffer_ensure_space (Buffer
*buffer
,
316 gsize in_buffer
, left_to_fill
;
318 in_buffer
= buffer_data_size (buffer
);
320 if (in_buffer
>= at_least_size
)
323 left_to_fill
= buffer_tailspace (buffer
);
325 if (in_buffer
+ left_to_fill
>= at_least_size
)
327 /* We fit in remaining space at end */
328 /* If the copy is small, compact now anyway so we can fill more */
330 compact_buffer (buffer
);
332 else if (buffer
->size
>= at_least_size
)
334 /* We fit, but only if we compact */
335 compact_buffer (buffer
);
339 /* Need to grow buffer */
340 while (buffer
->size
< at_least_size
)
341 grow_buffer (buffer
);
346 fill_input_buffer (GConverterInputStream
*stream
,
349 GCancellable
*cancellable
,
352 GConverterInputStreamPrivate
*priv
;
353 GInputStream
*base_stream
;
358 buffer_ensure_space (&priv
->input_buffer
, at_least_size
);
360 base_stream
= G_FILTER_INPUT_STREAM (stream
)->base_stream
;
361 nread
= g_pollable_stream_read (base_stream
,
362 priv
->input_buffer
.data
+ priv
->input_buffer
.end
,
363 buffer_tailspace (&priv
->input_buffer
),
370 priv
->input_buffer
.end
+= nread
;
371 priv
->need_input
= FALSE
;
379 read_internal (GInputStream
*stream
,
383 GCancellable
*cancellable
,
386 GConverterInputStream
*cstream
;
387 GConverterInputStreamPrivate
*priv
;
388 gsize available
, total_bytes_read
;
390 GConverterResult res
;
396 cstream
= G_CONVERTER_INPUT_STREAM (stream
);
397 priv
= cstream
->priv
;
399 available
= buffer_data_size (&priv
->converted_buffer
);
404 /* Converted data available, return that */
405 buffer_read (&priv
->converted_buffer
, buffer
, count
);
409 /* Full request not available, read all currently available and request
410 refill/conversion for more */
412 buffer_read (&priv
->converted_buffer
, buffer
, available
);
414 total_bytes_read
= available
;
415 buffer
= (char *) buffer
+ available
;
418 /* If there is no data to convert, and no pre-converted data,
419 do some i/o for more input */
420 if (buffer_data_size (&priv
->input_buffer
) == 0 &&
421 total_bytes_read
== 0 &&
424 nread
= fill_input_buffer (cstream
, count
, blocking
, cancellable
, error
);
428 priv
->at_input_end
= TRUE
;
431 /* First try to convert any available data (or state) directly to the user buffer: */
435 res
= g_converter_convert (priv
->converter
,
436 buffer_data (&priv
->input_buffer
),
437 buffer_data_size (&priv
->input_buffer
),
439 priv
->at_input_end
? G_CONVERTER_INPUT_AT_END
: 0,
443 if (res
!= G_CONVERTER_ERROR
)
445 total_bytes_read
+= bytes_written
;
446 buffer_consumed (&priv
->input_buffer
, bytes_read
);
447 if (res
== G_CONVERTER_FINISHED
)
448 priv
->finished
= TRUE
; /* We're done converting */
450 else if (total_bytes_read
== 0 &&
451 !g_error_matches (my_error
,
453 G_IO_ERROR_PARTIAL_INPUT
) &&
454 !g_error_matches (my_error
,
456 G_IO_ERROR_NO_SPACE
))
458 /* No previously read data and no "special" error, return error */
459 g_propagate_error (error
, my_error
);
463 g_error_free (my_error
);
466 /* We had some pre-converted data and/or we converted directly to the
468 if (total_bytes_read
> 0)
469 return total_bytes_read
;
471 /* If there is no more to convert, return EOF */
474 g_assert (buffer_data_size (&priv
->converted_buffer
) == 0);
478 /* There was "complexity" in the straight-to-buffer conversion,
479 * convert to our own buffer and write from that.
480 * At this point we didn't produce any data into @buffer.
483 /* Ensure we have *some* initial target space */
484 buffer_ensure_space (&priv
->converted_buffer
, count
);
488 g_assert (!priv
->finished
);
490 /* Try to convert to our buffer */
492 res
= g_converter_convert (priv
->converter
,
493 buffer_data (&priv
->input_buffer
),
494 buffer_data_size (&priv
->input_buffer
),
495 buffer_data (&priv
->converted_buffer
),
496 buffer_tailspace (&priv
->converted_buffer
),
497 priv
->at_input_end
? G_CONVERTER_INPUT_AT_END
: 0,
501 if (res
!= G_CONVERTER_ERROR
)
503 priv
->converted_buffer
.end
+= bytes_written
;
504 buffer_consumed (&priv
->input_buffer
, bytes_read
);
506 /* Maybe we consumed without producing any output */
507 if (buffer_data_size (&priv
->converted_buffer
) == 0 && res
!= G_CONVERTER_FINISHED
)
508 continue; /* Convert more */
510 if (res
== G_CONVERTER_FINISHED
)
511 priv
->finished
= TRUE
;
513 total_bytes_read
= MIN (count
, buffer_data_size (&priv
->converted_buffer
));
514 buffer_read (&priv
->converted_buffer
, buffer
, total_bytes_read
);
516 g_assert (priv
->finished
|| total_bytes_read
> 0);
518 return total_bytes_read
;
521 /* There was some kind of error filling our buffer */
523 if (g_error_matches (my_error
,
525 G_IO_ERROR_PARTIAL_INPUT
) &&
530 nread
= fill_input_buffer (cstream
,
531 buffer_data_size (&priv
->input_buffer
) + 4096,
537 /* Can't read any more data, return that error */
538 g_error_free (my_error
);
539 g_propagate_error (error
, my_error2
);
540 priv
->need_input
= TRUE
;
545 /* End of file, try INPUT_AT_END */
546 priv
->at_input_end
= TRUE
;
548 g_error_free (my_error
);
552 if (g_error_matches (my_error
,
554 G_IO_ERROR_NO_SPACE
))
556 /* Need more destination space, grow it
557 * Note: if we actually grow the buffer (as opposed to compacting it),
558 * this will double the size, not just add one byte. */
559 buffer_ensure_space (&priv
->converted_buffer
,
560 priv
->converted_buffer
.size
+ 1);
561 g_error_free (my_error
);
565 /* Any other random error, return it */
566 g_propagate_error (error
, my_error
);
570 g_assert_not_reached ();
574 g_converter_input_stream_read (GInputStream
*stream
,
577 GCancellable
*cancellable
,
580 return read_internal (stream
, buffer
, count
, TRUE
, cancellable
, error
);
584 g_converter_input_stream_can_poll (GPollableInputStream
*stream
)
586 GInputStream
*base_stream
= G_FILTER_INPUT_STREAM (stream
)->base_stream
;
588 return (G_IS_POLLABLE_INPUT_STREAM (base_stream
) &&
589 g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream
)));
593 g_converter_input_stream_is_readable (GPollableInputStream
*stream
)
595 GInputStream
*base_stream
= G_FILTER_INPUT_STREAM (stream
)->base_stream
;
596 GConverterInputStream
*cstream
= G_CONVERTER_INPUT_STREAM (stream
);
598 if (buffer_data_size (&cstream
->priv
->converted_buffer
))
600 else if (buffer_data_size (&cstream
->priv
->input_buffer
) &&
601 !cstream
->priv
->need_input
)
604 return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream
));
608 g_converter_input_stream_read_nonblocking (GPollableInputStream
*stream
,
613 return read_internal (G_INPUT_STREAM (stream
), buffer
, count
,
618 g_converter_input_stream_create_source (GPollableInputStream
*stream
,
619 GCancellable
*cancellable
)
621 GInputStream
*base_stream
= G_FILTER_INPUT_STREAM (stream
)->base_stream
;
622 GSource
*base_source
, *pollable_source
;
624 if (g_pollable_input_stream_is_readable (stream
))
625 base_source
= g_timeout_source_new (0);
627 base_source
= g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream
), NULL
);
629 pollable_source
= g_pollable_source_new_full (stream
, base_source
,
631 g_source_unref (base_source
);
633 return pollable_source
;
638 * g_converter_input_stream_get_converter:
639 * @converter_stream: a #GConverterInputStream
641 * Gets the #GConverter that is used by @converter_stream.
643 * Returns: (transfer none): the converter of the converter input stream
648 g_converter_input_stream_get_converter (GConverterInputStream
*converter_stream
)
650 return converter_stream
->priv
->converter
;