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 "gconverteroutputstream.h"
28 : : #include "gpollableoutputstream.h"
29 : : #include "gcancellable.h"
30 : : #include "gioenumtypes.h"
31 : : #include "gioerror.h"
32 : : #include "glibintl.h"
33 : :
34 : :
35 : : /**
36 : : * GConverterOutputStream:
37 : : *
38 : : * Converter output stream implements [class@Gio.OutputStream] and allows
39 : : * conversion of data of various types during reading.
40 : : *
41 : : * As of GLib 2.34, `GConverterOutputStream` implements
42 : : * [iface@Gio.PollableOutputStream].
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 _GConverterOutputStreamPrivate {
55 : : gboolean at_output_end;
56 : : gboolean finished;
57 : : GConverter *converter;
58 : : Buffer output_buffer; /* To be converted and written */
59 : : Buffer converted_buffer; /* Already converted */
60 : : };
61 : :
62 : : /* Buffering strategy:
63 : : *
64 : : * Each time we write we must at least consume some input, or
65 : : * return an error. Thus we start with writing all already
66 : : * converted data and *then* we start converting (reporting
67 : : * an error at any point in this).
68 : : *
69 : : * Its possible that what the user wrote is not enough data
70 : : * for the converter, so we must then buffer it in output_buffer
71 : : * and ask for more data, but we want to avoid this as much as
72 : : * possible, converting directly from the users buffer.
73 : : */
74 : :
75 : : enum {
76 : : PROP_0,
77 : : PROP_CONVERTER
78 : : };
79 : :
80 : : static void g_converter_output_stream_set_property (GObject *object,
81 : : guint prop_id,
82 : : const GValue *value,
83 : : GParamSpec *pspec);
84 : : static void g_converter_output_stream_get_property (GObject *object,
85 : : guint prop_id,
86 : : GValue *value,
87 : : GParamSpec *pspec);
88 : : static void g_converter_output_stream_finalize (GObject *object);
89 : : static gssize g_converter_output_stream_write (GOutputStream *stream,
90 : : const void *buffer,
91 : : gsize count,
92 : : GCancellable *cancellable,
93 : : GError **error);
94 : : static gboolean g_converter_output_stream_flush (GOutputStream *stream,
95 : : GCancellable *cancellable,
96 : : GError **error);
97 : :
98 : : static gboolean g_converter_output_stream_can_poll (GPollableOutputStream *stream);
99 : : static gboolean g_converter_output_stream_is_writable (GPollableOutputStream *stream);
100 : : static gssize g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
101 : : const void *buffer,
102 : : gsize size,
103 : : GError **error);
104 : :
105 : : static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
106 : : GCancellable *cancellable);
107 : :
108 : : static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
109 : :
110 : 86640 : G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
111 : : g_converter_output_stream,
112 : : G_TYPE_FILTER_OUTPUT_STREAM,
113 : : G_ADD_PRIVATE (GConverterOutputStream)
114 : : G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
115 : : g_converter_output_stream_pollable_iface_init))
116 : :
117 : : static void
118 : 6 : g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
119 : : {
120 : : GObjectClass *object_class;
121 : : GOutputStreamClass *istream_class;
122 : :
123 : 6 : object_class = G_OBJECT_CLASS (klass);
124 : 6 : object_class->get_property = g_converter_output_stream_get_property;
125 : 6 : object_class->set_property = g_converter_output_stream_set_property;
126 : 6 : object_class->finalize = g_converter_output_stream_finalize;
127 : :
128 : 6 : istream_class = G_OUTPUT_STREAM_CLASS (klass);
129 : 6 : istream_class->write_fn = g_converter_output_stream_write;
130 : 6 : istream_class->flush = g_converter_output_stream_flush;
131 : :
132 : : /**
133 : : * GConverterOutputStream:converter:
134 : : *
135 : : * The converter object.
136 : : */
137 : 6 : g_object_class_install_property (object_class,
138 : : PROP_CONVERTER,
139 : : g_param_spec_object ("converter", NULL, NULL,
140 : : G_TYPE_CONVERTER,
141 : : G_PARAM_READWRITE|
142 : : G_PARAM_CONSTRUCT_ONLY|
143 : : G_PARAM_STATIC_STRINGS));
144 : :
145 : 6 : }
146 : :
147 : : static void
148 : 6 : g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
149 : : {
150 : 6 : iface->can_poll = g_converter_output_stream_can_poll;
151 : 6 : iface->is_writable = g_converter_output_stream_is_writable;
152 : 6 : iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
153 : 6 : iface->create_source = g_converter_output_stream_create_source;
154 : 6 : }
155 : :
156 : : static void
157 : 25 : g_converter_output_stream_finalize (GObject *object)
158 : : {
159 : : GConverterOutputStreamPrivate *priv;
160 : : GConverterOutputStream *stream;
161 : :
162 : 25 : stream = G_CONVERTER_OUTPUT_STREAM (object);
163 : 25 : priv = stream->priv;
164 : :
165 : 25 : g_free (priv->output_buffer.data);
166 : 25 : g_free (priv->converted_buffer.data);
167 : 25 : if (priv->converter)
168 : 25 : g_object_unref (priv->converter);
169 : :
170 : 25 : G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
171 : 25 : }
172 : :
173 : : static void
174 : 25 : g_converter_output_stream_set_property (GObject *object,
175 : : guint prop_id,
176 : : const GValue *value,
177 : : GParamSpec *pspec)
178 : : {
179 : : GConverterOutputStream *cstream;
180 : :
181 : 25 : cstream = G_CONVERTER_OUTPUT_STREAM (object);
182 : :
183 : 25 : switch (prop_id)
184 : : {
185 : 25 : case PROP_CONVERTER:
186 : 25 : cstream->priv->converter = g_value_dup_object (value);
187 : 25 : break;
188 : :
189 : 0 : default:
190 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
191 : 0 : break;
192 : : }
193 : :
194 : 25 : }
195 : :
196 : : static void
197 : 1 : g_converter_output_stream_get_property (GObject *object,
198 : : guint prop_id,
199 : : GValue *value,
200 : : GParamSpec *pspec)
201 : : {
202 : : GConverterOutputStreamPrivate *priv;
203 : : GConverterOutputStream *cstream;
204 : :
205 : 1 : cstream = G_CONVERTER_OUTPUT_STREAM (object);
206 : 1 : priv = cstream->priv;
207 : :
208 : 1 : switch (prop_id)
209 : : {
210 : 1 : case PROP_CONVERTER:
211 : 1 : g_value_set_object (value, priv->converter);
212 : 1 : break;
213 : :
214 : 0 : default:
215 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216 : 0 : break;
217 : : }
218 : 1 : }
219 : :
220 : : static void
221 : 25 : g_converter_output_stream_init (GConverterOutputStream *stream)
222 : : {
223 : 25 : stream->priv = g_converter_output_stream_get_instance_private (stream);
224 : 25 : }
225 : :
226 : : /**
227 : : * g_converter_output_stream_new:
228 : : * @base_stream: a #GOutputStream
229 : : * @converter: a #GConverter
230 : : *
231 : : * Creates a new converter output stream for the @base_stream.
232 : : *
233 : : * Returns: a new #GOutputStream.
234 : : **/
235 : : GOutputStream *
236 : 25 : g_converter_output_stream_new (GOutputStream *base_stream,
237 : : GConverter *converter)
238 : : {
239 : : GOutputStream *stream;
240 : :
241 : 25 : g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
242 : :
243 : 25 : stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
244 : : "base-stream", base_stream,
245 : : "converter", converter,
246 : : NULL);
247 : :
248 : 25 : return stream;
249 : : }
250 : :
251 : : static gsize
252 : 687413 : buffer_data_size (Buffer *buffer)
253 : : {
254 : 687413 : return buffer->end - buffer->start;
255 : : }
256 : :
257 : : static gsize
258 : 341641 : buffer_tailspace (Buffer *buffer)
259 : : {
260 : 341641 : return buffer->size - buffer->end;
261 : : }
262 : :
263 : : static char *
264 : 172334 : buffer_data (Buffer *buffer)
265 : : {
266 : 172334 : return buffer->data + buffer->start;
267 : : }
268 : :
269 : : static void
270 : 3776 : buffer_consumed (Buffer *buffer,
271 : : gsize count)
272 : : {
273 : 3776 : buffer->start += count;
274 : 3776 : if (buffer->start == buffer->end)
275 : 3774 : buffer->start = buffer->end = 0;
276 : 3776 : }
277 : :
278 : : static void
279 : 90638 : compact_buffer (Buffer *buffer)
280 : : {
281 : : gsize in_buffer;
282 : :
283 : 90638 : in_buffer = buffer_data_size (buffer);
284 : 90638 : memmove (buffer->data,
285 : 90638 : buffer->data + buffer->start,
286 : : in_buffer);
287 : 90638 : buffer->end -= buffer->start;
288 : 90638 : buffer->start = 0;
289 : 90638 : }
290 : :
291 : : static void
292 : 61 : grow_buffer (Buffer *buffer)
293 : : {
294 : : char *data;
295 : : gsize size, in_buffer;
296 : :
297 : 61 : if (buffer->size == 0)
298 : 27 : size = INITIAL_BUFFER_SIZE;
299 : : else
300 : 34 : size = buffer->size * 2;
301 : :
302 : 61 : data = g_malloc (size);
303 : 61 : in_buffer = buffer_data_size (buffer);
304 : :
305 : 61 : if (in_buffer != 0)
306 : 17 : memcpy (data,
307 : 17 : buffer->data + buffer->start,
308 : : in_buffer);
309 : :
310 : 61 : g_free (buffer->data);
311 : 61 : buffer->data = data;
312 : 61 : buffer->end -= buffer->start;
313 : 61 : buffer->start = 0;
314 : 61 : buffer->size = size;
315 : 61 : }
316 : :
317 : : /* Ensures that the buffer can fit at_least_size bytes,
318 : : * *including* the current in-buffer data */
319 : : static void
320 : 168575 : buffer_ensure_space (Buffer *buffer,
321 : : gsize at_least_size)
322 : : {
323 : : gsize in_buffer, left_to_fill;
324 : :
325 : 168575 : in_buffer = buffer_data_size (buffer);
326 : :
327 : 168575 : if (in_buffer >= at_least_size)
328 : 0 : return;
329 : :
330 : 168575 : left_to_fill = buffer_tailspace (buffer);
331 : :
332 : 168575 : if (in_buffer + left_to_fill >= at_least_size)
333 : : {
334 : : /* We fit in remaining space at end */
335 : : /* If the copy is small, compact now anyway so we can fill more */
336 : 168538 : if (in_buffer < 256)
337 : 90638 : compact_buffer (buffer);
338 : : }
339 : 37 : else if (buffer->size >= at_least_size)
340 : : {
341 : : /* We fit, but only if we compact */
342 : 0 : compact_buffer (buffer);
343 : : }
344 : : else
345 : : {
346 : : /* Need to grow buffer */
347 : 85 : while (buffer->size < at_least_size)
348 : 48 : grow_buffer (buffer);
349 : : }
350 : : }
351 : :
352 : : static void
353 : 82060 : buffer_append (Buffer *buffer,
354 : : const char *data,
355 : : gsize data_size)
356 : : {
357 : 82060 : buffer_ensure_space (buffer,
358 : 82060 : buffer_data_size (buffer) + data_size);
359 : 82060 : memcpy (buffer->data + buffer->end, data, data_size);
360 : 82060 : buffer->end += data_size;
361 : 82060 : }
362 : :
363 : :
364 : : static gboolean
365 : 90983 : flush_buffer (GConverterOutputStream *stream,
366 : : gboolean blocking,
367 : : GCancellable *cancellable,
368 : : GError **error)
369 : : {
370 : : GConverterOutputStreamPrivate *priv;
371 : : GOutputStream *base_stream;
372 : : gsize nwritten;
373 : : gsize available;
374 : : gboolean res;
375 : :
376 : 90983 : priv = stream->priv;
377 : :
378 : 90983 : base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
379 : :
380 : 90983 : available = buffer_data_size (&priv->converted_buffer);
381 : 90983 : if (available > 0)
382 : : {
383 : 3726 : res = g_pollable_stream_write_all (base_stream,
384 : 3726 : buffer_data (&priv->converted_buffer),
385 : : available,
386 : : blocking,
387 : : &nwritten,
388 : : cancellable,
389 : : error);
390 : 3726 : buffer_consumed (&priv->converted_buffer, nwritten);
391 : 3726 : return res;
392 : : }
393 : 87257 : return TRUE;
394 : : }
395 : :
396 : :
397 : : static gssize
398 : 86485 : write_internal (GOutputStream *stream,
399 : : const void *buffer,
400 : : gsize count,
401 : : gboolean blocking,
402 : : GCancellable *cancellable,
403 : : GError **error)
404 : : {
405 : : GConverterOutputStream *cstream;
406 : : GConverterOutputStreamPrivate *priv;
407 : : gssize retval;
408 : : GConverterResult res;
409 : : gsize bytes_read;
410 : : gsize bytes_written;
411 : : GError *my_error;
412 : : const char *to_convert;
413 : : gsize to_convert_size, converted_bytes;
414 : : gboolean converting_from_buffer;
415 : :
416 : 86485 : cstream = G_CONVERTER_OUTPUT_STREAM (stream);
417 : 86485 : priv = cstream->priv;
418 : :
419 : : /* Write out all available pre-converted data and fail if
420 : : not possible */
421 : 86485 : if (!flush_buffer (cstream, blocking, cancellable, error))
422 : 0 : return -1;
423 : :
424 : 86485 : if (priv->finished)
425 : : {
426 : 1 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
427 : : _("Unexpected data after end of conversion"));
428 : 1 : return -1;
429 : : }
430 : :
431 : : /* Convert as much as possible */
432 : 86484 : if (buffer_data_size (&priv->output_buffer) > 0)
433 : : {
434 : 82042 : converting_from_buffer = TRUE;
435 : 82042 : buffer_append (&priv->output_buffer, buffer, count);
436 : 82042 : to_convert = buffer_data (&priv->output_buffer);
437 : 82042 : to_convert_size = buffer_data_size (&priv->output_buffer);
438 : : }
439 : : else
440 : : {
441 : 4442 : converting_from_buffer = FALSE;
442 : 4442 : to_convert = buffer;
443 : 4442 : to_convert_size = count;
444 : : }
445 : :
446 : : /* Ensure we have *some* initial target space */
447 : 86484 : buffer_ensure_space (&priv->converted_buffer, to_convert_size);
448 : :
449 : 86484 : converted_bytes = 0;
450 : 90939 : while (!priv->finished && converted_bytes < to_convert_size)
451 : : {
452 : : /* Ensure we have *some* target space */
453 : 86500 : if (buffer_tailspace (&priv->converted_buffer) == 0)
454 : 12 : grow_buffer (&priv->converted_buffer);
455 : :
456 : : /* Try to convert to our buffer */
457 : 86500 : my_error = NULL;
458 : 86500 : res = g_converter_convert (priv->converter,
459 : 86500 : to_convert + converted_bytes,
460 : : to_convert_size - converted_bytes,
461 : 86500 : buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
462 : : buffer_tailspace (&priv->converted_buffer),
463 : : 0,
464 : : &bytes_read,
465 : : &bytes_written,
466 : : &my_error);
467 : :
468 : 86500 : if (res != G_CONVERTER_ERROR)
469 : : {
470 : 4453 : priv->converted_buffer.end += bytes_written;
471 : 4453 : converted_bytes += bytes_read;
472 : :
473 : 4453 : if (res == G_CONVERTER_FINISHED)
474 : 1 : priv->finished = TRUE;
475 : : }
476 : : else
477 : : {
478 : : /* No-space errors can be handled locally: */
479 : 82047 : if (g_error_matches (my_error,
480 : : G_IO_ERROR,
481 : : G_IO_ERROR_NO_SPACE))
482 : : {
483 : : /* Need more destination space, grow it
484 : : * Note: if we actually grow the buffer (as opposed to compacting it),
485 : : * this will double the size, not just add one byte. */
486 : 2 : buffer_ensure_space (&priv->converted_buffer,
487 : 2 : priv->converted_buffer.size + 1);
488 : 2 : g_error_free (my_error);
489 : 2 : continue;
490 : : }
491 : :
492 : 82045 : if (converted_bytes > 0)
493 : : {
494 : : /* We got a conversion error, but we did convert some bytes before
495 : : that, so handle those before reporting the error */
496 : 2 : g_error_free (my_error);
497 : 2 : break;
498 : : }
499 : :
500 : 82043 : if (g_error_matches (my_error,
501 : : G_IO_ERROR,
502 : : G_IO_ERROR_PARTIAL_INPUT))
503 : : {
504 : : /* Consume everything to buffer that we append to next time
505 : : we write */
506 : 82042 : if (!converting_from_buffer)
507 : 18 : buffer_append (&priv->output_buffer, buffer, count);
508 : : /* in the converting_from_buffer case we already appended this */
509 : :
510 : 82042 : g_error_free (my_error);
511 : 82042 : return count; /* consume everything */
512 : : }
513 : :
514 : : /* Converted no data and got a normal error, return it */
515 : 1 : g_propagate_error (error, my_error);
516 : 1 : return -1;
517 : : }
518 : : }
519 : :
520 : 4441 : if (converting_from_buffer)
521 : : {
522 : 18 : buffer_consumed (&priv->output_buffer, converted_bytes);
523 : 18 : retval = count;
524 : : }
525 : : else
526 : 4423 : retval = converted_bytes;
527 : :
528 : : /* We now successfully consumed retval bytes, so we can't return an error,
529 : : even if writing this to the base stream fails. If it does we'll just
530 : : stop early and report this error when we try again on the next
531 : : write call. */
532 : 4441 : flush_buffer (cstream, blocking, cancellable, NULL);
533 : :
534 : 4441 : return retval;
535 : : }
536 : :
537 : : static gssize
538 : 45455 : g_converter_output_stream_write (GOutputStream *stream,
539 : : const void *buffer,
540 : : gsize count,
541 : : GCancellable *cancellable,
542 : : GError **error)
543 : : {
544 : 45455 : return write_internal (stream, buffer, count, TRUE, cancellable, error);
545 : : }
546 : :
547 : : static gboolean
548 : 29 : g_converter_output_stream_flush (GOutputStream *stream,
549 : : GCancellable *cancellable,
550 : : GError **error)
551 : : {
552 : : GConverterOutputStream *cstream;
553 : : GConverterOutputStreamPrivate *priv;
554 : : GConverterResult res;
555 : : GError *my_error;
556 : : gboolean is_closing;
557 : : gboolean flushed;
558 : : gsize bytes_read;
559 : : gsize bytes_written;
560 : :
561 : 29 : cstream = G_CONVERTER_OUTPUT_STREAM (stream);
562 : 29 : priv = cstream->priv;
563 : :
564 : 29 : is_closing = g_output_stream_is_closing (stream);
565 : :
566 : : /* Write out all available pre-converted data and fail if
567 : : not possible */
568 : 29 : if (!flush_buffer (cstream, TRUE, cancellable, error))
569 : 0 : return FALSE;
570 : :
571 : : /* Ensure we have *some* initial target space */
572 : 29 : buffer_ensure_space (&priv->converted_buffer, 1);
573 : :
574 : : /* Convert whole buffer */
575 : 29 : flushed = FALSE;
576 : 61 : while (!priv->finished && !flushed)
577 : : {
578 : : /* Ensure we have *some* target space */
579 : 33 : if (buffer_tailspace (&priv->converted_buffer) == 0)
580 : 1 : grow_buffer (&priv->converted_buffer);
581 : :
582 : : /* Try to convert to our buffer */
583 : 33 : my_error = NULL;
584 : 66 : res = g_converter_convert (priv->converter,
585 : 33 : buffer_data (&priv->output_buffer),
586 : : buffer_data_size (&priv->output_buffer),
587 : 33 : buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
588 : : buffer_tailspace (&priv->converted_buffer),
589 : : is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
590 : : &bytes_read,
591 : : &bytes_written,
592 : : &my_error);
593 : :
594 : 33 : if (res != G_CONVERTER_ERROR)
595 : : {
596 : 32 : priv->converted_buffer.end += bytes_written;
597 : 32 : buffer_consumed (&priv->output_buffer, bytes_read);
598 : :
599 : 32 : if (res == G_CONVERTER_FINISHED)
600 : 23 : priv->finished = TRUE;
601 : 32 : if (!is_closing &&
602 : : res == G_CONVERTER_FLUSHED)
603 : : {
604 : : /* Should not have returned FLUSHED with input left */
605 : 4 : g_assert (buffer_data_size (&priv->output_buffer) == 0);
606 : 4 : flushed = TRUE;
607 : : }
608 : : }
609 : : else
610 : : {
611 : : /* No-space errors can be handled locally: */
612 : 1 : if (g_error_matches (my_error,
613 : : G_IO_ERROR,
614 : : G_IO_ERROR_NO_SPACE))
615 : : {
616 : : /* Need more destination space, grow it
617 : : * Note: if we actually grow the buffer (as opposed to compacting it),
618 : : * this will double the size, not just add one byte. */
619 : 0 : buffer_ensure_space (&priv->converted_buffer,
620 : 0 : priv->converted_buffer.size + 1);
621 : 0 : g_error_free (my_error);
622 : 0 : continue;
623 : : }
624 : :
625 : : /* Any other error, including PARTIAL_INPUT can't be fixed by now
626 : : and is an error */
627 : 1 : g_propagate_error (error, my_error);
628 : 1 : return FALSE;
629 : : }
630 : : }
631 : :
632 : : /* Now write all converted data to base stream */
633 : 28 : if (!flush_buffer (cstream, TRUE, cancellable, error))
634 : 0 : return FALSE;
635 : :
636 : 28 : return TRUE;
637 : : }
638 : :
639 : : static gboolean
640 : 1 : g_converter_output_stream_can_poll (GPollableOutputStream *stream)
641 : : {
642 : 1 : GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
643 : :
644 : 2 : return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
645 : 1 : g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
646 : : }
647 : :
648 : : static gboolean
649 : 1 : g_converter_output_stream_is_writable (GPollableOutputStream *stream)
650 : : {
651 : 1 : GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
652 : :
653 : 1 : return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
654 : : }
655 : :
656 : : static gssize
657 : 41030 : g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
658 : : const void *buffer,
659 : : gsize count,
660 : : GError **error)
661 : : {
662 : 41030 : return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
663 : : NULL, error);
664 : : }
665 : :
666 : : static GSource *
667 : 0 : g_converter_output_stream_create_source (GPollableOutputStream *stream,
668 : : GCancellable *cancellable)
669 : : {
670 : 0 : GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
671 : : GSource *base_source, *pollable_source;
672 : :
673 : 0 : base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
674 : 0 : pollable_source = g_pollable_source_new_full (stream, base_source,
675 : : cancellable);
676 : 0 : g_source_unref (base_source);
677 : :
678 : 0 : return pollable_source;
679 : : }
680 : :
681 : : /**
682 : : * g_converter_output_stream_get_converter:
683 : : * @converter_stream: a #GConverterOutputStream
684 : : *
685 : : * Gets the #GConverter that is used by @converter_stream.
686 : : *
687 : : * Returns: (transfer none): the converter of the converter output stream
688 : : *
689 : : * Since: 2.24
690 : : */
691 : : GConverter *
692 : 10 : g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
693 : : {
694 : 10 : return converter_stream->priv->converter;
695 : : }
|