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