/src/pango/subprojects/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 | | * 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 | 0 | #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 | | 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 | | g_converter_output_stream_class_init (GConverterOutputStreamClass *klass) |
119 | 0 | { |
120 | 0 | GObjectClass *object_class; |
121 | 0 | GOutputStreamClass *istream_class; |
122 | |
|
123 | 0 | object_class = G_OBJECT_CLASS (klass); |
124 | 0 | object_class->get_property = g_converter_output_stream_get_property; |
125 | 0 | object_class->set_property = g_converter_output_stream_set_property; |
126 | 0 | object_class->finalize = g_converter_output_stream_finalize; |
127 | |
|
128 | 0 | istream_class = G_OUTPUT_STREAM_CLASS (klass); |
129 | 0 | istream_class->write_fn = g_converter_output_stream_write; |
130 | 0 | istream_class->flush = g_converter_output_stream_flush; |
131 | | |
132 | | /** |
133 | | * GConverterOutputStream:converter: |
134 | | * |
135 | | * The converter object. |
136 | | */ |
137 | 0 | g_object_class_install_property (object_class, |
138 | 0 | PROP_CONVERTER, |
139 | 0 | g_param_spec_object ("converter", NULL, NULL, |
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 | { |
426 | 0 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE, |
427 | 0 | _("Unexpected data after end of conversion")); |
428 | 0 | return -1; |
429 | 0 | } |
430 | | |
431 | | /* Convert as much as possible */ |
432 | 0 | if (buffer_data_size (&priv->output_buffer) > 0) |
433 | 0 | { |
434 | 0 | converting_from_buffer = TRUE; |
435 | 0 | buffer_append (&priv->output_buffer, buffer, count); |
436 | 0 | to_convert = buffer_data (&priv->output_buffer); |
437 | 0 | to_convert_size = buffer_data_size (&priv->output_buffer); |
438 | 0 | } |
439 | 0 | else |
440 | 0 | { |
441 | 0 | converting_from_buffer = FALSE; |
442 | 0 | to_convert = buffer; |
443 | 0 | to_convert_size = count; |
444 | 0 | } |
445 | | |
446 | | /* Ensure we have *some* initial target space */ |
447 | 0 | buffer_ensure_space (&priv->converted_buffer, to_convert_size); |
448 | |
|
449 | 0 | converted_bytes = 0; |
450 | 0 | while (!priv->finished && converted_bytes < to_convert_size) |
451 | 0 | { |
452 | | /* Ensure we have *some* target space */ |
453 | 0 | if (buffer_tailspace (&priv->converted_buffer) == 0) |
454 | 0 | grow_buffer (&priv->converted_buffer); |
455 | | |
456 | | /* Try to convert to our buffer */ |
457 | 0 | my_error = NULL; |
458 | 0 | res = g_converter_convert (priv->converter, |
459 | 0 | to_convert + converted_bytes, |
460 | 0 | to_convert_size - converted_bytes, |
461 | 0 | buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer), |
462 | 0 | buffer_tailspace (&priv->converted_buffer), |
463 | 0 | 0, |
464 | 0 | &bytes_read, |
465 | 0 | &bytes_written, |
466 | 0 | &my_error); |
467 | |
|
468 | 0 | if (res != G_CONVERTER_ERROR) |
469 | 0 | { |
470 | 0 | priv->converted_buffer.end += bytes_written; |
471 | 0 | converted_bytes += bytes_read; |
472 | |
|
473 | 0 | if (res == G_CONVERTER_FINISHED) |
474 | 0 | priv->finished = TRUE; |
475 | 0 | } |
476 | 0 | else |
477 | 0 | { |
478 | | /* No-space errors can be handled locally: */ |
479 | 0 | if (g_error_matches (my_error, |
480 | 0 | G_IO_ERROR, |
481 | 0 | G_IO_ERROR_NO_SPACE)) |
482 | 0 | { |
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 | 0 | buffer_ensure_space (&priv->converted_buffer, |
487 | 0 | priv->converted_buffer.size + 1); |
488 | 0 | g_error_free (my_error); |
489 | 0 | continue; |
490 | 0 | } |
491 | | |
492 | 0 | if (converted_bytes > 0) |
493 | 0 | { |
494 | | /* We got a conversion error, but we did convert some bytes before |
495 | | that, so handle those before reporting the error */ |
496 | 0 | g_error_free (my_error); |
497 | 0 | break; |
498 | 0 | } |
499 | | |
500 | 0 | if (g_error_matches (my_error, |
501 | 0 | G_IO_ERROR, |
502 | 0 | G_IO_ERROR_PARTIAL_INPUT)) |
503 | 0 | { |
504 | | /* Consume everything to buffer that we append to next time |
505 | | we write */ |
506 | 0 | if (!converting_from_buffer) |
507 | 0 | buffer_append (&priv->output_buffer, buffer, count); |
508 | | /* in the converting_from_buffer case we already appended this */ |
509 | |
|
510 | 0 | g_error_free (my_error); |
511 | 0 | return count; /* consume everything */ |
512 | 0 | } |
513 | | |
514 | | /* Converted no data and got a normal error, return it */ |
515 | 0 | g_propagate_error (error, my_error); |
516 | 0 | return -1; |
517 | 0 | } |
518 | 0 | } |
519 | | |
520 | 0 | if (converting_from_buffer) |
521 | 0 | { |
522 | 0 | buffer_consumed (&priv->output_buffer, converted_bytes); |
523 | 0 | retval = count; |
524 | 0 | } |
525 | 0 | else |
526 | 0 | 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 | 0 | flush_buffer (cstream, blocking, cancellable, NULL); |
533 | |
|
534 | 0 | return retval; |
535 | 0 | } |
536 | | |
537 | | static gssize |
538 | | g_converter_output_stream_write (GOutputStream *stream, |
539 | | const void *buffer, |
540 | | gsize count, |
541 | | GCancellable *cancellable, |
542 | | GError **error) |
543 | 0 | { |
544 | 0 | return write_internal (stream, buffer, count, TRUE, cancellable, error); |
545 | 0 | } |
546 | | |
547 | | static gboolean |
548 | | g_converter_output_stream_flush (GOutputStream *stream, |
549 | | GCancellable *cancellable, |
550 | | GError **error) |
551 | 0 | { |
552 | 0 | GConverterOutputStream *cstream; |
553 | 0 | GConverterOutputStreamPrivate *priv; |
554 | 0 | GConverterResult res; |
555 | 0 | GError *my_error; |
556 | 0 | gboolean is_closing; |
557 | 0 | gboolean flushed; |
558 | 0 | gsize bytes_read; |
559 | 0 | gsize bytes_written; |
560 | |
|
561 | 0 | cstream = G_CONVERTER_OUTPUT_STREAM (stream); |
562 | 0 | priv = cstream->priv; |
563 | |
|
564 | 0 | is_closing = g_output_stream_is_closing (stream); |
565 | | |
566 | | /* Write out all available pre-converted data and fail if |
567 | | not possible */ |
568 | 0 | if (!flush_buffer (cstream, TRUE, cancellable, error)) |
569 | 0 | return FALSE; |
570 | | |
571 | | /* Ensure we have *some* initial target space */ |
572 | 0 | buffer_ensure_space (&priv->converted_buffer, 1); |
573 | | |
574 | | /* Convert whole buffer */ |
575 | 0 | flushed = FALSE; |
576 | 0 | while (!priv->finished && !flushed) |
577 | 0 | { |
578 | | /* Ensure we have *some* target space */ |
579 | 0 | if (buffer_tailspace (&priv->converted_buffer) == 0) |
580 | 0 | grow_buffer (&priv->converted_buffer); |
581 | | |
582 | | /* Try to convert to our buffer */ |
583 | 0 | my_error = NULL; |
584 | 0 | res = g_converter_convert (priv->converter, |
585 | 0 | buffer_data (&priv->output_buffer), |
586 | 0 | buffer_data_size (&priv->output_buffer), |
587 | 0 | buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer), |
588 | 0 | buffer_tailspace (&priv->converted_buffer), |
589 | 0 | is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH, |
590 | 0 | &bytes_read, |
591 | 0 | &bytes_written, |
592 | 0 | &my_error); |
593 | |
|
594 | 0 | if (res != G_CONVERTER_ERROR) |
595 | 0 | { |
596 | 0 | priv->converted_buffer.end += bytes_written; |
597 | 0 | buffer_consumed (&priv->output_buffer, bytes_read); |
598 | |
|
599 | 0 | if (res == G_CONVERTER_FINISHED) |
600 | 0 | priv->finished = TRUE; |
601 | 0 | if (!is_closing && |
602 | 0 | res == G_CONVERTER_FLUSHED) |
603 | 0 | { |
604 | | /* Should not have returned FLUSHED with input left */ |
605 | 0 | g_assert (buffer_data_size (&priv->output_buffer) == 0); |
606 | 0 | flushed = TRUE; |
607 | 0 | } |
608 | 0 | } |
609 | 0 | else |
610 | 0 | { |
611 | | /* No-space errors can be handled locally: */ |
612 | 0 | if (g_error_matches (my_error, |
613 | 0 | G_IO_ERROR, |
614 | 0 | G_IO_ERROR_NO_SPACE)) |
615 | 0 | { |
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 | 0 | } |
624 | | |
625 | | /* Any other error, including PARTIAL_INPUT can't be fixed by now |
626 | | and is an error */ |
627 | 0 | g_propagate_error (error, my_error); |
628 | 0 | return FALSE; |
629 | 0 | } |
630 | 0 | } |
631 | | |
632 | | /* Now write all converted data to base stream */ |
633 | 0 | if (!flush_buffer (cstream, TRUE, cancellable, error)) |
634 | 0 | return FALSE; |
635 | | |
636 | 0 | return TRUE; |
637 | 0 | } |
638 | | |
639 | | static gboolean |
640 | | g_converter_output_stream_can_poll (GPollableOutputStream *stream) |
641 | 0 | { |
642 | 0 | GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream; |
643 | |
|
644 | 0 | return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) && |
645 | 0 | g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream))); |
646 | 0 | } |
647 | | |
648 | | static gboolean |
649 | | g_converter_output_stream_is_writable (GPollableOutputStream *stream) |
650 | 0 | { |
651 | 0 | GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream; |
652 | |
|
653 | 0 | return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream)); |
654 | 0 | } |
655 | | |
656 | | static gssize |
657 | | g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream, |
658 | | const void *buffer, |
659 | | gsize count, |
660 | | GError **error) |
661 | 0 | { |
662 | 0 | return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE, |
663 | 0 | NULL, error); |
664 | 0 | } |
665 | | |
666 | | static GSource * |
667 | | g_converter_output_stream_create_source (GPollableOutputStream *stream, |
668 | | GCancellable *cancellable) |
669 | 0 | { |
670 | 0 | GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream; |
671 | 0 | 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 | 0 | cancellable); |
676 | 0 | g_source_unref (base_source); |
677 | |
|
678 | 0 | return pollable_source; |
679 | 0 | } |
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 | | g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream) |
693 | 0 | { |
694 | 0 | return converter_stream->priv->converter; |
695 | 0 | } |