Blame gio/gconverteroutputstream.c

Packit ae235b
/* GIO - GLib Input, Output and Streaming Library
Packit ae235b
 *
Packit ae235b
 * Copyright (C) 2009 Red Hat, Inc.
Packit ae235b
 *
Packit ae235b
 * This library is free software; you can redistribute it and/or
Packit ae235b
 * modify it under the terms of the GNU Lesser General Public
Packit ae235b
 * License as published by the Free Software Foundation; either
Packit ae235b
 * version 2.1 of the License, or (at your option) any later version.
Packit ae235b
 *
Packit ae235b
 * This library is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit ae235b
 * Lesser General Public License for more details.
Packit ae235b
 *
Packit ae235b
 * You should have received a copy of the GNU Lesser General
Packit ae235b
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit ae235b
 *
Packit ae235b
 * Author: Alexander Larsson <alexl@redhat.com>
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include "config.h"
Packit ae235b
Packit ae235b
#include <string.h>
Packit ae235b
Packit ae235b
#include "gconverteroutputstream.h"
Packit ae235b
#include "gpollableoutputstream.h"
Packit ae235b
#include "gcancellable.h"
Packit ae235b
#include "gioenumtypes.h"
Packit ae235b
#include "gioerror.h"
Packit ae235b
#include "glibintl.h"
Packit ae235b
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * SECTION:gconverteroutputstream
Packit ae235b
 * @short_description: Converter Output Stream
Packit ae235b
 * @include: gio/gio.h
Packit ae235b
 * @see_also: #GOutputStream, #GConverter
Packit ae235b
 *
Packit ae235b
 * Converter output stream implements #GOutputStream and allows
Packit ae235b
 * conversion of data of various types during reading.
Packit ae235b
 *
Packit ae235b
 * As of GLib 2.34, #GConverterOutputStream implements
Packit ae235b
 * #GPollableOutputStream.
Packit ae235b
 **/
Packit ae235b
Packit ae235b
#define INITIAL_BUFFER_SIZE 4096
Packit ae235b
Packit ae235b
typedef struct {
Packit ae235b
  char *data;
Packit ae235b
  gsize start;
Packit ae235b
  gsize end;
Packit ae235b
  gsize size;
Packit ae235b
} Buffer;
Packit ae235b
Packit ae235b
struct _GConverterOutputStreamPrivate {
Packit ae235b
  gboolean at_output_end;
Packit ae235b
  gboolean finished;
Packit ae235b
  GConverter *converter;
Packit ae235b
  Buffer output_buffer; /* To be converted and written */
Packit ae235b
  Buffer converted_buffer; /* Already converted */
Packit ae235b
};
Packit ae235b
Packit ae235b
/* Buffering strategy:
Packit ae235b
 *
Packit ae235b
 * Each time we write we must at least consume some input, or
Packit ae235b
 * return an error. Thus we start with writing all already
Packit ae235b
 * converted data and *then* we start converting (reporting
Packit ae235b
 * an error at any point in this).
Packit ae235b
 *
Packit ae235b
 * Its possible that what the user wrote is not enough data
Packit ae235b
 * for the converter, so we must then buffer it in output_buffer
Packit ae235b
 * and ask for more data, but we want to avoid this as much as
Packit ae235b
 * possible, converting directly from the users buffer.
Packit ae235b
 */
Packit ae235b
Packit ae235b
enum {
Packit ae235b
  PROP_0,
Packit ae235b
  PROP_CONVERTER
Packit ae235b
};
Packit ae235b
Packit ae235b
static void   g_converter_output_stream_set_property (GObject        *object,
Packit ae235b
						      guint           prop_id,
Packit ae235b
						      const GValue   *value,
Packit ae235b
						      GParamSpec     *pspec);
Packit ae235b
static void   g_converter_output_stream_get_property (GObject        *object,
Packit ae235b
						      guint           prop_id,
Packit ae235b
						      GValue         *value,
Packit ae235b
						      GParamSpec     *pspec);
Packit ae235b
static void   g_converter_output_stream_finalize     (GObject        *object);
Packit ae235b
static gssize g_converter_output_stream_write        (GOutputStream  *stream,
Packit ae235b
						      const void     *buffer,
Packit ae235b
						      gsize           count,
Packit ae235b
						      GCancellable   *cancellable,
Packit ae235b
						      GError        **error);
