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 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 {
120 GObjectClass *object_class;
121 GOutputStreamClass *istream_class;
122
123 object_class = G_OBJECT_CLASS (klass);
124 object_class->get_property = g_converter_output_stream_get_property;
125 object_class->set_property = g_converter_output_stream_set_property;
126 object_class->finalize = g_converter_output_stream_finalize;
127
128 istream_class = G_OUTPUT_STREAM_CLASS (klass);
129 istream_class->write_fn = g_converter_output_stream_write;
130 istream_class->flush = g_converter_output_stream_flush;
131
132 /**
133 * GConverterOutputStream:converter:
134 *
135 * The converter object.
136 */
137 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 }
146
147 static void
148 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
149 {
150 iface->can_poll = g_converter_output_stream_can_poll;
151 iface->is_writable = g_converter_output_stream_is_writable;
152 iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
153 iface->create_source = g_converter_output_stream_create_source;
154 }
155
156 static void
157 g_converter_output_stream_finalize (GObject *object)
158 {
159 GConverterOutputStreamPrivate *priv;
160 GConverterOutputStream *stream;
161
162 stream = G_CONVERTER_OUTPUT_STREAM (object);
163 priv = stream->priv;
164
165 g_free (priv->output_buffer.data);
166 g_free (priv->converted_buffer.data);
167 if (priv->converter)
168 g_object_unref (priv->converter);
169
170 G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
171 }
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 {
179 GConverterOutputStream *cstream;
180
181 cstream = G_CONVERTER_OUTPUT_STREAM (object);
182
183 switch (prop_id)
184 {
185 case PROP_CONVERTER:
186 cstream->priv->converter = g_value_dup_object (value);
187 break;
188
189 default:
190 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
191 break;
192 }
193
194 }
195
196 static void
197 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 cstream = G_CONVERTER_OUTPUT_STREAM (object);
206 priv = cstream->priv;
207
208 switch (prop_id)
209 {
210 case PROP_CONVERTER:
211 g_value_set_object (value, priv->converter);
212 break;
213
214 default:
215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216 break;
217 }
218 }
219
220 static void
221 g_converter_output_stream_init (GConverterOutputStream *stream)
222 {
223 stream->priv = g_converter_output_stream_get_instance_private (stream);
224 }
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 {
239 GOutputStream *stream;
240
241 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
242
243 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
244 "base-stream", base_stream,
245 "converter", converter,
246 NULL);
247
248 return stream;
249 }
250
251 static gsize
252 buffer_data_size (Buffer *buffer)
253 {
254 return buffer->end - buffer->start;
255 }
256
257 static gsize
258 buffer_tailspace (Buffer *buffer)
259 {
260 return buffer->size - buffer->end;
261 }
262
263 static char *
264 buffer_data (Buffer *buffer)
265 {
266 return buffer->data + buffer->start;
267 }
268
269 static void
270 buffer_consumed (Buffer *buffer,
271 gsize count)
272 {
273 buffer->start += count;
274 if (buffer->start == buffer->end)
275 buffer->start = buffer->end = 0;
276 }
277
278 static void
279 compact_buffer (Buffer *buffer)
280 {
281 gsize in_buffer;
282
283 in_buffer = buffer_data_size (buffer);
284 memmove (buffer->data,
285 buffer->data + buffer->start,
286 in_buffer);
287 buffer->end -= buffer->start;
288 buffer->start = 0;
289 }
290
291 static void
292 grow_buffer (Buffer *buffer)
293 {
294 char *data;
295 gsize size, in_buffer;
296
297 if (buffer->size == 0)
298 size = INITIAL_BUFFER_SIZE;
299 else
300 size = buffer->size * 2;
301
302 data = g_malloc (size);
303 in_buffer = buffer_data_size (buffer);
304
305 if (in_buffer != 0)
306 memcpy (data,
307 buffer->data + buffer->start,
308 in_buffer);
309
310 g_free (buffer->data);
311 buffer->data = data;
312 buffer->end -= buffer->start;
313 buffer->start = 0;
314 buffer->size = size;
315 }
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 {
323 gsize in_buffer, left_to_fill;
324
325 in_buffer = buffer_data_size (buffer);
326
327 if (in_buffer >= at_least_size)
328 return;
329
330 left_to_fill = buffer_tailspace (buffer);
331
332 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 if (in_buffer < 256)
337 compact_buffer (buffer);
338 }
339 else if (buffer->size >= at_least_size)
340 {
341 /* We fit, but only if we compact */
342 compact_buffer (buffer);
343 }
344 else
345 {
346 /* Need to grow buffer */
347 while (buffer->size < at_least_size)
348 grow_buffer (buffer);
349 }
350 }
351
352 static void
353 buffer_append (Buffer *buffer,
354 const char *data,
355 gsize data_size)
356 {
357 buffer_ensure_space (buffer,
358 buffer_data_size (buffer) + data_size);
359 memcpy (buffer->data + buffer->end, data, data_size);
360 buffer->end += data_size;
361 }
362
363
364 static gboolean
365 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 priv = stream->priv;
377
378 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
379
380 available = buffer_data_size (&priv->converted_buffer);
381 if (available > 0)
382 {
383 res = g_pollable_stream_write_all (base_stream,
384 buffer_data (&priv->converted_buffer),
385 available,
386 blocking,
387 &nwritten,
388 cancellable,
389 error);
390 buffer_consumed (&priv->converted_buffer, nwritten);
391 return res;
392 }
393 return TRUE;
394 }
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 {
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 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
417 priv = cstream->priv;
418
419 /* Write out all available pre-converted data and fail if
420 not possible */
421 if (!flush_buffer (cstream, blocking, cancellable, error))
422 return -1;
423
424 if (priv->finished)
425 return 0;
426
427 /* Convert as much as possible */
428 if (buffer_data_size (&priv->output_buffer) > 0)
429 {
430 converting_from_buffer = TRUE;
431 buffer_append (&priv->output_buffer, buffer, count);
432 to_convert = buffer_data (&priv->output_buffer);
433 to_convert_size = buffer_data_size (&priv->output_buffer);
434 }
435 else
436 {
437 converting_from_buffer = FALSE;
438 to_convert = buffer;
439 to_convert_size = count;
440 }
441
442 /* Ensure we have *some* initial target space */
443 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
444
445 converted_bytes = 0;
446 while (!priv->finished && converted_bytes < to_convert_size)
447 {
448 /* Ensure we have *some* target space */
449 if (buffer_tailspace (&priv->converted_buffer) == 0)
450 grow_buffer (&priv->converted_buffer);
451
452 /* Try to convert to our buffer */
453 my_error = NULL;
454 res = g_converter_convert (priv->converter,
455 to_convert + converted_bytes,
456 to_convert_size - converted_bytes,
457 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
458 buffer_tailspace (&priv->converted_buffer),
459 0,
460 &bytes_read,
461 &bytes_written,
462 &my_error);
463
464 if (res != G_CONVERTER_ERROR)
465 {
466 priv->converted_buffer.end += bytes_written;
467 converted_bytes += bytes_read;
468
469 if (res == G_CONVERTER_FINISHED)
470 priv->finished = TRUE;
471 }
472 else
473 {
474 /* No-space errors can be handled locally: */
475 if (g_error_matches (my_error,
476 G_IO_ERROR,
477 G_IO_ERROR_NO_SPACE))
478 {
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 buffer_ensure_space (&priv->converted_buffer,
483 priv->converted_buffer.size + 1);
484 g_error_free (my_error);
485 continue;
486 }
487
488 if (converted_bytes > 0)
489 {
490 /* We got a conversion error, but we did convert some bytes before
491 that, so handle those before reporting the error */
492 g_error_free (my_error);
493 break;
494 }
495
496 if (g_error_matches (my_error,
497 G_IO_ERROR,
498 G_IO_ERROR_PARTIAL_INPUT))
499 {
500 /* Consume everything to buffer that we append to next time
501 we write */
502 if (!converting_from_buffer)
503 buffer_append (&priv->output_buffer, buffer, count);
504 /* in the converting_from_buffer case we already appended this */
505
506 g_error_free (my_error);
507 return count; /* consume everything */
508 }
509
510 /* Converted no data and got a normal error, return it */
511 g_propagate_error (error, my_error);
512 return -1;
513 }
514 }
515
516 if (converting_from_buffer)
517 {
518 buffer_consumed (&priv->output_buffer, converted_bytes);
519 retval = count;
520 }
521 else
522 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 flush_buffer (cstream, blocking, cancellable, NULL);
529
530 return retval;
531 }
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 {
540 return write_internal (stream, buffer, count, TRUE, cancellable, error);
541 }
542
543 static gboolean
544 g_converter_output_stream_flush (GOutputStream *stream,
545 GCancellable *cancellable,
546 GError **error)
547 {
548 GConverterOutputStream *cstream;
549 GConverterOutputStreamPrivate *priv;
550 GConverterResult res;
551 GError *my_error;
552 gboolean is_closing;
553 gboolean flushed;
554 gsize bytes_read;
555 gsize bytes_written;
556
557 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
558 priv = cstream->priv;
559
560 is_closing = g_output_stream_is_closing (stream);
561
562 /* Write out all available pre-converted data and fail if
563 not possible */
564 if (!flush_buffer (cstream, TRUE, cancellable, error))
565 return FALSE;
566
567 /* Ensure we have *some* initial target space */
568 buffer_ensure_space (&priv->converted_buffer, 1);
569
570 /* Convert whole buffer */
571 flushed = FALSE;
572 while (!priv->finished && !flushed)
573 {
574 /* Ensure we have *some* target space */
575 if (buffer_tailspace (&priv->converted_buffer) == 0)
576 grow_buffer (&priv->converted_buffer);
577
578 /* Try to convert to our buffer */
579 my_error = NULL;
580 res = g_converter_convert (priv->converter,
581 buffer_data (&priv->output_buffer),
582 buffer_data_size (&priv->output_buffer),
583 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
584 buffer_tailspace (&priv->converted_buffer),
585 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
586 &bytes_read,
587 &bytes_written,
588 &my_error);
589
590 if (res != G_CONVERTER_ERROR)
591 {
592 priv->converted_buffer.end += bytes_written;
593 buffer_consumed (&priv->output_buffer, bytes_read);
594
595 if (res == G_CONVERTER_FINISHED)
596 priv->finished = TRUE;
597 if (!is_closing &&
598 res == G_CONVERTER_FLUSHED)
599 {
600 /* Should not have returned FLUSHED with input left */
601 g_assert (buffer_data_size (&priv->output_buffer) == 0);
602 flushed = TRUE;
603 }
604 }
605 else
606 {
607 /* No-space errors can be handled locally: */
608 if (g_error_matches (my_error,
609 G_IO_ERROR,
610 G_IO_ERROR_NO_SPACE))
611 {
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 buffer_ensure_space (&priv->converted_buffer,
616 priv->converted_buffer.size + 1);
617 g_error_free (my_error);
618 continue;
619 }
620
621 /* Any other error, including PARTIAL_INPUT can't be fixed by now
622 and is an error */
623 g_propagate_error (error, my_error);
624 return FALSE;
625 }
626 }
627
628 /* Now write all converted data to base stream */
629 if (!flush_buffer (cstream, TRUE, cancellable, error))
630 return FALSE;
631
632 return TRUE;
633 }
634
635 static gboolean
636 g_converter_output_stream_can_poll (GPollableOutputStream *stream)
637 {
638 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
639
640 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
641 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
642 }
643
644 static gboolean
645 g_converter_output_stream_is_writable (GPollableOutputStream *stream)
646 {
647 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
648
649 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
650 }
651
652 static gssize
653 g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
654 const void *buffer,
655 gsize count,
656 GError **error)
657 {
658 return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
659 NULL, error);
660 }
661
662 static GSource *
663 g_converter_output_stream_create_source (GPollableOutputStream *stream,
664 GCancellable *cancellable)
665 {
666 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
667 GSource *base_source, *pollable_source;
668
669 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
670 pollable_source = g_pollable_source_new_full (stream, base_source,
671 cancellable);
672 g_source_unref (base_source);
673
674 return pollable_source;
675 }
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 {
690 return converter_stream->priv->converter;
691 }