(root)/
glib-2.79.0/
gio/
gconverteroutputstream.c
       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  }