Packit ae235b
static gboolean g_converter_output_stream_flush      (GOutputStream  *stream,
Packit ae235b
						      GCancellable   *cancellable,
Packit ae235b
						      GError        **error);
Packit ae235b
Packit ae235b
static gboolean g_converter_output_stream_can_poll          (GPollableOutputStream *stream);
Packit ae235b
static gboolean g_converter_output_stream_is_writable       (GPollableOutputStream *stream);
Packit ae235b
static gssize   g_converter_output_stream_write_nonblocking (GPollableOutputStream  *stream,
Packit ae235b
							     const void             *buffer,
Packit ae235b
							     gsize                  size,
Packit ae235b
							     GError               **error);
Packit ae235b
Packit ae235b
static GSource *g_converter_output_stream_create_source     (GPollableOutputStream *stream,
Packit ae235b
							     GCancellable          *cancellable);
Packit ae235b
Packit ae235b
static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
Packit ae235b
Packit ae235b
G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
Packit ae235b
			 g_converter_output_stream,
Packit ae235b
			 G_TYPE_FILTER_OUTPUT_STREAM,
Packit ae235b
                         G_ADD_PRIVATE (GConverterOutputStream)
Packit ae235b
			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
Packit ae235b
						g_converter_output_stream_pollable_iface_init))
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
Packit ae235b
{
Packit ae235b
  GObjectClass *object_class;
Packit ae235b
  GOutputStreamClass *istream_class;
Packit ae235b
Packit ae235b
  object_class = G_OBJECT_CLASS (klass);
Packit ae235b
  object_class->get_property = g_converter_output_stream_get_property;
Packit ae235b
  object_class->set_property = g_converter_output_stream_set_property;
Packit ae235b
  object_class->finalize     = g_converter_output_stream_finalize;
Packit ae235b
Packit ae235b
  istream_class = G_OUTPUT_STREAM_CLASS (klass);
Packit ae235b
  istream_class->write_fn = g_converter_output_stream_write;
Packit ae235b
  istream_class->flush = g_converter_output_stream_flush;
Packit ae235b
Packit ae235b
  g_object_class_install_property (object_class,
Packit ae235b
				   PROP_CONVERTER,
Packit ae235b
				   g_param_spec_object ("converter",
Packit ae235b
							P_("Converter"),
Packit ae235b
							P_("The converter object"),
Packit ae235b
							G_TYPE_CONVERTER,
Packit ae235b
							G_PARAM_READWRITE|
Packit ae235b
							G_PARAM_CONSTRUCT_ONLY|
Packit ae235b
							G_PARAM_STATIC_STRINGS));
Packit ae235b
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
Packit ae235b
{
Packit ae235b
  iface->can_poll = g_converter_output_stream_can_poll;
Packit ae235b
  iface->is_writable = g_converter_output_stream_is_writable;
Packit ae235b
  iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
Packit ae235b
  iface->create_source = g_converter_output_stream_create_source;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_converter_output_stream_finalize (GObject *object)
Packit ae235b
{
Packit ae235b
  GConverterOutputStreamPrivate *priv;
Packit ae235b
  GConverterOutputStream        *stream;
Packit ae235b
Packit ae235b
  stream = G_CONVERTER_OUTPUT_STREAM (object);
Packit ae235b
  priv = stream->priv;
Packit ae235b
Packit ae235b
  g_free (priv->output_buffer.data);
Packit ae235b
  g_free (priv->converted_buffer.data);
Packit ae235b
  if (priv->converter)
Packit ae235b
    g_object_unref (priv->converter);
Packit ae235b
Packit ae235b
  G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_converter_output_stream_set_property (GObject      *object,
Packit ae235b
				       guint         prop_id,
Packit ae235b
				       const GValue *value,
Packit ae235b
				       GParamSpec   *pspec)
Packit ae235b
{
Packit ae235b
  GConverterOutputStream *cstream;
Packit ae235b
Packit ae235b
  cstream = G_CONVERTER_OUTPUT_STREAM (object);
Packit ae235b
Packit ae235b
   switch (prop_id)
Packit ae235b
    {
Packit ae235b
    case PROP_CONVERTER:
Packit ae235b
      cstream->priv->converter = g_value_dup_object (value);
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    default:
Packit ae235b
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit ae235b
      break;
Packit ae235b
    }
Packit ae235b
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_converter_output_stream_get_property (GObject    *object,
Packit ae235b
				       guint       prop_id,
Packit ae235b
				       GValue     *value,
Packit ae235b
				       GParamSpec *pspec)
Packit ae235b
{
Packit ae235b
  GConverterOutputStreamPrivate *priv;
Packit ae235b
  GConverterOutputStream        *cstream;
Packit ae235b
Packit ae235b
  cstream = G_CONVERTER_OUTPUT_STREAM (object);
Packit ae235b
  priv = cstream->priv;
Packit ae235b
Packit ae235b
  switch (prop_id)
Packit ae235b
    {
Packit ae235b
    case PROP_CONVERTER:
Packit ae235b
      g_value_set_object (value, priv->converter);
Packit ae235b
      break;
Packit ae235b
Packit ae235b
    default:
Packit ae235b
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit ae235b
      break;
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
g_converter_output_stream_init (GConverterOutputStream *stream)
Packit ae235b
{
Packit ae235b
  stream->priv = g_converter_output_stream_get_instance_private (stream);
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_converter_output_stream_new:
Packit ae235b
 * @base_stream: a #GOutputStream
Packit ae235b
 * @converter: a #GConverter
Packit ae235b
 *
Packit ae235b
 * Creates a new converter output stream for the @base_stream.
Packit ae235b
 *
Packit ae235b
 * Returns: a new #GOutputStream.
Packit ae235b
 **/
Packit ae235b
GOutputStream *
Packit ae235b
g_converter_output_stream_new (GOutputStream *base_stream,
Packit ae235b
                               GConverter    *converter)
Packit ae235b
{
Packit ae235b
  GOutputStream *stream;
Packit ae235b
Packit ae235b
  g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
Packit ae235b
Packit ae235b
  stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
Packit ae235b
                         "base-stream", base_stream,
Packit ae235b
			 "converter", converter,
Packit ae235b
			 NULL);
Packit ae235b
Packit ae235b
  return stream;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gsize
Packit ae235b
buffer_data_size (Buffer *buffer)
Packit ae235b
{
Packit ae235b
  return buffer->end - buffer->start;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gsize
Packit ae235b
buffer_tailspace (Buffer *buffer)
Packit ae235b
{
Packit ae235b
  return buffer->size - buffer->end;
Packit ae235b
}
Packit ae235b
Packit ae235b
static char *
Packit ae235b
buffer_data (Buffer *buffer)
Packit ae235b
{
Packit ae235b
  return buffer->data + buffer->start;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
buffer_consumed (Buffer *buffer,
Packit ae235b
		 gsize count)
Packit ae235b
{
Packit ae235b
  buffer->start += count;
Packit ae235b
  if (buffer->start == buffer->end)
Packit ae235b
    buffer->start = buffer->end = 0;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
compact_buffer (Buffer *buffer)
Packit ae235b
{
Packit ae235b
  gsize in_buffer;
Packit ae235b
Packit ae235b
  in_buffer = buffer_data_size (buffer);
Packit ae235b
  memmove (buffer->data,
Packit ae235b
	   buffer->data + buffer->start,
Packit ae235b
	   in_buffer);
Packit ae235b
  buffer->end -= buffer->start;
Packit ae235b
  buffer->start = 0;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
grow_buffer (Buffer *buffer)
Packit ae235b
{
Packit ae235b
  char *data;
Packit ae235b
  gsize size, in_buffer;
Packit ae235b
Packit ae235b
  if (buffer->size == 0)
Packit ae235b
    size = INITIAL_BUFFER_SIZE;
Packit ae235b
  else
Packit ae235b
    size = buffer->size * 2;
Packit ae235b
Packit ae235b
  data = g_malloc (size);
Packit ae235b
  in_buffer = buffer_data_size (buffer);
Packit ae235b
Packit ae235b
  if (in_buffer != 0)
Packit ae235b
    memcpy (data,
Packit ae235b
            buffer->data + buffer->start,
Packit ae235b
            in_buffer);
Packit ae235b
Packit ae235b
  g_free (buffer->data);
Packit ae235b
  buffer->data = data;
Packit ae235b
  buffer->end -= buffer->start;
Packit ae235b
  buffer->start = 0;
Packit ae235b
  buffer->size = size;
Packit ae235b
}
Packit ae235b
Packit ae235b
/* Ensures that the buffer can fit at_least_size bytes,
Packit ae235b
 * *including* the current in-buffer data */
Packit ae235b
static void
Packit ae235b
buffer_ensure_space (Buffer *buffer,
Packit ae235b
		     gsize at_least_size)
Packit ae235b
{
Packit ae235b
  gsize in_buffer, left_to_fill;
Packit ae235b
Packit ae235b
  in_buffer = buffer_data_size (buffer);
Packit ae235b
Packit ae235b
  if (in_buffer >= at_least_size)
Packit ae235b
    return;
Packit ae235b
Packit ae235b
  left_to_fill = buffer_tailspace (buffer);
Packit ae235b
Packit ae235b
  if (in_buffer + left_to_fill >= at_least_size)
Packit ae235b
    {
Packit ae235b
      /* We fit in remaining space at end */
Packit ae235b
      /* If the copy is small, compact now anyway so we can fill more */
Packit ae235b
      if (in_buffer < 256)
Packit ae235b
	compact_buffer (buffer);
Packit ae235b
    }
Packit ae235b
  else if (buffer->size >= at_least_size)
Packit ae235b
    {
Packit ae235b
      /* We fit, but only if we compact */
Packit ae235b
      compact_buffer (buffer);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      /* Need to grow buffer */
Packit ae235b
      while (buffer->size < at_least_size)
Packit ae235b
	grow_buffer (buffer);
Packit ae235b
    }
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
buffer_append (Buffer *buffer,
Packit ae235b
	       const char *data,
Packit ae235b
	       gsize data_size)
Packit ae235b
{
Packit ae235b
  buffer_ensure_space (buffer,
Packit ae235b
		       buffer_data_size (buffer) + data_size);
Packit ae235b
  memcpy (buffer->data + buffer->end, data, data_size);
Packit ae235b
  buffer->end += data_size;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
flush_buffer (GConverterOutputStream *stream,
Packit ae235b
	      gboolean                blocking,
Packit ae235b
	      GCancellable           *cancellable,
Packit ae235b
	      GError                **error)
Packit ae235b
{
Packit ae235b
  GConverterOutputStreamPrivate *priv;
Packit ae235b
  GOutputStream *base_stream;
Packit ae235b
  gsize nwritten;
Packit ae235b
  gsize available;
Packit ae235b
  gboolean res;
Packit ae235b
Packit ae235b
  priv = stream->priv;
Packit ae235b
Packit ae235b
  base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
Packit ae235b
Packit ae235b
  available = buffer_data_size (&priv->converted_buffer);
Packit ae235b
  if (available > 0)
Packit ae235b
    {
Packit ae235b
      res = g_pollable_stream_write_all (base_stream,
Packit ae235b
					 buffer_data (&priv->converted_buffer),
Packit ae235b
					 available,
Packit ae235b
					 blocking,
Packit ae235b
					 &nwritten,
Packit ae235b
					 cancellable,
Packit ae235b
					 error);
Packit ae235b
      buffer_consumed (&priv->converted_buffer, nwritten);
Packit ae235b
      return res;
Packit ae235b
    }
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
static gssize
Packit ae235b
write_internal (GOutputStream  *stream,
Packit ae235b
		const void     *buffer,
Packit ae235b
		gsize           count,
Packit ae235b
		gboolean        blocking,
Packit ae235b
		GCancellable   *cancellable,
Packit ae235b
		GError        **error)
Packit ae235b
{
Packit ae235b
  GConverterOutputStream *cstream;
Packit ae235b
  GConverterOutputStreamPrivate *priv;
Packit ae235b
  gssize retval;
Packit ae235b
  GConverterResult res;
Packit ae235b
  gsize bytes_read;
Packit ae235b
  gsize bytes_written;
Packit ae235b
  GError *my_error;
Packit ae235b
  const char *to_convert;
Packit ae235b
  gsize to_convert_size, converted_bytes;
Packit ae235b
  gboolean converting_from_buffer;
Packit ae235b
Packit ae235b
  cstream = G_CONVERTER_OUTPUT_STREAM (stream);
Packit ae235b
  priv = cstream->priv;
Packit ae235b
Packit ae235b
  /* Write out all available pre-converted data and fail if
Packit ae235b
     not possible */
Packit ae235b
  if (!flush_buffer (cstream, blocking, cancellable, error))
Packit ae235b
    return -1;
Packit ae235b
Packit ae235b
  if (priv->finished)
Packit ae235b
    return 0;
Packit ae235b
Packit ae235b
  /* Convert as much as possible */
Packit ae235b
  if (buffer_data_size (&priv->output_buffer) > 0)
Packit ae235b
    {
Packit ae235b
      converting_from_buffer = TRUE;
Packit ae235b
      buffer_append (&priv->output_buffer, buffer, count);
Packit ae235b
      to_convert = buffer_data (&priv->output_buffer);
Packit ae235b
      to_convert_size = buffer_data_size (&priv->output_buffer);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      converting_from_buffer = FALSE;
Packit ae235b
      to_convert = buffer;
Packit ae235b
      to_convert_size = count;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  /* Ensure we have *some* initial target space */
Packit ae235b
  buffer_ensure_space (&priv->converted_buffer, to_convert_size);
Packit ae235b
Packit ae235b
  converted_bytes = 0;
Packit ae235b
  while (!priv->finished && converted_bytes < to_convert_size)
Packit ae235b
    {
Packit ae235b
      /* Ensure we have *some* target space */
Packit ae235b
      if (buffer_tailspace (&priv->converted_buffer) == 0)
Packit ae235b
	grow_buffer (&priv->converted_buffer);
Packit ae235b
Packit ae235b
      /* Try to convert to our buffer */
Packit ae235b
      my_error = NULL;
Packit ae235b
      res = g_converter_convert (priv->converter,
Packit ae235b
				 to_convert + converted_bytes,
Packit ae235b
				 to_convert_size - converted_bytes,
Packit ae235b
				 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
Packit ae235b
				 buffer_tailspace (&priv->converted_buffer),
Packit ae235b
				 0,
Packit ae235b
				 &bytes_read,
Packit ae235b
				 &bytes_written,
Packit ae235b
				 &my_error);
Packit ae235b
Packit ae235b
      if (res != G_CONVERTER_ERROR)
Packit ae235b
	{
Packit ae235b
	  priv->converted_buffer.end += bytes_written;
Packit ae235b
	  converted_bytes += bytes_read;
Packit ae235b
Packit ae235b
	  if (res == G_CONVERTER_FINISHED)
Packit ae235b
	    priv->finished = TRUE;
Packit ae235b
	}
Packit ae235b
      else
Packit ae235b
	{
Packit ae235b
	  /* No-space errors can be handled locally: */
Packit ae235b
	  if (g_error_matches (my_error,
Packit ae235b
			       G_IO_ERROR,
Packit ae235b
			       G_IO_ERROR_NO_SPACE))
Packit ae235b
	    {
Packit ae235b
	      /* Need more destination space, grow it
Packit ae235b
	       * Note: if we actually grow the buffer (as opposed to compacting it),
Packit ae235b
	       * this will double the size, not just add one byte. */
Packit ae235b
	      buffer_ensure_space (&priv->converted_buffer,
Packit ae235b
				   priv->converted_buffer.size + 1);
Packit ae235b
	      g_error_free (my_error);
Packit ae235b
	      continue;
Packit ae235b
	    }
Packit ae235b
Packit ae235b
	  if (converted_bytes > 0)
Packit ae235b
	    {
Packit ae235b
	      /* We got an conversion error, but we did convert some bytes before
Packit ae235b
		 that, so handle those before reporting the error */
Packit ae235b
	      g_error_free (my_error);
Packit ae235b
	      break;
Packit ae235b
	    }
Packit ae235b
Packit ae235b
	  if (g_error_matches (my_error,
Packit ae235b
			       G_IO_ERROR,
Packit ae235b
			       G_IO_ERROR_PARTIAL_INPUT))
Packit ae235b
	    {
Packit ae235b
	      /* Consume everything to buffer that we append to next time
Packit ae235b
		 we write */
Packit ae235b
	      if (!converting_from_buffer)
Packit ae235b
		buffer_append (&priv->output_buffer, buffer, count);
Packit ae235b
	      /* in the converting_from_buffer case we already appended this */
Packit ae235b
Packit ae235b
              g_error_free (my_error);
Packit ae235b
	      return count; /* consume everything */
Packit ae235b
	    }
Packit ae235b
Packit ae235b
	  /* Converted no data and got an normal error, return it */
Packit ae235b
	  g_propagate_error (error, my_error);
Packit ae235b
	  return -1;
Packit ae235b
	}
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (converting_from_buffer)
Packit ae235b
    {
Packit ae235b
      buffer_consumed (&priv->output_buffer, converted_bytes);
Packit ae235b
      retval = count;
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    retval = converted_bytes;
Packit ae235b
Packit ae235b
  /* We now successfully consumed retval bytes, so we can't return an error,
Packit ae235b
     even if writing this to the base stream fails. If it does we'll just
Packit ae235b
     stop early and report this error when we try again on the next
Packit ae235b
     write call. */
Packit ae235b
  flush_buffer (cstream, blocking, cancellable, NULL);
Packit ae235b
Packit ae235b
  return retval;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gssize
Packit ae235b
g_converter_output_stream_write (GOutputStream  *stream,
Packit ae235b
				 const void     *buffer,
Packit ae235b
				 gsize           count,
Packit ae235b
				 GCancellable   *cancellable,
Packit ae235b
				 GError        **error)
Packit ae235b
{
Packit ae235b
  return write_internal (stream, buffer, count, TRUE, cancellable, error);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
g_converter_output_stream_flush (GOutputStream  *stream,
Packit ae235b
				 GCancellable   *cancellable,
Packit ae235b
				 GError        **error)
Packit ae235b
{
Packit ae235b
  GConverterOutputStream *cstream;
Packit ae235b
  GConverterOutputStreamPrivate *priv;
Packit ae235b
  GConverterResult res;
Packit ae235b
  GError *my_error;
Packit ae235b
  gboolean is_closing;
Packit ae235b
  gboolean flushed;
Packit ae235b
  gsize bytes_read;
Packit ae235b
  gsize bytes_written;
Packit ae235b
Packit ae235b
  cstream = G_CONVERTER_OUTPUT_STREAM (stream);
Packit ae235b
  priv = cstream->priv;
Packit ae235b
Packit ae235b
  is_closing = g_output_stream_is_closing (stream);
Packit ae235b
Packit ae235b
  /* Write out all available pre-converted data and fail if
Packit ae235b
     not possible */
Packit ae235b
  if (!flush_buffer (cstream, TRUE, cancellable, error))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  /* Ensure we have *some* initial target space */
Packit ae235b
  buffer_ensure_space (&priv->converted_buffer, 1);
Packit ae235b
Packit ae235b
  /* Convert whole buffer */
Packit ae235b
  flushed = FALSE;
Packit ae235b
  while (!priv->finished && !flushed)
Packit ae235b
    {
Packit ae235b
      /* Ensure we have *some* target space */
Packit ae235b
      if (buffer_tailspace (&priv->converted_buffer) == 0)
Packit ae235b
	grow_buffer (&priv->converted_buffer);
Packit ae235b
Packit ae235b
      /* Try to convert to our buffer */
Packit ae235b
      my_error = NULL;
Packit ae235b
      res = g_converter_convert (priv->converter,
Packit ae235b
				 buffer_data (&priv->output_buffer),
Packit ae235b
				 buffer_data_size (&priv->output_buffer),
Packit ae235b
				 buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
Packit ae235b
				 buffer_tailspace (&priv->converted_buffer),
Packit ae235b
				 is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
Packit ae235b
				 &bytes_read,
Packit ae235b
				 &bytes_written,
Packit ae235b
				 &my_error);
Packit ae235b
Packit ae235b
      if (res != G_CONVERTER_ERROR)
Packit ae235b
	{
Packit ae235b
	  priv->converted_buffer.end += bytes_written;
Packit ae235b
	  buffer_consumed (&priv->output_buffer, bytes_read);
Packit ae235b
Packit ae235b
	  if (res == G_CONVERTER_FINISHED)
Packit ae235b
	    priv->finished = TRUE;
Packit ae235b
	  if (!is_closing &&
Packit ae235b
	      res == G_CONVERTER_FLUSHED)
Packit ae235b
	    {
Packit ae235b
	      /* Should not have retured FLUSHED with input left */
Packit ae235b
	      g_assert (buffer_data_size (&priv->output_buffer) == 0);
Packit ae235b
	      flushed = TRUE;
Packit ae235b
	    }
Packit ae235b
	}
Packit ae235b
      else
Packit ae235b
	{
Packit ae235b
	  /* No-space errors can be handled locally: */
Packit ae235b
	  if (g_error_matches (my_error,
Packit ae235b
			       G_IO_ERROR,
Packit ae235b
			       G_IO_ERROR_NO_SPACE))
Packit ae235b
	    {
Packit ae235b
	      /* Need more destination space, grow it
Packit ae235b
	       * Note: if we actually grow the buffer (as opposed to compacting it),
Packit ae235b
	       * this will double the size, not just add one byte. */
Packit ae235b
	      buffer_ensure_space (&priv->converted_buffer,
Packit ae235b
				   priv->converted_buffer.size + 1);
Packit ae235b
	      g_error_free (my_error);
Packit ae235b
	      continue;
Packit ae235b
	    }
Packit ae235b
Packit ae235b
	  /* Any other error, including PARTIAL_INPUT can't be fixed by now
Packit ae235b
	     and is an error */
Packit ae235b
	  g_propagate_error (error, my_error);
Packit ae235b
	  return FALSE;
Packit ae235b
	}
Packit ae235b
    }
Packit ae235b
Packit ae235b
  /* Now write all converted data to base stream */
Packit ae235b
  if (!flush_buffer (cstream, TRUE, cancellable, error))
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
g_converter_output_stream_can_poll (GPollableOutputStream *stream)
Packit ae235b
{
Packit ae235b
  GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
Packit ae235b
Packit ae235b
  return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
Packit ae235b
	  g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
g_converter_output_stream_is_writable (GPollableOutputStream *stream)
Packit ae235b
{
Packit ae235b
  GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
Packit ae235b
Packit ae235b
  return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
Packit ae235b
}
Packit ae235b
Packit ae235b
static gssize
Packit ae235b
g_converter_output_stream_write_nonblocking (GPollableOutputStream  *stream,
Packit ae235b
					     const void             *buffer,
Packit ae235b
					     gsize                   count,
Packit ae235b
					     GError                **error)
Packit ae235b
{
Packit ae235b
  return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
Packit ae235b
			 NULL, error);
Packit ae235b
}
Packit ae235b
Packit ae235b
static GSource *
Packit ae235b
g_converter_output_stream_create_source (GPollableOutputStream *stream,
Packit ae235b
					 GCancellable          *cancellable)
Packit ae235b
{
Packit ae235b
  GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
Packit ae235b
  GSource *base_source, *pollable_source;
Packit ae235b
Packit ae235b
  base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
Packit ae235b
  pollable_source = g_pollable_source_new_full (stream, base_source,
Packit ae235b
						cancellable);
Packit ae235b
  g_source_unref (base_source);
Packit ae235b
Packit ae235b
  return pollable_source;
Packit ae235b
}
Packit ae235b
Packit ae235b
/**
Packit ae235b
 * g_converter_output_stream_get_converter:
Packit ae235b
 * @converter_stream: a #GConverterOutputStream
Packit ae235b
 *
Packit ae235b
 * Gets the #GConverter that is used by @converter_stream.
Packit ae235b
 *
Packit ae235b
 * Returns: (transfer none): the converter of the converter output stream
Packit ae235b
 *
Packit ae235b
 * Since: 2.24
Packit ae235b
 */
Packit ae235b
GConverter *
Packit ae235b
g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
Packit ae235b
{
Packit ae235b
  return converter_stream->priv->converter;
Packit ae235b
}