/src/glib/gio/gconverterinputstream.c
Line | Count | Source (jump to first uncovered line) |
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 | | * SECTION:gconverterinputstream |
37 | | * @short_description: Converter Input Stream |
38 | | * @include: gio/gio.h |
39 | | * @see_also: #GInputStream, #GConverter |
40 | | * |
41 | | * Converter input stream implements #GInputStream and allows |
42 | | * conversion of data of various types during reading. |
43 | | * |
44 | | * As of GLib 2.34, #GConverterInputStream implements |
45 | | * #GPollableInputStream. |
46 | | **/ |
47 | | |
48 | 0 | #define INITIAL_BUFFER_SIZE 4096 |
49 | | |
50 | | typedef struct { |
51 | | char *data; |
52 | | gsize start; |
53 | | gsize end; |
54 | | gsize size; |
55 | | } Buffer; |
56 | | |
57 | | struct _GConverterInputStreamPrivate { |
58 | | gboolean at_input_end; |
59 | | gboolean finished; |
60 | | gboolean need_input; |
61 | | GConverter *converter; |
62 | | Buffer input_buffer; |
63 | | Buffer converted_buffer; |
64 | | }; |
65 | | |
66 | | enum { |
67 | | PROP_0, |
68 | | PROP_CONVERTER |
69 | | }; |
70 | | |
71 | | static void g_converter_input_stream_set_property (GObject *object, |
72 | | guint prop_id, |
73 | | const GValue *value, |
74 | | GParamSpec *pspec); |
75 | | static void g_converter_input_stream_get_property (GObject *object, |
76 | | guint prop_id, |
77 | | GValue *value, |
78 | | GParamSpec *pspec); |
79 | | static void g_converter_input_stream_finalize (GObject *object); |
80 | | static gssize g_converter_input_stream_read (GInputStream *stream, |
81 | | void *buffer, |
82 | | gsize count, |
83 | | GCancellable *cancellable, |
84 | | GError **error); |
85 | | |
86 | | static gboolean g_converter_input_stream_can_poll (GPollableInputStream *stream); |
87 | | static gboolean g_converter_input_stream_is_readable (GPollableInputStream *stream); |
88 | | static gssize g_converter_input_stream_read_nonblocking (GPollableInputStream *stream, |
89 | | void *buffer, |
90 | | gsize size, |
91 | | GError **error); |
92 | | |
93 | | static GSource *g_converter_input_stream_create_source (GPollableInputStream *stream, |
94 | | GCancellable *cancellable); |
95 | | |
96 | | static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface); |
97 | | |
98 | | G_DEFINE_TYPE_WITH_CODE (GConverterInputStream, |
99 | | g_converter_input_stream, |
100 | | G_TYPE_FILTER_INPUT_STREAM, |
101 | | G_ADD_PRIVATE (GConverterInputStream) |
102 | | G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, |
103 | | g_converter_input_stream_pollable_iface_init)) |
104 | | |
105 | | static void |
106 | | g_converter_input_stream_class_init (GConverterInputStreamClass *klass) |
107 | 0 | { |
108 | 0 | GObjectClass *object_class; |
109 | 0 | GInputStreamClass *istream_class; |
110 | |
|
111 | 0 | object_class = G_OBJECT_CLASS (klass); |
112 | 0 | object_class->get_property = g_converter_input_stream_get_property; |
113 | 0 | object_class->set_property = g_converter_input_stream_set_property; |
114 | 0 | object_class->finalize = g_converter_input_stream_finalize; |
115 | |
|
116 | 0 | istream_class = G_INPUT_STREAM_CLASS (klass); |
117 | 0 | istream_class->read_fn = g_converter_input_stream_read; |
118 | |
|
119 | 0 | g_object_class_install_property (object_class, |
120 | 0 | PROP_CONVERTER, |
121 | 0 | g_param_spec_object ("converter", |
122 | 0 | P_("Converter"), |
123 | 0 | P_("The converter object"), |
124 | 0 | G_TYPE_CONVERTER, |
125 | 0 | G_PARAM_READWRITE| |
126 | 0 | G_PARAM_CONSTRUCT_ONLY| |
127 | 0 | G_PARAM_STATIC_STRINGS)); |
128 | |
|
129 | 0 | } |
130 | | |
131 | | static void |
132 | | g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface) |
133 | 0 | { |
134 | 0 | iface->can_poll = g_converter_input_stream_can_poll; |
135 | 0 | iface->is_readable = g_converter_input_stream_is_readable; |
136 | 0 | iface->read_nonblocking = g_converter_input_stream_read_nonblocking; |
137 | 0 | iface->create_source = g_converter_input_stream_create_source; |
138 | 0 | } |
139 | | |
140 | | static void |
141 | | g_converter_input_stream_finalize (GObject *object) |
142 | 0 | { |
143 | 0 | GConverterInputStreamPrivate *priv; |
144 | 0 | GConverterInputStream *stream; |
145 | |
|
146 | 0 | stream = G_CONVERTER_INPUT_STREAM (object); |
147 | 0 | priv = stream->priv; |
148 | |
|
149 | 0 | g_free (priv->input_buffer.data); |
150 | 0 | g_free (priv->converted_buffer.data); |
151 | 0 | if (priv->converter) |
152 | 0 | g_object_unref (priv->converter); |
153 | |
|
154 | 0 | G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object); |
155 | 0 | } |
156 | | |
157 | | static void |
158 | | g_converter_input_stream_set_property (GObject *object, |
159 | | guint prop_id, |
160 | | const GValue *value, |
161 | | GParamSpec *pspec) |
162 | 0 | { |
163 | 0 | GConverterInputStream *cstream; |
164 | |
|
165 | 0 | cstream = G_CONVERTER_INPUT_STREAM (object); |
166 | |
|
167 | 0 | switch (prop_id) |
168 | 0 | { |
169 | 0 | case PROP_CONVERTER: |
170 | 0 | cstream->priv->converter = g_value_dup_object (value); |
171 | 0 | break; |
172 | | |
173 | 0 | default: |
174 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
175 | 0 | break; |
176 | 0 | } |
177 | |
|
178 | 0 | } |
179 | | |
180 | | static void |
181 | | g_converter_input_stream_get_property (GObject *object, |
182 | | guint prop_id, |
183 | | GValue *value, |
184 | | GParamSpec *pspec) |
185 | 0 | { |
186 | 0 | GConverterInputStreamPrivate *priv; |
187 | 0 | GConverterInputStream *cstream; |
188 | |
|
189 | 0 | cstream = G_CONVERTER_INPUT_STREAM (object); |
190 | 0 | priv = cstream->priv; |
191 | |
|
192 | 0 | switch (prop_id) |
193 | 0 | { |
194 | 0 | case PROP_CONVERTER: |
195 | 0 | g_value_set_object (value, priv->converter); |
196 | 0 | break; |
197 | | |
198 | 0 | default: |
199 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
200 | 0 | break; |
201 | 0 | } |
202 | |
|
203 | 0 | } |
204 | | static void |
205 | | g_converter_input_stream_init (GConverterInputStream *stream) |
206 | 0 | { |
207 | 0 | stream->priv = g_converter_input_stream_get_instance_private (stream); |
208 | 0 | } |
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 | | g_converter_input_stream_new (GInputStream *base_stream, |
221 | | GConverter *converter) |
222 | 0 | { |
223 | 0 | GInputStream *stream; |
224 | |
|
225 | 0 | g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL); |
226 | | |
227 | 0 | stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM, |
228 | 0 | "base-stream", base_stream, |
229 | 0 | "converter", converter, |
230 | 0 | NULL); |
231 | |
|
232 | 0 | return stream; |
233 | 0 | } |
234 | | |
235 | | static gsize |
236 | | buffer_data_size (Buffer *buffer) |
237 | 0 | { |
238 | 0 | return buffer->end - buffer->start; |
239 | 0 | } |
240 | | |
241 | | static gsize |
242 | | buffer_tailspace (Buffer *buffer) |
243 | 0 | { |
244 | 0 | return buffer->size - buffer->end; |
245 | 0 | } |
246 | | |
247 | | static char * |
248 | | buffer_data (Buffer *buffer) |
249 | 0 | { |
250 | 0 | return buffer->data + buffer->start; |
251 | 0 | } |
252 | | |
253 | | static void |
254 | | buffer_consumed (Buffer *buffer, |
255 | | gsize count) |
256 | 0 | { |
257 | 0 | buffer->start += count; |
258 | 0 | if (buffer->start == buffer->end) |
259 | 0 | buffer->start = buffer->end = 0; |
260 | 0 | } |
261 | | |
262 | | static void |
263 | | buffer_read (Buffer *buffer, |
264 | | char *dest, |
265 | | gsize count) |
266 | 0 | { |
267 | 0 | if (count != 0) |
268 | 0 | memcpy (dest, buffer->data + buffer->start, count); |
269 | |
|
270 | 0 | buffer_consumed (buffer, count); |
271 | 0 | } |
272 | | |
273 | | static void |
274 | | compact_buffer (Buffer *buffer) |
275 | 0 | { |
276 | 0 | gsize in_buffer; |
277 | |
|
278 | 0 | in_buffer = buffer_data_size (buffer); |
279 | 0 | memmove (buffer->data, |
280 | 0 | buffer->data + buffer->start, |
281 | 0 | in_buffer); |
282 | 0 | buffer->end -= buffer->start; |
283 | 0 | buffer->start = 0; |
284 | 0 | } |
285 | | |
286 | | static void |
287 | | grow_buffer (Buffer *buffer) |
288 | 0 | { |
289 | 0 | char *data; |
290 | 0 | gsize size, in_buffer; |
291 | |
|
292 | 0 | if (buffer->size == 0) |
293 | 0 | size = INITIAL_BUFFER_SIZE; |
294 | 0 | else |
295 | 0 | size = buffer->size * 2; |
296 | |
|
297 | 0 | data = g_malloc (size); |
298 | 0 | in_buffer = buffer_data_size (buffer); |
299 | |
|
300 | 0 | if (in_buffer != 0) |
301 | 0 | memcpy (data, |
302 | 0 | buffer->data + buffer->start, |
303 | 0 | in_buffer); |
304 | |
|
305 | 0 | g_free (buffer->data); |
306 | 0 | buffer->data = data; |
307 | 0 | buffer->end -= buffer->start; |
308 | 0 | buffer->start = 0; |
309 | 0 | buffer->size = size; |
310 | 0 | } |
311 | | |
312 | | /* Ensures that the buffer can fit at_least_size bytes, |
313 | | * *including* the current in-buffer data */ |
314 | | static void |
315 | | buffer_ensure_space (Buffer *buffer, |
316 | | gsize at_least_size) |
317 | 0 | { |
318 | 0 | gsize in_buffer, left_to_fill; |
319 | |
|
320 | 0 | in_buffer = buffer_data_size (buffer); |
321 | |
|
322 | 0 | if (in_buffer >= at_least_size) |
323 | 0 | return; |
324 | | |
325 | 0 | left_to_fill = buffer_tailspace (buffer); |
326 | |
|
327 | 0 | if (in_buffer + left_to_fill >= at_least_size) |
328 | 0 | { |
329 | | /* We fit in remaining space at end */ |
330 | | /* If the copy is small, compact now anyway so we can fill more */ |
331 | 0 | if (in_buffer < 256) |
332 | 0 | compact_buffer (buffer); |
333 | 0 | } |
334 | 0 | else if (buffer->size >= at_least_size) |
335 | 0 | { |
336 | | /* We fit, but only if we compact */ |
337 | 0 | compact_buffer (buffer); |
338 | 0 | } |
339 | 0 | else |
340 | 0 | { |
341 | | /* Need to grow buffer */ |
342 | 0 | while (buffer->size < at_least_size) |
343 | 0 | grow_buffer (buffer); |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | static gssize |
348 | | fill_input_buffer (GConverterInputStream *stream, |
349 | | gsize at_least_size, |
350 | | gboolean blocking, |
351 | | GCancellable *cancellable, |
352 | | GError **error) |
353 | 0 | { |
354 | 0 | GConverterInputStreamPrivate *priv; |
355 | 0 | GInputStream *base_stream; |
356 | 0 | gssize nread; |
357 | |
|
358 | 0 | priv = stream->priv; |
359 | |
|
360 | 0 | buffer_ensure_space (&priv->input_buffer, at_least_size); |
361 | |
|
362 | 0 | base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
363 | 0 | nread = g_pollable_stream_read (base_stream, |
364 | 0 | priv->input_buffer.data + priv->input_buffer.end, |
365 | 0 | buffer_tailspace (&priv->input_buffer), |
366 | 0 | blocking, |
367 | 0 | cancellable, |
368 | 0 | error); |
369 | |
|
370 | 0 | if (nread > 0) |
371 | 0 | { |
372 | 0 | priv->input_buffer.end += nread; |
373 | 0 | priv->need_input = FALSE; |
374 | 0 | } |
375 | |
|
376 | 0 | return nread; |
377 | 0 | } |
378 | | |
379 | | |
380 | | static gssize |
381 | | read_internal (GInputStream *stream, |
382 | | void *buffer, |
383 | | gsize count, |
384 | | gboolean blocking, |
385 | | GCancellable *cancellable, |
386 | | GError **error) |
387 | 0 | { |
388 | 0 | GConverterInputStream *cstream; |
389 | 0 | GConverterInputStreamPrivate *priv; |
390 | 0 | gsize available, total_bytes_read; |
391 | 0 | gssize nread; |
392 | 0 | GConverterResult res; |
393 | 0 | gsize bytes_read; |
394 | 0 | gsize bytes_written; |
395 | 0 | GError *my_error; |
396 | 0 | GError *my_error2; |
397 | |
|
398 | 0 | cstream = G_CONVERTER_INPUT_STREAM (stream); |
399 | 0 | priv = cstream->priv; |
400 | |
|
401 | 0 | available = buffer_data_size (&priv->converted_buffer); |
402 | |
|
403 | 0 | if (available > 0 && |
404 | 0 | count <= available) |
405 | 0 | { |
406 | | /* Converted data available, return that */ |
407 | 0 | buffer_read (&priv->converted_buffer, buffer, count); |
408 | 0 | return count; |
409 | 0 | } |
410 | | |
411 | | /* Full request not available, read all currently available and request |
412 | | refill/conversion for more */ |
413 | | |
414 | 0 | buffer_read (&priv->converted_buffer, buffer, available); |
415 | |
|
416 | 0 | total_bytes_read = available; |
417 | 0 | buffer = (char *) buffer + available; |
418 | 0 | 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 | 0 | if (buffer_data_size (&priv->input_buffer) == 0 && |
423 | 0 | total_bytes_read == 0 && |
424 | 0 | !priv->at_input_end) |
425 | 0 | { |
426 | 0 | nread = fill_input_buffer (cstream, count, blocking, cancellable, error); |
427 | 0 | if (nread < 0) |
428 | 0 | return -1; |
429 | 0 | if (nread == 0) |
430 | 0 | priv->at_input_end = TRUE; |
431 | 0 | } |
432 | | |
433 | | /* First try to convert any available data (or state) directly to the user buffer: */ |
434 | 0 | if (!priv->finished) |
435 | 0 | { |
436 | 0 | my_error = NULL; |
437 | 0 | res = g_converter_convert (priv->converter, |
438 | 0 | buffer_data (&priv->input_buffer), |
439 | 0 | buffer_data_size (&priv->input_buffer), |
440 | 0 | buffer, count, |
441 | 0 | priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0, |
442 | 0 | &bytes_read, |
443 | 0 | &bytes_written, |
444 | 0 | &my_error); |
445 | 0 | if (res != G_CONVERTER_ERROR) |
446 | 0 | { |
447 | 0 | total_bytes_read += bytes_written; |
448 | 0 | buffer_consumed (&priv->input_buffer, bytes_read); |
449 | 0 | if (res == G_CONVERTER_FINISHED) |
450 | 0 | priv->finished = TRUE; /* We're done converting */ |
451 | 0 | } |
452 | 0 | else if (total_bytes_read == 0 && |
453 | 0 | !g_error_matches (my_error, |
454 | 0 | G_IO_ERROR, |
455 | 0 | G_IO_ERROR_PARTIAL_INPUT) && |
456 | 0 | !g_error_matches (my_error, |
457 | 0 | G_IO_ERROR, |
458 | 0 | G_IO_ERROR_NO_SPACE)) |
459 | 0 | { |
460 | | /* No previously read data and no "special" error, return error */ |
461 | 0 | g_propagate_error (error, my_error); |
462 | 0 | return -1; |
463 | 0 | } |
464 | 0 | else |
465 | 0 | g_error_free (my_error); |
466 | 0 | } |
467 | | |
468 | | /* We had some pre-converted data and/or we converted directly to the |
469 | | user buffer */ |
470 | 0 | if (total_bytes_read > 0) |
471 | 0 | return total_bytes_read; |
472 | | |
473 | | /* If there is no more to convert, return EOF */ |
474 | 0 | if (priv->finished) |
475 | 0 | { |
476 | 0 | g_assert (buffer_data_size (&priv->converted_buffer) == 0); |
477 | 0 | return 0; |
478 | 0 | } |
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 | 0 | buffer_ensure_space (&priv->converted_buffer, count); |
487 | |
|
488 | 0 | while (TRUE) |
489 | 0 | { |
490 | 0 | g_assert (!priv->finished); |
491 | | |
492 | | /* Try to convert to our buffer */ |
493 | 0 | my_error = NULL; |
494 | 0 | res = g_converter_convert (priv->converter, |
495 | 0 | buffer_data (&priv->input_buffer), |
496 | 0 | buffer_data_size (&priv->input_buffer), |
497 | 0 | buffer_data (&priv->converted_buffer), |
498 | 0 | buffer_tailspace (&priv->converted_buffer), |
499 | 0 | priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0, |
500 | 0 | &bytes_read, |
501 | 0 | &bytes_written, |
502 | 0 | &my_error); |
503 | 0 | if (res != G_CONVERTER_ERROR) |
504 | 0 | { |
505 | 0 | priv->converted_buffer.end += bytes_written; |
506 | 0 | buffer_consumed (&priv->input_buffer, bytes_read); |
507 | | |
508 | | /* Maybe we consumed without producing any output */ |
509 | 0 | if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED) |
510 | 0 | continue; /* Convert more */ |
511 | | |
512 | 0 | if (res == G_CONVERTER_FINISHED) |
513 | 0 | priv->finished = TRUE; |
514 | |
|
515 | 0 | total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer)); |
516 | 0 | buffer_read (&priv->converted_buffer, buffer, total_bytes_read); |
517 | |
|
518 | 0 | g_assert (priv->finished || total_bytes_read > 0); |
519 | | |
520 | 0 | return total_bytes_read; |
521 | 0 | } |
522 | | |
523 | | /* There was some kind of error filling our buffer */ |
524 | | |
525 | 0 | if (g_error_matches (my_error, |
526 | 0 | G_IO_ERROR, |
527 | 0 | G_IO_ERROR_PARTIAL_INPUT) && |
528 | 0 | !priv->at_input_end) |
529 | 0 | { |
530 | | /* Need more data */ |
531 | 0 | my_error2 = NULL; |
532 | 0 | nread = fill_input_buffer (cstream, |
533 | 0 | buffer_data_size (&priv->input_buffer) + 4096, |
534 | 0 | blocking, |
535 | 0 | cancellable, |
536 | 0 | &my_error2); |
537 | 0 | if (nread < 0) |
538 | 0 | { |
539 | | /* Can't read any more data, return that error */ |
540 | 0 | g_error_free (my_error); |
541 | 0 | g_propagate_error (error, my_error2); |
542 | 0 | priv->need_input = TRUE; |
543 | 0 | return -1; |
544 | 0 | } |
545 | 0 | else if (nread == 0) |
546 | 0 | { |
547 | | /* End of file, try INPUT_AT_END */ |
548 | 0 | priv->at_input_end = TRUE; |
549 | 0 | } |
550 | 0 | g_error_free (my_error); |
551 | 0 | continue; |
552 | 0 | } |
553 | | |
554 | 0 | if (g_error_matches (my_error, |
555 | 0 | G_IO_ERROR, |
556 | 0 | G_IO_ERROR_NO_SPACE)) |
557 | 0 | { |
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 | 0 | buffer_ensure_space (&priv->converted_buffer, |
562 | 0 | priv->converted_buffer.size + 1); |
563 | 0 | g_error_free (my_error); |
564 | 0 | continue; |
565 | 0 | } |
566 | | |
567 | | /* Any other random error, return it */ |
568 | 0 | g_propagate_error (error, my_error); |
569 | 0 | return -1; |
570 | 0 | } |
571 | | |
572 | 0 | g_assert_not_reached (); |
573 | 0 | } |
574 | | |
575 | | static gssize |
576 | | g_converter_input_stream_read (GInputStream *stream, |
577 | | void *buffer, |
578 | | gsize count, |
579 | | GCancellable *cancellable, |
580 | | GError **error) |
581 | 0 | { |
582 | 0 | return read_internal (stream, buffer, count, TRUE, cancellable, error); |
583 | 0 | } |
584 | | |
585 | | static gboolean |
586 | | g_converter_input_stream_can_poll (GPollableInputStream *stream) |
587 | 0 | { |
588 | 0 | GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
589 | |
|
590 | 0 | return (G_IS_POLLABLE_INPUT_STREAM (base_stream) && |
591 | 0 | g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream))); |
592 | 0 | } |
593 | | |
594 | | static gboolean |
595 | | g_converter_input_stream_is_readable (GPollableInputStream *stream) |
596 | 0 | { |
597 | 0 | GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
598 | 0 | GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream); |
599 | |
|
600 | 0 | if (buffer_data_size (&cstream->priv->converted_buffer)) |
601 | 0 | return TRUE; |
602 | 0 | else if (buffer_data_size (&cstream->priv->input_buffer) && |
603 | 0 | !cstream->priv->need_input) |
604 | 0 | return TRUE; |
605 | 0 | else |
606 | 0 | return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream)); |
607 | 0 | } |
608 | | |
609 | | static gssize |
610 | | g_converter_input_stream_read_nonblocking (GPollableInputStream *stream, |
611 | | void *buffer, |
612 | | gsize count, |
613 | | GError **error) |
614 | 0 | { |
615 | 0 | return read_internal (G_INPUT_STREAM (stream), buffer, count, |
616 | 0 | FALSE, NULL, error); |
617 | 0 | } |
618 | | |
619 | | static GSource * |
620 | | g_converter_input_stream_create_source (GPollableInputStream *stream, |
621 | | GCancellable *cancellable) |
622 | 0 | { |
623 | 0 | GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream; |
624 | 0 | GSource *base_source, *pollable_source; |
625 | |
|
626 | 0 | if (g_pollable_input_stream_is_readable (stream)) |
627 | 0 | base_source = g_timeout_source_new (0); |
628 | 0 | else |
629 | 0 | base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL); |
630 | |
|
631 | 0 | pollable_source = g_pollable_source_new_full (stream, base_source, |
632 | 0 | cancellable); |
633 | 0 | g_source_unref (base_source); |
634 | |
|
635 | 0 | return pollable_source; |
636 | 0 | } |
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 | | g_converter_input_stream_get_converter (GConverterInputStream *converter_stream) |
651 | 0 | { |
652 | 0 | return converter_stream->priv->converter; |
653 | 0 | } |