Added Scottish Gaelic translation
[glib.git] / gio / gconverteroutputstream.c
blob46616b1a48a232c804e8f124960b1c3f19ed19f0
1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2009 Red Hat, Inc.
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 of the License, or (at your option) any later version.
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.
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/>.
18 * Author: Alexander Larsson <alexl@redhat.com>
21 #include "config.h"
23 #include <string.h>
25 #include "gconverteroutputstream.h"
26 #include "gpollableoutputstream.h"
27 #include "gcancellable.h"
28 #include "gioenumtypes.h"
29 #include "gioerror.h"
30 #include "glibintl.h"
33 /**
34 * SECTION:gconverteroutputstream
35 * @short_description: Converter Output Stream
36 * @include: gio/gio.h
37 * @see_also: #GOutputStream, #GConverter
39 * Converter output stream implements #GOutputStream and allows
40 * conversion of data of various types during reading.
42 * As of GLib 2.34, #GConverterOutputStream implements
43 * #GPollableOutputStream.
44 **/
46 #define INITIAL_BUFFER_SIZE 4096
48 typedef struct {
49 char *data;
50 gsize start;
51 gsize end;
52 gsize size;
53 } Buffer;
55 struct _GConverterOutputStreamPrivate {
56 gboolean at_output_end;
57 gboolean finished;
58 GConverter *converter;
59 Buffer output_buffer; /* To be converted and written */
60 Buffer converted_buffer; /* Already converted */
63 /* Buffering strategy:
65 * Each time we write we must at least consume some input, or
66 * return an error. Thus we start with writing all already
67 * converted data and *then* we start converting (reporting
68 * an error at any point in this).
70 * Its possible that what the user wrote is not enough data
71 * for the converter, so we must then buffer it in output_buffer
72 * and ask for more data, but we want to avoid this as much as
73 * possible, converting directly from the users buffer.
76 enum {
77 PROP_0,
78 PROP_CONVERTER
81 static void g_converter_output_stream_set_property (GObject *object,
82 guint prop_id,
83 const GValue *value,
84 GParamSpec *pspec);
85 static void g_converter_output_stream_get_property (GObject *object,
86 guint prop_id,
87 GValue *value,
88 GParamSpec *pspec);
89 static void g_converter_output_stream_finalize (GObject *object);
90 static gssize g_converter_output_stream_write (GOutputStream *stream,
91 const void *buffer,
92 gsize count,
93 GCancellable *cancellable,
94 GError **error);
95 static gboolean g_converter_output_stream_flush (GOutputStream *stream,
96 GCancellable *cancellable,
97 GError **error);
99 static gboolean g_converter_output_stream_can_poll (GPollableOutputStream *stream);
100 static gboolean g_converter_output_stream_is_writable (GPollableOutputStream *stream);
101 static gssize g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
102 const void *buffer,
103 gsize size,
104 GError **error);
106 static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
107 GCancellable *cancellable);
109 static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
111 G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
112 g_converter_output_stream,
113 G_TYPE_FILTER_OUTPUT_STREAM,
114 G_ADD_PRIVATE (GConverterOutputStream)
115 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
116 g_converter_output_stream_pollable_iface_init))
118 static void
119 g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
121 GObjectClass *object_class;
122 GOutputStreamClass *istream_class;
124 object_class = G_OBJECT_CLASS (klass);
125 object_class->get_property = g_converter_output_stream_get_property;
126 object_class->set_property = g_converter_output_stream_set_property;
127 object_class->finalize = g_converter_output_stream_finalize;
129 istream_class = G_OUTPUT_STREAM_CLASS (klass);
130 istream_class->write_fn = g_converter_output_stream_write;
131 istream_class->flush = g_converter_output_stream_flush;
133 g_object_class_install_property (object_class,
134 PROP_CONVERTER,
135 g_param_spec_object ("converter",
136 P_("Converter"),
137 P_("The converter object"),
138 G_TYPE_CONVERTER,
139 G_PARAM_READWRITE|
140 G_PARAM_CONSTRUCT_ONLY|
141 G_PARAM_STATIC_STRINGS));
145 static void
146 g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
148 iface->can_poll = g_converter_output_stream_can_poll;
149 iface->is_writable = g_converter_output_stream_is_writable;
150 iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
151 iface->create_source = g_converter_output_stream_create_source;
154 static void
155 g_converter_output_stream_finalize (GObject *object)
157 GConverterOutputStreamPrivate *priv;
158 GConverterOutputStream *stream;
160 stream = G_CONVERTER_OUTPUT_STREAM (object);
161 priv = stream->priv;
163 g_free (priv->output_buffer.data);
164 g_free (priv->converted_buffer.data);
165 if (priv->converter)
166 g_object_unref (priv->converter);
168 G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
171 static void
172 g_converter_output_stream_set_property (GObject *object,
173 guint prop_id,
174 const GValue *value,
175 GParamSpec *pspec)
177 GConverterOutputStream *cstream;
179 cstream = G_CONVERTER_OUTPUT_STREAM (object);
181 switch (prop_id)
183 case PROP_CONVERTER:
184 cstream->priv->converter = g_value_dup_object (value);
185 break;
187 default:
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
189 break;
194 static void
195 g_converter_output_stream_get_property (GObject *object,
196 guint prop_id,
197 GValue *value,
198 GParamSpec *pspec)
200 GConverterOutputStreamPrivate *priv;
201 GConverterOutputStream *cstream;
203 cstream = G_CONVERTER_OUTPUT_STREAM (object);
204 priv = cstream->priv;
206 switch (prop_id)
208 case PROP_CONVERTER:
209 g_value_set_object (value, priv->converter);
210 break;
212 default:
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214 break;
218 static void
219 g_converter_output_stream_init (GConverterOutputStream *stream)
221 stream->priv = g_converter_output_stream_get_instance_private (stream);
225 * g_converter_output_stream_new:
226 * @base_stream: a #GOutputStream
227 * @converter: a #GConverter
229 * Creates a new converter output stream for the @base_stream.
231 * Returns: a new #GOutputStream.
233 GOutputStream *
234 g_converter_output_stream_new (GOutputStream *base_stream,
235 GConverter *converter)
237 GOutputStream *stream;
239 g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
241 stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
242 "base-stream", base_stream,
243 "converter", converter,
244 NULL);
246 return stream;
249 static gsize
250 buffer_data_size (Buffer *buffer)
252 return buffer->end - buffer->start;
255 static gsize
256 buffer_tailspace (Buffer *buffer)
258 return buffer->size - buffer->end;
261 static char *
262 buffer_data (Buffer *buffer)
264 return buffer->data + buffer->start;
267 static void
268 buffer_consumed (Buffer *buffer,
269 gsize count)
271 buffer->start += count;
272 if (buffer->start == buffer->end)
273 buffer->start = buffer->end = 0;
276 static void
277 compact_buffer (Buffer *buffer)
279 gsize in_buffer;
281 in_buffer = buffer_data_size (buffer);
282 memmove (buffer->data,
283 buffer->data + buffer->start,
284 in_buffer);
285 buffer->end -= buffer->start;
286 buffer->start = 0;
289 static void
290 grow_buffer (Buffer *buffer)
292 char *data;
293 gsize size, in_buffer;
295 if (buffer->size == 0)
296 size = INITIAL_BUFFER_SIZE;
297 else
298 size = buffer->size * 2;
300 data = g_malloc (size);
301 in_buffer = buffer_data_size (buffer);
303 memcpy (data,
304 buffer->data + buffer->start,
305 in_buffer);
306 g_free (buffer->data);
307 buffer->data = data;
308 buffer->end -= buffer->start;
309 buffer->start = 0;
310 buffer->size = size;
313 /* Ensures that the buffer can fit at_least_size bytes,
314 * *including* the current in-buffer data */
315 static void
316 buffer_ensure_space (Buffer *buffer,
317 gsize at_least_size)
319 gsize in_buffer, left_to_fill;
321 in_buffer = buffer_data_size (buffer);
323 if (in_buffer >= at_least_size)
324 return;
326 left_to_fill = buffer_tailspace (buffer);
328 if (in_buffer + left_to_fill >= at_least_size)
330 /* We fit in remaining space at end */
331 /* If the copy is small, compact now anyway so we can fill more */
332 if (in_buffer < 256)
333 compact_buffer (buffer);
335 else if (buffer->size >= at_least_size)
337 /* We fit, but only if we compact */
338 compact_buffer (buffer);
340 else
342 /* Need to grow buffer */
343 while (buffer->size < at_least_size)
344 grow_buffer (buffer);
348 static void
349 buffer_append (Buffer *buffer,
350 const char *data,
351 gsize data_size)
353 buffer_ensure_space (buffer,
354 buffer_data_size (buffer) + data_size);
355 memcpy (buffer->data + buffer->end, data, data_size);
356 buffer->end += data_size;
360 static gboolean
361 flush_buffer (GConverterOutputStream *stream,
362 gboolean blocking,
363 GCancellable *cancellable,
364 GError **error)
366 GConverterOutputStreamPrivate *priv;
367 GOutputStream *base_stream;
368 gsize nwritten;
369 gsize available;
370 gboolean res;
372 priv = stream->priv;
374 base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
376 available = buffer_data_size (&priv->converted_buffer);
377 if (available > 0)
379 res = g_pollable_stream_write_all (base_stream,
380 buffer_data (&priv->converted_buffer),
381 available,
382 blocking,
383 &nwritten,
384 cancellable,
385 error);
386 buffer_consumed (&priv->converted_buffer, nwritten);
387 return res;
389 return TRUE;
393 static gssize
394 write_internal (GOutputStream *stream,
395 const void *buffer,
396 gsize count,
397 gboolean blocking,
398 GCancellable *cancellable,
399 GError **error)
401 GConverterOutputStream *cstream;
402 GConverterOutputStreamPrivate *priv;
403 gssize retval;
404 GConverterResult res;
405 gsize bytes_read;
406 gsize bytes_written;
407 GError *my_error;
408 const char *to_convert;
409 gsize to_convert_size, converted_bytes;
410 gboolean converting_from_buffer;
412 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
413 priv = cstream->priv;
415 /* Write out all available pre-converted data and fail if
416 not possible */
417 if (!flush_buffer (cstream, blocking, cancellable, error))
418 return -1;
420 if (priv->finished)
421 return 0;
423 /* Convert as much as possible */
424 if (buffer_data_size (&priv->output_buffer) > 0)
426 converting_from_buffer = TRUE;
427 buffer_append (&priv->output_buffer, buffer, count);
428 to_convert = buffer_data (&priv->output_buffer);
429 to_convert_size = buffer_data_size (&priv->output_buffer);
431 else
433 converting_from_buffer = FALSE;
434 to_convert = buffer;
435 to_convert_size = count;
438 /* Ensure we have *some* initial target space */
439 buffer_ensure_space (&priv->converted_buffer, to_convert_size);
441 converted_bytes = 0;
442 while (!priv->finished && converted_bytes < to_convert_size)
444 /* Ensure we have *some* target space */
445 if (buffer_tailspace (&priv->converted_buffer) == 0)
446 grow_buffer (&priv->converted_buffer);
448 /* Try to convert to our buffer */
449 my_error = NULL;
450 res = g_converter_convert (priv->converter,
451 to_convert + converted_bytes,
452 to_convert_size - converted_bytes,
453 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
454 buffer_tailspace (&priv->converted_buffer),
456 &bytes_read,
457 &bytes_written,
458 &my_error);
460 if (res != G_CONVERTER_ERROR)
462 priv->converted_buffer.end += bytes_written;
463 converted_bytes += bytes_read;
465 if (res == G_CONVERTER_FINISHED)
466 priv->finished = TRUE;
468 else
470 /* No-space errors can be handled locally: */
471 if (g_error_matches (my_error,
472 G_IO_ERROR,
473 G_IO_ERROR_NO_SPACE))
475 /* Need more destination space, grow it
476 * Note: if we actually grow the buffer (as opposed to compacting it),
477 * this will double the size, not just add one byte. */
478 buffer_ensure_space (&priv->converted_buffer,
479 priv->converted_buffer.size + 1);
480 g_error_free (my_error);
481 continue;
484 if (converted_bytes > 0)
486 /* We got an conversion error, but we did convert some bytes before
487 that, so handle those before reporting the error */
488 g_error_free (my_error);
489 break;
492 if (g_error_matches (my_error,
493 G_IO_ERROR,
494 G_IO_ERROR_PARTIAL_INPUT))
496 /* Consume everything to buffer that we append to next time
497 we write */
498 if (!converting_from_buffer)
499 buffer_append (&priv->output_buffer, buffer, count);
500 /* in the converting_from_buffer case we already appended this */
502 g_error_free (my_error);
503 return count; /* consume everything */
506 /* Converted no data and got an normal error, return it */
507 g_propagate_error (error, my_error);
508 return -1;
512 if (converting_from_buffer)
514 buffer_consumed (&priv->output_buffer, converted_bytes);
515 retval = count;
517 else
518 retval = converted_bytes;
520 /* We now successfully consumed retval bytes, so we can't return an error,
521 even if writing this to the base stream fails. If it does we'll just
522 stop early and report this error when we try again on the next
523 write call. */
524 flush_buffer (cstream, blocking, cancellable, NULL);
526 return retval;
529 static gssize
530 g_converter_output_stream_write (GOutputStream *stream,
531 const void *buffer,
532 gsize count,
533 GCancellable *cancellable,
534 GError **error)
536 return write_internal (stream, buffer, count, TRUE, cancellable, error);
539 static gboolean
540 g_converter_output_stream_flush (GOutputStream *stream,
541 GCancellable *cancellable,
542 GError **error)
544 GConverterOutputStream *cstream;
545 GConverterOutputStreamPrivate *priv;
546 GConverterResult res;
547 GError *my_error;
548 gboolean is_closing;
549 gboolean flushed;
550 gsize bytes_read;
551 gsize bytes_written;
553 cstream = G_CONVERTER_OUTPUT_STREAM (stream);
554 priv = cstream->priv;
556 is_closing = g_output_stream_is_closing (stream);
558 /* Write out all available pre-converted data and fail if
559 not possible */
560 if (!flush_buffer (cstream, TRUE, cancellable, error))
561 return FALSE;
563 /* Ensure we have *some* initial target space */
564 buffer_ensure_space (&priv->converted_buffer, 1);
566 /* Convert whole buffer */
567 flushed = FALSE;
568 while (!priv->finished && !flushed)
570 /* Ensure we have *some* target space */
571 if (buffer_tailspace (&priv->converted_buffer) == 0)
572 grow_buffer (&priv->converted_buffer);
574 /* Try to convert to our buffer */
575 my_error = NULL;
576 res = g_converter_convert (priv->converter,
577 buffer_data (&priv->output_buffer),
578 buffer_data_size (&priv->output_buffer),
579 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
580 buffer_tailspace (&priv->converted_buffer),
581 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
582 &bytes_read,
583 &bytes_written,
584 &my_error);
586 if (res != G_CONVERTER_ERROR)
588 priv->converted_buffer.end += bytes_written;
589 buffer_consumed (&priv->output_buffer, bytes_read);
591 if (res == G_CONVERTER_FINISHED)
592 priv->finished = TRUE;
593 if (!is_closing &&
594 res == G_CONVERTER_FLUSHED)
596 /* Should not have retured FLUSHED with input left */
597 g_assert (buffer_data_size (&priv->output_buffer) == 0);
598 flushed = TRUE;
601 else
603 /* No-space errors can be handled locally: */
604 if (g_error_matches (my_error,
605 G_IO_ERROR,
606 G_IO_ERROR_NO_SPACE))
608 /* Need more destination space, grow it
609 * Note: if we actually grow the buffer (as opposed to compacting it),
610 * this will double the size, not just add one byte. */
611 buffer_ensure_space (&priv->converted_buffer,
612 priv->converted_buffer.size + 1);
613 g_error_free (my_error);
614 continue;
617 /* Any other error, including PARTIAL_INPUT can't be fixed by now
618 and is an error */
619 g_propagate_error (error, my_error);
620 return FALSE;
624 /* Now write all converted data to base stream */
625 if (!flush_buffer (cstream, TRUE, cancellable, error))
626 return FALSE;
628 return TRUE;
631 static gboolean
632 g_converter_output_stream_can_poll (GPollableOutputStream *stream)
634 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
636 return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
637 g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
640 static gboolean
641 g_converter_output_stream_is_writable (GPollableOutputStream *stream)
643 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
645 return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
648 static gssize
649 g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
650 const void *buffer,
651 gsize count,
652 GError **error)
654 return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
655 NULL, error);
658 static GSource *
659 g_converter_output_stream_create_source (GPollableOutputStream *stream,
660 GCancellable *cancellable)
662 GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
663 GSource *base_source, *pollable_source;
665 base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
666 pollable_source = g_pollable_source_new_full (stream, base_source,
667 cancellable);
668 g_source_unref (base_source);
670 return pollable_source;
674 * g_converter_output_stream_get_converter:
675 * @converter_stream: a #GConverterOutputStream
677 * Gets the #GConverter that is used by @converter_stream.
679 * Returns: (transfer none): the converter of the converter output stream
681 * Since: 2.24
683 GConverter *
684 g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
686 return converter_stream->priv->converter;