X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=gio%2Fgmemoryoutputstream.c;h=a89701d6010dc1d0c5aa2b2541a01216743ed61d;hb=2e5bd8cf47f9e1559ccc44823a2f321b8ff8c1ea;hp=182d2dead19d40cbbc1e64039ae73c075495b102;hpb=d85b722734a6fcfe94032f6113de9e5c190fd7c3;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gmemoryoutputstream.c b/gio/gmemoryoutputstream.c index 182d2de..a89701d 100644 --- a/gio/gmemoryoutputstream.c +++ b/gio/gmemoryoutputstream.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Authors: * Christian Kellner @@ -25,8 +23,9 @@ #include "config.h" #include "gmemoryoutputstream.h" #include "goutputstream.h" +#include "gpollableoutputstream.h" #include "gseekable.h" -#include "gsimpleasyncresult.h" +#include "gtask.h" #include "gioerror.h" #include "string.h" #include "glibintl.h" @@ -41,6 +40,8 @@ * #GMemoryOutputStream is a class for using arbitrary * memory chunks as output for GIO streaming output operations. * + * As of GLib 2.34, #GMemoryOutputStream trivially implements + * #GPollableOutputStream: it always polls as ready. */ #define MIN_ARRAY_SIZE 16 @@ -54,8 +55,8 @@ enum { PROP_DESTROY_FUNCTION }; -struct _GMemoryOutputStreamPrivate { - +struct _GMemoryOutputStreamPrivate +{ gpointer data; /* Write buffer */ gsize len; /* Current length of the data buffer. Can change with resizing. */ gsize valid_len; /* The part of data that has been written to */ @@ -86,16 +87,6 @@ static gboolean g_memory_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error); -static void g_memory_output_stream_write_async (GOutputStream *stream, - const void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gssize g_memory_output_stream_write_finish (GOutputStream *stream, - GAsyncResult *result, - GError **error); static void g_memory_output_stream_close_async (GOutputStream *stream, int io_priority, GCancellable *cancellable, @@ -119,9 +110,18 @@ static gboolean g_memory_output_stream_truncate (GSeekable *see GCancellable *cancellable, GError **error); +static gboolean g_memory_output_stream_is_writable (GPollableOutputStream *stream); +static GSource *g_memory_output_stream_create_source (GPollableOutputStream *stream, + GCancellable *cancellable); + +static void g_memory_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface); + G_DEFINE_TYPE_WITH_CODE (GMemoryOutputStream, g_memory_output_stream, G_TYPE_OUTPUT_STREAM, + G_ADD_PRIVATE (GMemoryOutputStream) G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, - g_memory_output_stream_seekable_iface_init)) + g_memory_output_stream_seekable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, + g_memory_output_stream_pollable_iface_init)) static void @@ -130,8 +130,6 @@ g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass) GOutputStreamClass *ostream_class; GObjectClass *gobject_class; - g_type_class_add_private (klass, sizeof (GMemoryOutputStreamPrivate)); - gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = g_memory_output_stream_set_property; gobject_class->get_property = g_memory_output_stream_get_property; @@ -141,8 +139,6 @@ g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass) ostream_class->write_fn = g_memory_output_stream_write; ostream_class->close_fn = g_memory_output_stream_close; - ostream_class->write_async = g_memory_output_stream_write_async; - ostream_class->write_finish = g_memory_output_stream_write_finish; ostream_class->close_async = g_memory_output_stream_close_async; ostream_class->close_finish = g_memory_output_stream_close_finish; @@ -225,6 +221,13 @@ g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass) } static void +g_memory_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface) +{ + iface->is_writable = g_memory_output_stream_is_writable; + iface->create_source = g_memory_output_stream_create_source; +} + +static void g_memory_output_stream_set_property (GObject *object, guint prop_id, const GValue *value, @@ -320,42 +323,63 @@ g_memory_output_stream_seekable_iface_init (GSeekableIface *iface) static void g_memory_output_stream_init (GMemoryOutputStream *stream) { - stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, - G_TYPE_MEMORY_OUTPUT_STREAM, - GMemoryOutputStreamPrivate); + stream->priv = g_memory_output_stream_get_instance_private (stream); stream->priv->pos = 0; stream->priv->valid_len = 0; } /** * g_memory_output_stream_new: (skip) - * @data: pointer to a chunk of memory to use, or %NULL + * @data: (allow-none): pointer to a chunk of memory to use, or %NULL * @size: the size of @data - * @realloc_function: a function with realloc() semantics (like g_realloc()) + * @realloc_function: (allow-none): a function with realloc() semantics (like g_realloc()) * to be called when @data needs to be grown, or %NULL - * @destroy_function: a function to be called on @data when the stream is + * @destroy_function: (allow-none): a function to be called on @data when the stream is * finalized, or %NULL * * Creates a new #GMemoryOutputStream. * - * If @data is non-%NULL, the stream will use that for its internal storage. + * In most cases this is not the function you want. See + * g_memory_output_stream_new_resizable() instead. + * + * If @data is non-%NULL, the stream will use that for its internal storage. + * * If @realloc_fn is non-%NULL, it will be used for resizing the internal - * storage when necessary. To construct a fixed-size output stream, - * pass %NULL as @realloc_fn. + * storage when necessary and the stream will be considered resizable. + * In that case, the stream will start out being (conceptually) empty. + * @size is used only as a hint for how big @data is. Specifically, + * seeking to the end of a newly-created stream will seek to zero, not + * @size. Seeking past the end of the stream and then writing will + * introduce a zero-filled gap. + * + * If @realloc_fn is %NULL then the stream is fixed-sized. Seeking to + * the end will seek to @size exactly. Writing past the end will give + * an 'out of space' error. Attempting to seek past the end will fail. + * Unlike the resizable case, seeking to an offset within the stream and + * writing will preserve the bytes passed in as @data before that point + * and will return them as part of g_memory_output_stream_steal_data(). + * If you intend to seek you should probably therefore ensure that @data + * is properly initialised. * - * |[ - * /* a stream that can grow */ + * It is probably only meaningful to provide @data and @size in the case + * that you want a fixed-sized stream. Put another way: if @realloc_fn + * is non-%NULL then it makes most sense to give @data as %NULL and + * @size as 0 (allowing #GMemoryOutputStream to do the initial + * allocation for itself). + * + * |[ + * // a stream that can grow * stream = g_memory_output_stream_new (NULL, 0, realloc, free); * - * /* another stream that can grow */ + * // another stream that can grow * stream2 = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); * - * /* a fixed-size stream */ + * // a fixed-size stream * data = malloc (200); * stream3 = g_memory_output_stream_new (data, 200, NULL, free); * ]| * - * Return value: A newly created #GMemoryOutputStream object. + * Returns: A newly created #GMemoryOutputStream object. **/ GOutputStream * g_memory_output_stream_new (gpointer data, @@ -376,6 +400,20 @@ g_memory_output_stream_new (gpointer data, } /** + * g_memory_output_stream_new_resizable: + * + * Creates a new #GMemoryOutputStream, using g_realloc() and g_free() + * for memory allocation. + * + * Since: 2.36 + */ +GOutputStream * +g_memory_output_stream_new_resizable (void) +{ + return g_memory_output_stream_new (NULL, 0, g_realloc, g_free); +} + +/** * g_memory_output_stream_get_data: * @ostream: a #GMemoryOutputStream * @@ -399,16 +437,20 @@ g_memory_output_stream_get_data (GMemoryOutputStream *ostream) * @ostream: a #GMemoryOutputStream * * Gets the size of the currently allocated data area (available from - * g_memory_output_stream_get_data()). If the stream isn't - * growable (no realloc was passed to g_memory_output_stream_new()) then - * this is the maximum size of the stream and further writes - * will return %G_IO_ERROR_NO_SPACE. + * g_memory_output_stream_get_data()). + * + * You probably don't want to use this function on resizable streams. + * See g_memory_output_stream_get_data_size() instead. For resizable + * streams the size returned by this function is an implementation + * detail and may be change at any time in response to operations on the + * stream. * - * Note that for growable streams the returned size may become invalid on - * the next write or truncate operation on the stream. + * If the stream is fixed-sized (ie: no realloc was passed to + * g_memory_output_stream_new()) then this is the maximum size of the + * stream and further writes will return %G_IO_ERROR_NO_SPACE. * - * If you want the number of bytes currently written to the stream, use - * g_memory_output_stream_get_data_size(). + * In any case, if you want the number of bytes currently written to the + * stream, use g_memory_output_stream_get_data_size(). * * Returns: the number of bytes allocated for the data buffer */ @@ -424,9 +466,8 @@ g_memory_output_stream_get_size (GMemoryOutputStream *ostream) * g_memory_output_stream_get_data_size: * @ostream: a #GMemoryOutputStream * - * Returns the number of bytes from the start up - * to including the last byte written in the stream - * that has not been truncated away. + * Returns the number of bytes from the start up to including the last + * byte written in the stream that has not been truncated away. * * Returns: the number of bytes written to the stream * @@ -469,6 +510,34 @@ g_memory_output_stream_steal_data (GMemoryOutputStream *ostream) return data; } +/** + * g_memory_output_stream_steal_as_bytes: + * @ostream: a #GMemoryOutputStream + * + * Returns data from the @ostream as a #GBytes. @ostream must be + * closed before calling this function. + * + * Returns: (transfer full): the stream's data + * + * Since: 2.34 + **/ +GBytes * +g_memory_output_stream_steal_as_bytes (GMemoryOutputStream *ostream) +{ + GBytes *result; + + g_return_val_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream), NULL); + g_return_val_if_fail (g_output_stream_is_closed (G_OUTPUT_STREAM (ostream)), NULL); + + result = g_bytes_new_with_free_func (ostream->priv->data, + ostream->priv->valid_len, + ostream->priv->destroy, + ostream->priv->data); + ostream->priv->data = NULL; + + return result; +} + static gboolean array_resize (GMemoryOutputStream *ostream, gsize size, @@ -525,12 +594,12 @@ array_resize (GMemoryOutputStream *ostream, return TRUE; } -static gint -g_nearest_pow (gint num) +static gsize +g_nearest_pow (gsize num) { - gint n = 1; + gsize n = 1; - while (n < num) + while (n < num && n > 0) n <<= 1; return n; @@ -561,17 +630,19 @@ g_memory_output_stream_write (GOutputStream *stream, if (priv->pos + count > priv->len) { - /* At least enought to fit the write, rounded up - for greater than linear growth. - TODO: This wastes a lot of memory at large stream sizes. - Figure out a more rational allocation strategy. */ + /* At least enough to fit the write, rounded up for greater than + * linear growth. + * + * Assuming that we're using something like realloc(), the kernel + * will overcommit memory to us, so doubling the size each time + * will keep the number of realloc calls low without wasting too + * much memory. + */ new_size = g_nearest_pow (priv->pos + count); - /* Check for overflow again. We have only checked if - pos + count > G_MAXSIZE, but it only catches the case of writing - more than 4GiB total on a 32-bit system. There's still the problem - of g_nearest_pow overflowing above 0x7fffffff, so we're - effectively limited to 2GiB. */ - if (new_size < priv->len) + /* Check for overflow again. We have checked if + pos + count > G_MAXSIZE, but now check if g_nearest_pow () has + overflowed */ + if (new_size == 0) goto overflow; new_size = MAX (new_size, MIN_ARRAY_SIZE); @@ -611,75 +682,21 @@ g_memory_output_stream_close (GOutputStream *stream, } static void -g_memory_output_stream_write_async (GOutputStream *stream, - const void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data) -{ - GSimpleAsyncResult *simple; - GError *error = NULL; - gssize nwritten; - - nwritten = G_OUTPUT_STREAM_GET_CLASS (stream)->write_fn (stream, - buffer, - count, - cancellable, - &error); - - simple = g_simple_async_result_new (G_OBJECT (stream), - callback, - data, - g_memory_output_stream_write_async); - - if (error) - g_simple_async_result_take_error (simple, error); - else - g_simple_async_result_set_op_res_gssize (simple, nwritten); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); -} - -static gssize -g_memory_output_stream_write_finish (GOutputStream *stream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - gssize nwritten; - - simple = G_SIMPLE_ASYNC_RESULT (result); - - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == - g_memory_output_stream_write_async); - - nwritten = g_simple_async_result_get_op_res_gssize (simple); - - return nwritten; -} - -static void g_memory_output_stream_close_async (GOutputStream *stream, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) { - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (G_OBJECT (stream), - callback, - data, - g_memory_output_stream_close_async); + GTask *task; + task = g_task_new (stream, cancellable, callback, data); /* will always return TRUE */ g_memory_output_stream_close (stream, cancellable, NULL); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); + g_task_return_boolean (task, TRUE); + g_object_unref (task); } static gboolean @@ -687,14 +704,9 @@ g_memory_output_stream_close_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { - GSimpleAsyncResult *simple; - - simple = G_SIMPLE_ASYNC_RESULT (result); + g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == - g_memory_output_stream_close_async); - - return TRUE; + return g_task_propagate_boolean (G_TASK (result), error); } static goffset @@ -717,10 +729,10 @@ g_memory_output_stream_can_seek (GSeekable *seekable) static gboolean g_memory_output_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error) + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error) { GMemoryOutputStream *stream; GMemoryOutputStreamPrivate *priv; @@ -740,7 +752,14 @@ g_memory_output_stream_seek (GSeekable *seekable, break; case G_SEEK_END: - absolute = priv->len + offset; + /* For resizable streams, we consider the end to be the data + * length. For fixed-sized streams, we consider the end to be the + * size of the buffer. + */ + if (priv->realloc_fn) + absolute = priv->valid_len + offset; + else + absolute = priv->len + offset; break; default: @@ -761,7 +780,13 @@ g_memory_output_stream_seek (GSeekable *seekable, return FALSE; } - if (absolute > priv->len) + /* Can't seek past the end of a fixed-size stream. + * + * Note: seeking to the non-existent byte at the end of a fixed-sized + * stream is valid (eg: a 1-byte fixed sized stream can have position + * 0 or 1). Therefore '>' is what we want. + * */ + if (priv->realloc_fn == NULL && absolute > priv->len) { g_set_error_literal (error, G_IO_ERROR, @@ -784,6 +809,7 @@ g_memory_output_stream_can_truncate (GSeekable *seekable) ostream = G_MEMORY_OUTPUT_STREAM (seekable); priv = ostream->priv; + /* We do not allow truncation of fixed-sized streams */ return priv->realloc_fn != NULL; } @@ -798,5 +824,26 @@ g_memory_output_stream_truncate (GSeekable *seekable, if (!array_resize (ostream, offset, FALSE, error)) return FALSE; + ostream->priv->valid_len = offset; + return TRUE; } + +static gboolean +g_memory_output_stream_is_writable (GPollableOutputStream *stream) +{ + return TRUE; +} + +static GSource * +g_memory_output_stream_create_source (GPollableOutputStream *stream, + GCancellable *cancellable) +{ + GSource *base_source, *pollable_source; + + base_source = g_timeout_source_new (0); + pollable_source = g_pollable_source_new_full (stream, base_source, cancellable); + g_source_unref (base_source); + + return pollable_source; +}