* 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 <http://www.gnu.org/licenses/>.
*
* Author: Christian Kellner <gicmo@gnome.org>
*/
-#include <config.h>
+#include "config.h"
#include "gbufferedoutputstream.h"
#include "goutputstream.h"
-#include "gsimpleasyncresult.h"
+#include "gseekable.h"
+#include "gtask.h"
#include "string.h"
+#include "gioerror.h"
#include "glibintl.h"
/**
* SECTION:gbufferedoutputstream
* @short_description: Buffered Output Stream
- * @see_also: #GFilterOutputStream, #GOutputStream.
+ * @include: gio/gio.h
+ * @see_also: #GFilterOutputStream, #GOutputStream
*
* Buffered output stream implements #GFilterOutputStream and provides
* for buffered writes.
*
* By default, #GBufferedOutputStream's buffer size is set at 4 kilobytes.
*
- * To create a buffered output stream, use g_buffered_output_stream_new(), or
- * g_buffered_output_stream_new_sized() to specify the buffer's size at construction.
+ * To create a buffered output stream, use g_buffered_output_stream_new(),
+ * or g_buffered_output_stream_new_sized() to specify the buffer's size
+ * at construction.
*
* To get the size of a buffer within a buffered input stream, use
* g_buffered_output_stream_get_buffer_size(). To change the size of a
- * buffered output stream's buffer, use g_buffered_output_stream_set_buffer_size().
- * Note: the buffer's size cannot be reduced below the size of the data within the
- * buffer.
- *
+ * buffered output stream's buffer, use
+ * g_buffered_output_stream_set_buffer_size(). Note that the buffer's
+ * size cannot be reduced below the size of the data within the buffer.
**/
-
-
#define DEFAULT_BUFFER_SIZE 4096
struct _GBufferedOutputStreamPrivate {
enum {
PROP_0,
- PROP_BUFSIZE
+ PROP_BUFSIZE,
+ PROP_AUTO_GROW
};
static void g_buffered_output_stream_set_property (GObject *object,
GCancellable *cancellable,
GError **error);
-static void g_buffered_output_stream_write_async (GOutputStream *stream,
- const void *buffer,
- gsize count,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer data);
-static gssize g_buffered_output_stream_write_finish (GOutputStream *stream,
- GAsyncResult *result,
- GError **error);
static void g_buffered_output_stream_flush_async (GOutputStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncResult *result,
GError **error);
-G_DEFINE_TYPE (GBufferedOutputStream,
- g_buffered_output_stream,
- G_TYPE_FILTER_OUTPUT_STREAM)
+static void g_buffered_output_stream_seekable_iface_init (GSeekableIface *iface);
+static goffset g_buffered_output_stream_tell (GSeekable *seekable);
+static gboolean g_buffered_output_stream_can_seek (GSeekable *seekable);
+static gboolean g_buffered_output_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean g_buffered_output_stream_can_truncate (GSeekable *seekable);
+static gboolean g_buffered_output_stream_truncate (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error);
+
+G_DEFINE_TYPE_WITH_CODE (GBufferedOutputStream,
+ g_buffered_output_stream,
+ G_TYPE_FILTER_OUTPUT_STREAM,
+ G_ADD_PRIVATE (GBufferedOutputStream)
+ G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+ g_buffered_output_stream_seekable_iface_init))
static void
{
GObjectClass *object_class;
GOutputStreamClass *ostream_class;
-
- g_type_class_add_private (klass, sizeof (GBufferedOutputStreamPrivate));
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = g_buffered_output_stream_get_property;
object_class->finalize = g_buffered_output_stream_finalize;
ostream_class = G_OUTPUT_STREAM_CLASS (klass);
- ostream_class->write = g_buffered_output_stream_write;
+ ostream_class->write_fn = g_buffered_output_stream_write;
ostream_class->flush = g_buffered_output_stream_flush;
- ostream_class->close = g_buffered_output_stream_close;
- ostream_class->write_async = g_buffered_output_stream_write_async;
- ostream_class->write_finish = g_buffered_output_stream_write_finish;
+ ostream_class->close_fn = g_buffered_output_stream_close;
ostream_class->flush_async = g_buffered_output_stream_flush_async;
ostream_class->flush_finish = g_buffered_output_stream_flush_finish;
ostream_class->close_async = g_buffered_output_stream_close_async;
1,
G_MAXUINT,
DEFAULT_BUFFER_SIZE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT|
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+ g_object_class_install_property (object_class,
+ PROP_AUTO_GROW,
+ g_param_spec_boolean ("auto-grow",
+ P_("Auto-grow"),
+ P_("Whether the buffer should automatically grow"),
+ FALSE,
+ G_PARAM_READWRITE|
+ G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
}
/**
**/
void
g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
- gsize size)
+ gsize size)
{
GBufferedOutputStreamPrivate *priv;
guint8 *buffer;
priv = stream->priv;
+ if (size == priv->len)
+ return;
+
if (priv->buffer)
{
size = MAX (size, priv->pos);
priv->len = size;
priv->pos = 0;
}
+
+ g_object_notify (G_OBJECT (stream), "buffer-size");
}
/**
* @auto_grow: a #gboolean.
*
* Sets whether or not the @stream's buffer should automatically grow.
+ * If @auto_grow is true, then each write will just make the buffer
+ * larger, and you must manually flush the buffer to actually write out
+ * the data to the underlying stream.
**/
void
g_buffered_output_stream_set_auto_grow (GBufferedOutputStream *stream,
- gboolean auto_grow)
+ gboolean auto_grow)
{
+ GBufferedOutputStreamPrivate *priv;
g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
-
- stream->priv->auto_grow = auto_grow;
+ priv = stream->priv;
+ auto_grow = auto_grow != FALSE;
+ if (priv->auto_grow != auto_grow)
+ {
+ priv->auto_grow = auto_grow;
+ g_object_notify (G_OBJECT (stream), "auto-grow");
+ }
}
static void
-g_buffered_output_stream_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+g_buffered_output_stream_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- GBufferedOutputStream *buffered_stream;
- GBufferedOutputStreamPrivate *priv;
+ GBufferedOutputStream *stream;
- buffered_stream = G_BUFFERED_OUTPUT_STREAM (object);
- priv = buffered_stream->priv;
+ stream = G_BUFFERED_OUTPUT_STREAM (object);
switch (prop_id)
{
-
case PROP_BUFSIZE:
- g_buffered_output_stream_set_buffer_size (buffered_stream, g_value_get_uint (value));
+ g_buffered_output_stream_set_buffer_size (stream, g_value_get_uint (value));
break;
+ case PROP_AUTO_GROW:
+ g_buffered_output_stream_set_auto_grow (stream, g_value_get_boolean (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
switch (prop_id)
{
-
case PROP_BUFSIZE:
g_value_set_uint (value, priv->len);
break;
+ case PROP_AUTO_GROW:
+ g_value_set_boolean (value, priv->auto_grow);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_free (priv->buffer);
- if (G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize)
- (*G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize) (object);
+ G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize (object);
}
static void
g_buffered_output_stream_init (GBufferedOutputStream *stream)
{
- stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
- G_TYPE_BUFFERED_OUTPUT_STREAM,
- GBufferedOutputStreamPrivate);
+ stream->priv = g_buffered_output_stream_get_instance_private (stream);
+}
+static void
+g_buffered_output_stream_seekable_iface_init (GSeekableIface *iface)
+{
+ iface->tell = g_buffered_output_stream_tell;
+ iface->can_seek = g_buffered_output_stream_can_seek;
+ iface->seek = g_buffered_output_stream_seek;
+ iface->can_truncate = g_buffered_output_stream_can_truncate;
+ iface->truncate_fn = g_buffered_output_stream_truncate;
}
/**
**/
GOutputStream *
g_buffered_output_stream_new_sized (GOutputStream *base_stream,
- guint size)
+ gsize size)
{
GOutputStream *stream;
count = priv->pos - bytes_written;
if (count > 0)
- g_memmove (priv->buffer, priv->buffer + bytes_written, count);
+ memmove (priv->buffer, priv->buffer + bytes_written, count);
priv->pos -= bytes_written;
GError **error)
{
GBufferedOutputStream *bstream;
- GBufferedOutputStreamPrivate *priv;
GOutputStream *base_stream;
gboolean res;
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
- priv = bstream->priv;
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
res = flush_buffer (bstream, cancellable, error);
- if (res == FALSE) {
+ if (res == FALSE)
return FALSE;
- }
- res = g_output_stream_flush (base_stream,
- cancellable,
- error);
+ res = g_output_stream_flush (base_stream, cancellable, error);
+
return res;
}
GError **error)
{
GBufferedOutputStream *bstream;
- GBufferedOutputStreamPrivate *priv;
GOutputStream *base_stream;
gboolean res;
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
- priv = bstream->priv;
base_stream = G_FILTER_OUTPUT_STREAM (bstream)->base_stream;
-
res = flush_buffer (bstream, cancellable, error);
- /* report the first error but still close the stream */
- if (res)
+ if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
{
- res = g_output_stream_close (base_stream,
- cancellable,
- error);
+ /* report the first error but still close the stream */
+ if (res)
+ res = g_output_stream_close (base_stream, cancellable, error);
+ else
+ g_output_stream_close (base_stream, cancellable, NULL);
}
- else
+
+ return res;
+}
+
+static goffset
+g_buffered_output_stream_tell (GSeekable *seekable)
+{
+ GBufferedOutputStream *bstream;
+ GBufferedOutputStreamPrivate *priv;
+ GOutputStream *base_stream;
+ GSeekable *base_stream_seekable;
+ goffset base_offset;
+
+ bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
+ priv = bstream->priv;
+
+ base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
+ if (!G_IS_SEEKABLE (base_stream))
+ return 0;
+
+ base_stream_seekable = G_SEEKABLE (base_stream);
+
+ base_offset = g_seekable_tell (base_stream_seekable);
+ return base_offset + priv->pos;
+}
+
+static gboolean
+g_buffered_output_stream_can_seek (GSeekable *seekable)
+{
+ GOutputStream *base_stream;
+
+ base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
+ return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream));
+}
+
+static gboolean
+g_buffered_output_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GBufferedOutputStream *bstream;
+ GOutputStream *base_stream;
+ GSeekable *base_stream_seekable;
+ gboolean flushed;
+
+ bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
+
+ base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
+ if (!G_IS_SEEKABLE (base_stream))
{
- g_output_stream_close (base_stream,
- cancellable,
- NULL);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Seek not supported on base stream"));
+ return FALSE;
}
- return res;
+ base_stream_seekable = G_SEEKABLE (base_stream);
+ flushed = flush_buffer (bstream, cancellable, error);
+ if (!flushed)
+ return FALSE;
+
+ return g_seekable_seek (base_stream_seekable, offset, type, cancellable, error);
+}
+
+static gboolean
+g_buffered_output_stream_can_truncate (GSeekable *seekable)
+{
+ GOutputStream *base_stream;
+
+ base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
+ return G_IS_SEEKABLE (base_stream) && g_seekable_can_truncate (G_SEEKABLE (base_stream));
+}
+
+static gboolean
+g_buffered_output_stream_truncate (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GBufferedOutputStream *bstream;
+ GOutputStream *base_stream;
+ GSeekable *base_stream_seekable;
+ gboolean flushed;
+
+ bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
+ base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
+ if (!G_IS_SEEKABLE (base_stream))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Truncate not supported on base stream"));
+ return FALSE;
+ }
+
+ base_stream_seekable = G_SEEKABLE (base_stream);
+
+ flushed = flush_buffer (bstream, cancellable, error);
+ if (!flushed)
+ return FALSE;
+ return g_seekable_truncate (base_stream_seekable, offset, cancellable, error);
}
/* ************************** */
* and so closing and writing is just a special
* case of flushing + some addition stuff */
static void
-flush_buffer_thread (GSimpleAsyncResult *result,
- GObject *object,
- GCancellable *cancellable)
+flush_buffer_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
GBufferedOutputStream *stream;
GOutputStream *base_stream;
GError *error = NULL;
stream = G_BUFFERED_OUTPUT_STREAM (object);
- fdata = g_simple_async_result_get_op_res_gpointer (result);
+ fdata = task_data;
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
res = flush_buffer (stream, cancellable, &error);
/* if flushing the buffer didn't work don't even bother
* to flush the stream but just report that error */
if (res && fdata->flush_stream)
- {
- res = g_output_stream_flush (base_stream,
- cancellable,
- &error);
- }
+ res = g_output_stream_flush (base_stream, cancellable, &error);
if (fdata->close_stream)
{
/* if flushing the buffer or the stream returned
* an error report that first error but still try
* close the stream */
- if (res == FALSE)
+ if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
{
- g_output_stream_close (base_stream,
- cancellable,
- NULL);
- }
- else
- {
- res = g_output_stream_close (base_stream,
- cancellable,
- &error);
- }
-
+ if (res == FALSE)
+ g_output_stream_close (base_stream, cancellable, NULL);
+ else
+ res = g_output_stream_close (base_stream, cancellable, &error);
+ }
}
if (res == FALSE)
- {
- g_simple_async_result_set_from_error (result, error);
- g_error_free (error);
- }
-}
-
-typedef struct {
-
- FlushData fdata;
-
- gsize count;
- const void *buffer;
-
-} WriteData;
-
-static void
-free_write_data (gpointer data)
-{
- g_slice_free (WriteData, data);
-}
-
-static void
-g_buffered_output_stream_write_async (GOutputStream *stream,
- const void *buffer,
- gsize count,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer data)
-{
- GBufferedOutputStream *buffered_stream;
- GBufferedOutputStreamPrivate *priv;
- GSimpleAsyncResult *res;
- WriteData *wdata;
-
- buffered_stream = G_BUFFERED_OUTPUT_STREAM (stream);
- priv = buffered_stream->priv;
-
- wdata = g_slice_new (WriteData);
- wdata->count = count;
- wdata->buffer = buffer;
-
- res = g_simple_async_result_new (G_OBJECT (stream),
- callback,
- data,
- g_buffered_output_stream_write_async);
-
- g_simple_async_result_set_op_res_gpointer (res, wdata, free_write_data);
-
- /* if we have space left directly call the
- * callback (from idle) otherwise schedule a buffer
- * flush in the thread. In both cases the actual
- * copying of the data to the buffer will be done in
- * the write_finish () func since that should
- * be fast enough */
- if (priv->len - priv->pos > 0)
- {
- g_simple_async_result_complete_in_idle (res);
- }
+ g_task_return_error (task, error);
else
- {
- wdata->fdata.flush_stream = FALSE;
- wdata->fdata.close_stream = FALSE;
- g_simple_async_result_run_in_thread (res,
- flush_buffer_thread,
- io_priority,
- cancellable);
- g_object_unref (res);
- }
-}
-
-static gssize
-g_buffered_output_stream_write_finish (GOutputStream *stream,
- GAsyncResult *result,
- GError **error)
-{
- GBufferedOutputStreamPrivate *priv;
- GBufferedOutputStream *buffered_stream;
- GSimpleAsyncResult *simple;
- WriteData *wdata;
- gssize count;
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
- buffered_stream = G_BUFFERED_OUTPUT_STREAM (stream);
- priv = buffered_stream->priv;
-
- g_assert (g_simple_async_result_get_source_tag (simple) ==
- g_buffered_output_stream_write_async);
-
- wdata = g_simple_async_result_get_op_res_gpointer (simple);
-
- /* Now do the real copying of data to the buffer */
- count = priv->len - priv->pos;
- count = MIN (wdata->count, count);
-
- memcpy (priv->buffer + priv->pos, wdata->buffer, count);
-
- priv->pos += count;
-
- return count;
+ g_task_return_boolean (task, TRUE);
}
static void
GAsyncReadyCallback callback,
gpointer data)
{
- GSimpleAsyncResult *res;
- FlushData *fdata;
+ GTask *task;
+ FlushData *fdata;
fdata = g_slice_new (FlushData);
fdata->flush_stream = TRUE;
fdata->close_stream = FALSE;
- res = g_simple_async_result_new (G_OBJECT (stream),
- callback,
- data,
- g_buffered_output_stream_flush_async);
-
- g_simple_async_result_set_op_res_gpointer (res, fdata, free_flush_data);
+ task = g_task_new (stream, cancellable, callback, data);
+ g_task_set_task_data (task, fdata, free_flush_data);
+ g_task_set_priority (task, io_priority);
- g_simple_async_result_run_in_thread (res,
- flush_buffer_thread,
- io_priority,
- cancellable);
- g_object_unref (res);
+ g_task_run_in_thread (task, flush_buffer_thread);
+ g_object_unref (task);
}
static gboolean
GAsyncResult *result,
GError **error)
{
- GSimpleAsyncResult *simple;
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
-
- g_assert (g_simple_async_result_get_source_tag (simple) ==
- g_buffered_output_stream_flush_async);
+ g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
GAsyncReadyCallback callback,
gpointer data)
{
- GSimpleAsyncResult *res;
- FlushData *fdata;
+ GTask *task;
+ FlushData *fdata;
fdata = g_slice_new (FlushData);
fdata->close_stream = TRUE;
- res = g_simple_async_result_new (G_OBJECT (stream),
- callback,
- data,
- g_buffered_output_stream_close_async);
+ task = g_task_new (stream, cancellable, callback, data);
+ g_task_set_task_data (task, fdata, free_flush_data);
+ g_task_set_priority (task, io_priority);
- g_simple_async_result_set_op_res_gpointer (res, fdata, free_flush_data);
-
- g_simple_async_result_run_in_thread (res,
- flush_buffer_thread,
- io_priority,
- cancellable);
- g_object_unref (res);
+ g_task_run_in_thread (task, flush_buffer_thread);
+ g_object_unref (task);
}
static gboolean
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_assert (g_simple_async_result_get_source_tag (simple) ==
- g_buffered_output_stream_flush_async);
-
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (result), error);
}
-
-/* vim: ts=2 sw=2 et */