Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2009 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Alexander Larsson <alexl@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <string.h>
26 : :
27 : : #include "gconverterinputstream.h"
28 : : #include "gpollableinputstream.h"
29 : : #include "gcancellable.h"
30 : : #include "gioenumtypes.h"
31 : : #include "gioerror.h"
32 : : #include "glibintl.h"
33 : :
34 : :
35 : : /**
36 : : * GConverterInputStream:
37 : : *
38 : : * Converter input stream implements [class@Gio.InputStream] and allows
39 : : * conversion of data of various types during reading.
40 : : *
41 : : * As of GLib 2.34, `GConverterInputStream` implements
42 : : * [iface@Gio.PollableInputStream].
43 : : */
44 : :
45 : : #define INITIAL_BUFFER_SIZE 4096
46 : :
47 : : typedef struct {
48 : : char *data;
49 : : gsize start;
50 : : gsize end;
51 : : gsize size;
52 : : } Buffer;
53 : :
54 : : struct _GConverterInputStreamPrivate {
55 : : gboolean at_input_end;
56 : : gboolean finished;
57 : : gboolean need_input;
58 : : GConverter *converter;
59 : : Buffer input_buffer;
60 : : Buffer converted_buffer;
61 : : };
62 : :
63 : : enum {
64 : : PROP_0,
65 : : PROP_CONVERTER
66 : : };
67 : :
68 : : static void g_converter_input_stream_set_property (GObject *object,
69 : : guint prop_id,
70 : : const GValue *value,
71 : : GParamSpec *pspec);
72 : : static void g_converter_input_stream_get_property (GObject *object,
73 : : guint prop_id,
74 : : GValue *value,
75 : : GParamSpec *pspec);
76 : : static void g_converter_input_stream_finalize (GObject *object);
77 : : static gssize g_converter_input_stream_read (GInputStream *stream,
78 : : void *buffer,
79 : : gsize count,
80 : : GCancellable *cancellable,
81 : : GError **error);
82 : :
83 : : static gboolean g_converter_input_stream_can_poll (GPollableInputStream *stream);
84 : : static gboolean g_converter_input_stream_is_readable (GPollableInputStream *stream);
85 : : static gssize g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
86 : : void *buffer,
87 : : gsize size,
88 : : GError **error);
89 : :
90 : : static GSource *g_converter_input_stream_create_source (GPollableInputStream *stream,
91 : : GCancellable *cancellable);
92 : :
93 : : static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
94 : :
95 : 45024 : G_DEFINE_TYPE_WITH_CODE (GConverterInputStream,
96 : : g_converter_input_stream,
97 : : G_TYPE_FILTER_INPUT_STREAM,
98 : : G_ADD_PRIVATE (GConverterInputStream)
99 : : G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
100 : : g_converter_input_stream_pollable_iface_init))
101 : :
102 : : static void
103 : 4 : g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
104 : : {
105 : : GObjectClass *object_class;
106 : : GInputStreamClass *istream_class;
107 : :
108 : 4 : object_class = G_OBJECT_CLASS (klass);
109 : 4 : object_class->get_property = g_converter_input_stream_get_property;
110 : 4 : object_class->set_property = g_converter_input_stream_set_property;
111 : 4 : object_class->finalize = g_converter_input_stream_finalize;
112 : :
113 : 4 : istream_class = G_INPUT_STREAM_CLASS (klass);
114 : 4 : istream_class->read_fn = g_converter_input_stream_read;
115 : :
116 : : /**
117 : : * GConverterInputStream:converter:
118 : : *
119 : : * The converter object.
120 : : */
121 : 4 : g_object_class_install_property (object_class,
122 : : PROP_CONVERTER,
123 : : g_param_spec_object ("converter", NULL, NULL,
124 : : G_TYPE_CONVERTER,
125 : : G_PARAM_READWRITE|
126 : : G_PARAM_CONSTRUCT_ONLY|
127 : : G_PARAM_STATIC_STRINGS));
128 : :
129 : 4 : }
130 : :
131 : : static void
132 : 4 : g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
133 : : {
134 : 4 : iface->can_poll = g_converter_input_stream_can_poll;
135 : 4 : iface->is_readable = g_converter_input_stream_is_readable;
136 : 4 : iface->read_nonblocking = g_converter_input_stream_read_nonblocking;
137 : 4 : iface->create_source = g_converter_input_stream_create_source;
138 : 4 : }
139 : :
140 : : static void
141 : 29 : g_converter_input_stream_finalize (GObject *object)
142 : : {
143 : : GConverterInputStreamPrivate *priv;
144 : : GConverterInputStream *stream;
145 : :
146 : 29 : stream = G_CONVERTER_INPUT_STREAM (object);
147 : 29 : priv = stream->priv;
148 : :
149 : 29 : g_free (priv->input_buffer.data);
150 : 29 : g_free (priv->converted_buffer.data);
151 : 29 : if (priv->converter)
152 : 29 : g_object_unref (priv->converter);
153 : :
154 : 29 : G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object);
155 : 29 : }
156 : :
157 : : static void
158 : 29 : g_converter_input_stream_set_property (GObject *object,
159 : : guint prop_id,
160 : : const GValue *value,
161 : : GParamSpec *pspec)
162 : : {
163 : : GConverterInputStream *cstream;
164 : :
165 : 29 : cstream = G_CONVERTER_INPUT_STREAM (object);
166 : :
167 : 29 : switch (prop_id)
168 : : {
169 : 29 : case PROP_CONVERTER:
170 : 29 : cstream->priv->converter = g_value_dup_object (value);
171 : 29 : break;
172 : :
173 : 0 : default:
174 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175 : 0 : break;
176 : : }
177 : :
178 : 29 : }
179 : :
180 : : static void
181 : 1 : g_converter_input_stream_get_property (GObject *object,
182 : : guint prop_id,
183 : : GValue *value,
184 : : GParamSpec *pspec)
185 : : {
186 : : GConverterInputStreamPrivate *priv;
187 : : GConverterInputStream *cstream;
188 : :
189 : 1 : cstream = G_CONVERTER_INPUT_STREAM (object);
190 : 1 : priv = cstream->priv;
191 : :
192 : 1 : switch (prop_id)
193 : : {
194 : 1 : case PROP_CONVERTER:
195 : 1 : g_value_set_object (value, priv->converter);
196 : 1 : break;
197 : :
198 : 0 : default:
199 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200 : 0 : break;
201 : : }
202 : :
203 : 1 : }
204 : : static void
205 : 29 : g_converter_input_stream_init (GConverterInputStream *stream)
206 : : {
207 : 29 : stream->priv = g_converter_input_stream_get_instance_private (stream);
208 : 29 : }
209 : :
210 : : /**
211 : : * g_converter_input_stream_new:
212 : : * @base_stream: a #GInputStream
213 : : * @converter: a #GConverter
214 : : *
215 : : * Creates a new converter input stream for the @base_stream.
216 : : *
217 : : * Returns: a new #GInputStream.
218 : : **/
219 : : GInputStream *
220 : 29 : g_converter_input_stream_new (GInputStream *base_stream,
221 : : GConverter *converter)
222 : : {
223 : : GInputStream *stream;
224 : :
225 : 29 : g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
226 : :
227 : 29 : stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
228 : : "base-stream", base_stream,
229 : : "converter", converter,
230 : : NULL);
231 : :
232 : 29 : return stream;
233 : : }
234 : :
235 : : static gsize
236 : 60486 : buffer_data_size (Buffer *buffer)
237 : : {
238 : 60486 : return buffer->end - buffer->start;
239 : : }
240 : :
241 : : static gsize
242 : 7775 : buffer_tailspace (Buffer *buffer)
243 : : {
244 : 7775 : return buffer->size - buffer->end;
245 : : }
246 : :
247 : : static char *
248 : 3997 : buffer_data (Buffer *buffer)
249 : : {
250 : 3997 : return buffer->data + buffer->start;
251 : : }
252 : :
253 : : static void
254 : 48548 : buffer_consumed (Buffer *buffer,
255 : : gsize count)
256 : : {
257 : 48548 : buffer->start += count;
258 : 48548 : if (buffer->start == buffer->end)
259 : 7493 : buffer->start = buffer->end = 0;
260 : 48548 : }
261 : :
262 : : static void
263 : 44825 : buffer_read (Buffer *buffer,
264 : : char *dest,
265 : : gsize count)
266 : : {
267 : 44825 : if (count != 0)
268 : 41052 : memcpy (dest, buffer->data + buffer->start, count);
269 : :
270 : 44825 : buffer_consumed (buffer, count);
271 : 44825 : }
272 : :
273 : : static void
274 : 3744 : compact_buffer (Buffer *buffer)
275 : : {
276 : : gsize in_buffer;
277 : :
278 : 3744 : in_buffer = buffer_data_size (buffer);
279 : 3744 : memmove (buffer->data,
280 : 3744 : buffer->data + buffer->start,
281 : : in_buffer);
282 : 3744 : buffer->end -= buffer->start;
283 : 3744 : buffer->start = 0;
284 : 3744 : }
285 : :
286 : : static void
287 : 61 : grow_buffer (Buffer *buffer)
288 : : {
289 : : char *data;
290 : : gsize size, in_buffer;
291 : :
292 : 61 : if (buffer->size == 0)
293 : 39 : size = INITIAL_BUFFER_SIZE;
294 : : else
295 : 22 : size = buffer->size * 2;
296 : :
297 : 61 : data = g_malloc (size);
298 : 61 : in_buffer = buffer_data_size (buffer);
299 : :
300 : 61 : if (in_buffer != 0)
301 : 8 : memcpy (data,
302 : 8 : buffer->data + buffer->start,
303 : : in_buffer);
304 : :
305 : 61 : g_free (buffer->data);
306 : 61 : buffer->data = data;
307 : 61 : buffer->end -= buffer->start;
308 : 61 : buffer->start = 0;
309 : 61 : buffer->size = size;
310 : 61 : }
311 : :
312 : : /* Ensures that the buffer can fit at_least_size bytes,
313 : : * *including* the current in-buffer data */
314 : : static void
315 : 3861 : buffer_ensure_space (Buffer *buffer,
316 : : gsize at_least_size)
317 : : {
318 : : gsize in_buffer, left_to_fill;
319 : :
320 : 3861 : in_buffer = buffer_data_size (buffer);
321 : :
322 : 3861 : if (in_buffer >= at_least_size)
323 : 0 : return;
324 : :
325 : 3861 : left_to_fill = buffer_tailspace (buffer);
326 : :
327 : 3861 : if (in_buffer + left_to_fill >= at_least_size)
328 : : {
329 : : /* We fit in remaining space at end */
330 : : /* If the copy is small, compact now anyway so we can fill more */
331 : 3804 : if (in_buffer < 256)
332 : 3736 : compact_buffer (buffer);
333 : : }
334 : 57 : else if (buffer->size >= at_least_size)
335 : : {
336 : : /* We fit, but only if we compact */
337 : 8 : compact_buffer (buffer);
338 : : }
339 : : else
340 : : {
341 : : /* Need to grow buffer */
342 : 110 : while (buffer->size < at_least_size)
343 : 61 : grow_buffer (buffer);
344 : : }
345 : : }
346 : :
347 : : static gssize
348 : 3796 : fill_input_buffer (GConverterInputStream *stream,
349 : : gsize at_least_size,
350 : : gboolean blocking,
351 : : GCancellable *cancellable,
352 : : GError **error)
353 : : {
354 : : GConverterInputStreamPrivate *priv;
355 : : GInputStream *base_stream;
356 : : gssize nread;
357 : :
358 : 3796 : priv = stream->priv;
359 : :
360 : 3796 : buffer_ensure_space (&priv->input_buffer, at_least_size);
361 : :
362 : 3796 : base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
363 : 3796 : nread = g_pollable_stream_read (base_stream,
364 : 3796 : priv->input_buffer.data + priv->input_buffer.end,
365 : : buffer_tailspace (&priv->input_buffer),
366 : : blocking,
367 : : cancellable,
368 : : error);
369 : :
370 : 3796 : if (nread > 0)
371 : : {
372 : 3740 : priv->input_buffer.end += nread;
373 : 3740 : priv->need_input = FALSE;
374 : : }
375 : :
376 : 3796 : return nread;
377 : : }
378 : :
379 : :
380 : : static gssize
381 : 44799 : read_internal (GInputStream *stream,
382 : : void *buffer,
383 : : gsize count,
384 : : gboolean blocking,
385 : : GCancellable *cancellable,
386 : : GError **error)
387 : : {
388 : : GConverterInputStream *cstream;
389 : : GConverterInputStreamPrivate *priv;
390 : : gsize available, total_bytes_read;
391 : : gssize nread;
392 : : GConverterResult res;
393 : : gsize bytes_read;
394 : : gsize bytes_written;
395 : : GError *my_error;
396 : : GError *my_error2;
397 : :
398 : 44799 : cstream = G_CONVERTER_INPUT_STREAM (stream);
399 : 44799 : priv = cstream->priv;
400 : :
401 : 44799 : available = buffer_data_size (&priv->converted_buffer);
402 : :
403 : 44799 : if (available > 0 &&
404 : : count <= available)
405 : : {
406 : : /* Converted data available, return that */
407 : 41025 : buffer_read (&priv->converted_buffer, buffer, count);
408 : 41025 : return count;
409 : : }
410 : :
411 : : /* Full request not available, read all currently available and request
412 : : refill/conversion for more */
413 : :
414 : 3774 : buffer_read (&priv->converted_buffer, buffer, available);
415 : :
416 : 3774 : total_bytes_read = available;
417 : 3774 : buffer = (char *) buffer + available;
418 : 3774 : count -= available;
419 : :
420 : : /* If there is no data to convert, and no pre-converted data,
421 : : do some i/o for more input */
422 : 3774 : if (buffer_data_size (&priv->input_buffer) == 0 &&
423 : 3712 : total_bytes_read == 0 &&
424 : 3712 : !priv->at_input_end)
425 : : {
426 : 3710 : nread = fill_input_buffer (cstream, count, blocking, cancellable, error);
427 : 3710 : if (nread < 0)
428 : 1 : return -1;
429 : 3709 : if (nread == 0)
430 : 19 : priv->at_input_end = TRUE;
431 : : }
432 : :
433 : : /* First try to convert any available data (or state) directly to the user buffer: */
434 : 3773 : if (!priv->finished)
435 : : {
436 : 3761 : my_error = NULL;
437 : 3761 : res = g_converter_convert (priv->converter,
438 : 3761 : buffer_data (&priv->input_buffer),
439 : : buffer_data_size (&priv->input_buffer),
440 : : buffer, count,
441 : 3761 : priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
442 : : &bytes_read,
443 : : &bytes_written,
444 : : &my_error);
445 : 3761 : if (res != G_CONVERTER_ERROR)
446 : : {
447 : 3697 : total_bytes_read += bytes_written;
448 : 3697 : buffer_consumed (&priv->input_buffer, bytes_read);
449 : 3697 : if (res == G_CONVERTER_FINISHED)
450 : 20 : priv->finished = TRUE; /* We're done converting */
451 : : }
452 : 128 : else if (total_bytes_read == 0 &&
453 : 64 : !g_error_matches (my_error,
454 : : G_IO_ERROR,
455 : 8 : G_IO_ERROR_PARTIAL_INPUT) &&
456 : 8 : !g_error_matches (my_error,
457 : : G_IO_ERROR,
458 : : G_IO_ERROR_NO_SPACE))
459 : : {
460 : : /* No previously read data and no "special" error, return error */
461 : 1 : g_propagate_error (error, my_error);
462 : 1 : return -1;
463 : : }
464 : : else
465 : 63 : g_error_free (my_error);
466 : : }
467 : :
468 : : /* We had some pre-converted data and/or we converted directly to the
469 : : user buffer */
470 : 3772 : if (total_bytes_read > 0)
471 : 3687 : return total_bytes_read;
472 : :
473 : : /* If there is no more to convert, return EOF */
474 : 85 : if (priv->finished)
475 : : {
476 : 22 : g_assert (buffer_data_size (&priv->converted_buffer) == 0);
477 : 22 : return 0;
478 : : }
479 : :
480 : : /* There was "complexity" in the straight-to-buffer conversion,
481 : : * convert to our own buffer and write from that.
482 : : * At this point we didn't produce any data into @buffer.
483 : : */
484 : :
485 : : /* Ensure we have *some* initial target space */
486 : 63 : buffer_ensure_space (&priv->converted_buffer, count);
487 : :
488 : : while (TRUE)
489 : : {
490 : 118 : g_assert (!priv->finished);
491 : :
492 : : /* Try to convert to our buffer */
493 : 118 : my_error = NULL;
494 : 236 : res = g_converter_convert (priv->converter,
495 : 118 : buffer_data (&priv->input_buffer),
496 : : buffer_data_size (&priv->input_buffer),
497 : 118 : buffer_data (&priv->converted_buffer),
498 : : buffer_tailspace (&priv->converted_buffer),
499 : 118 : priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
500 : : &bytes_read,
501 : : &bytes_written,
502 : : &my_error);
503 : 118 : if (res != G_CONVERTER_ERROR)
504 : : {
505 : 26 : priv->converted_buffer.end += bytes_written;
506 : 26 : buffer_consumed (&priv->input_buffer, bytes_read);
507 : :
508 : : /* Maybe we consumed without producing any output */
509 : 26 : if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED)
510 : 0 : continue; /* Convert more */
511 : :
512 : 26 : if (res == G_CONVERTER_FINISHED)
513 : 2 : priv->finished = TRUE;
514 : :
515 : 26 : total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer));
516 : 26 : buffer_read (&priv->converted_buffer, buffer, total_bytes_read);
517 : :
518 : 26 : g_assert (priv->finished || total_bytes_read > 0);
519 : :
520 : 26 : return total_bytes_read;
521 : : }
522 : :
523 : : /* There was some kind of error filling our buffer */
524 : :
525 : 92 : if (g_error_matches (my_error,
526 : : G_IO_ERROR,
527 : 143 : G_IO_ERROR_PARTIAL_INPUT) &&
528 : 90 : !priv->at_input_end)
529 : : {
530 : : /* Need more data */
531 : 86 : my_error2 = NULL;
532 : 86 : nread = fill_input_buffer (cstream,
533 : 86 : buffer_data_size (&priv->input_buffer) + 4096,
534 : : blocking,
535 : : cancellable,
536 : : &my_error2);
537 : 86 : if (nread < 0)
538 : : {
539 : : /* Can't read any more data, return that error */
540 : 33 : g_error_free (my_error);
541 : 33 : g_propagate_error (error, my_error2);
542 : 33 : priv->need_input = TRUE;
543 : 33 : return -1;
544 : : }
545 : 53 : else if (nread == 0)
546 : : {
547 : : /* End of file, try INPUT_AT_END */
548 : 3 : priv->at_input_end = TRUE;
549 : : }
550 : 53 : g_error_free (my_error);
551 : 53 : continue;
552 : : }
553 : :
554 : 6 : if (g_error_matches (my_error,
555 : : G_IO_ERROR,
556 : : G_IO_ERROR_NO_SPACE))
557 : : {
558 : : /* Need more destination space, grow it
559 : : * Note: if we actually grow the buffer (as opposed to compacting it),
560 : : * this will double the size, not just add one byte. */
561 : 2 : buffer_ensure_space (&priv->converted_buffer,
562 : 2 : priv->converted_buffer.size + 1);
563 : 2 : g_error_free (my_error);
564 : 2 : continue;
565 : : }
566 : :
567 : : /* Any other random error, return it */
568 : 4 : g_propagate_error (error, my_error);
569 : 4 : return -1;
570 : : }
571 : :
572 : : g_assert_not_reached ();
573 : : }
574 : :
575 : : static gssize
576 : 44753 : g_converter_input_stream_read (GInputStream *stream,
577 : : void *buffer,
578 : : gsize count,
579 : : GCancellable *cancellable,
580 : : GError **error)
581 : : {
582 : 44753 : return read_internal (stream, buffer, count, TRUE, cancellable, error);
583 : : }
584 : :
585 : : static gboolean
586 : 2 : g_converter_input_stream_can_poll (GPollableInputStream *stream)
587 : : {
588 : 2 : GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
589 : :
590 : 4 : return (G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
591 : 2 : g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)));
592 : : }
593 : :
594 : : static gboolean
595 : 96 : g_converter_input_stream_is_readable (GPollableInputStream *stream)
596 : : {
597 : 96 : GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
598 : 96 : GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream);
599 : :
600 : 96 : if (buffer_data_size (&cstream->priv->converted_buffer))
601 : 0 : return TRUE;
602 : 96 : else if (buffer_data_size (&cstream->priv->input_buffer) &&
603 : 84 : !cstream->priv->need_input)
604 : 18 : return TRUE;
605 : : else
606 : 78 : return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream));
607 : : }
608 : :
609 : : static gssize
610 : 46 : g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
611 : : void *buffer,
612 : : gsize count,
613 : : GError **error)
614 : : {
615 : 46 : return read_internal (G_INPUT_STREAM (stream), buffer, count,
616 : : FALSE, NULL, error);
617 : : }
618 : :
619 : : static GSource *
620 : 1 : g_converter_input_stream_create_source (GPollableInputStream *stream,
621 : : GCancellable *cancellable)
622 : : {
623 : 1 : GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
624 : : GSource *base_source, *pollable_source;
625 : :
626 : 1 : if (g_pollable_input_stream_is_readable (stream))
627 : 0 : base_source = g_timeout_source_new (0);
628 : : else
629 : 1 : base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL);
630 : :
631 : 1 : pollable_source = g_pollable_source_new_full (stream, base_source,
632 : : cancellable);
633 : 1 : g_source_unref (base_source);
634 : :
635 : 1 : return pollable_source;
636 : : }
637 : :
638 : :
639 : : /**
640 : : * g_converter_input_stream_get_converter:
641 : : * @converter_stream: a #GConverterInputStream
642 : : *
643 : : * Gets the #GConverter that is used by @converter_stream.
644 : : *
645 : : * Returns: (transfer none): the converter of the converter input stream
646 : : *
647 : : * Since: 2.24
648 : : */
649 : : GConverter *
650 : 1 : g_converter_input_stream_get_converter (GConverterInputStream *converter_stream)
651 : : {
652 : 1 : return converter_stream->priv->converter;
653 : : }
|