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