From 90739baec071f4bba19558a3e08a9f330f78070e Mon Sep 17 00:00:00 2001 From: Maciej Piechotka Date: Wed, 28 Mar 2012 14:12:44 +0200 Subject: [PATCH] Make GBufferedInputStream implement GSeekable https://bugzilla.gnome.org/show_bug.cgi?id=673034 --- gio/gbufferedinputstream.c | 138 +++++++++++++++++++++++++++++- gio/tests/buffered-input-stream.c | 86 +++++++++++++++++++ 2 files changed, 220 insertions(+), 4 deletions(-) diff --git a/gio/gbufferedinputstream.c b/gio/gbufferedinputstream.c index e62a3dedf..166bd414f 100644 --- a/gio/gbufferedinputstream.c +++ b/gio/gbufferedinputstream.c @@ -27,6 +27,7 @@ #include "gcancellable.h" #include "gasyncresult.h" #include "gsimpleasyncresult.h" +#include "gseekable.h" #include "gioerror.h" #include #include "glibintl.h" @@ -114,12 +115,29 @@ static gssize g_buffered_input_stream_real_fill_finish (GBufferedInputStream *s GAsyncResult *result, GError **error); +static void g_buffered_input_stream_seekable_iface_init (GSeekableIface *iface); +static goffset g_buffered_input_stream_tell (GSeekable *seekable); +static gboolean g_buffered_input_stream_can_seek (GSeekable *seekable); +static gboolean g_buffered_input_stream_seek (GSeekable *seekable, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error); +static gboolean g_buffered_input_stream_can_truncate (GSeekable *seekable); +static gboolean g_buffered_input_stream_truncate (GSeekable *seekable, + goffset offset, + GCancellable *cancellable, + GError **error); + +static void g_buffered_input_stream_finalize (GObject *object); + static void compact_buffer (GBufferedInputStream *stream); -G_DEFINE_TYPE (GBufferedInputStream, - g_buffered_input_stream, - G_TYPE_FILTER_INPUT_STREAM) - +G_DEFINE_TYPE_WITH_CODE (GBufferedInputStream, + g_buffered_input_stream, + G_TYPE_FILTER_INPUT_STREAM, + G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, + g_buffered_input_stream_seekable_iface_init)) static void g_buffered_input_stream_class_init (GBufferedInputStreamClass *klass) @@ -286,6 +304,16 @@ g_buffered_input_stream_finalize (GObject *object) G_OBJECT_CLASS (g_buffered_input_stream_parent_class)->finalize (object); } +static void +g_buffered_input_stream_seekable_iface_init (GSeekableIface *iface) +{ + iface->tell = g_buffered_input_stream_tell; + iface->can_seek = g_buffered_input_stream_can_seek; + iface->seek = g_buffered_input_stream_seek; + iface->can_truncate = g_buffered_input_stream_can_truncate; + iface->truncate_fn = g_buffered_input_stream_truncate; +} + static void g_buffered_input_stream_init (GBufferedInputStream *stream) { @@ -826,6 +854,108 @@ g_buffered_input_stream_read (GInputStream *stream, return bytes_read; } +static goffset +g_buffered_input_stream_tell (GSeekable *seekable) +{ + GBufferedInputStream *bstream; + GBufferedInputStreamPrivate *priv; + GInputStream *base_stream; + GSeekable *base_stream_seekable; + gsize available; + goffset base_offset; + + bstream = G_BUFFERED_INPUT_STREAM (seekable); + priv = bstream->priv; + + base_stream = G_FILTER_INPUT_STREAM (seekable)->base_stream; + if (!G_IS_SEEKABLE (base_stream)) + return 0; + base_stream_seekable = G_SEEKABLE (base_stream); + + available = priv->end - priv->pos; + base_offset = g_seekable_tell (base_stream_seekable); + + return base_offset - available; +} + +static gboolean +g_buffered_input_stream_can_seek (GSeekable *seekable) +{ + GInputStream *base_stream; + + base_stream = G_FILTER_INPUT_STREAM (seekable)->base_stream; + return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream)); +} + +static gboolean +g_buffered_input_stream_seek (GSeekable *seekable, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error) +{ + GBufferedInputStream *bstream; + GBufferedInputStreamPrivate *priv; + GInputStream *base_stream; + GSeekable *base_stream_seekable; + + bstream = G_BUFFERED_INPUT_STREAM (seekable); + priv = bstream->priv; + + base_stream = G_FILTER_INPUT_STREAM (seekable)->base_stream; + if (!G_IS_SEEKABLE (base_stream)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Seek not supported on base stream")); + return FALSE; + } + + base_stream_seekable = G_SEEKABLE (base_stream); + + if (type == G_SEEK_CUR) + { + if (offset <= priv->end - priv->pos && offset >= -priv->pos) + { + priv->pos += offset; + return TRUE; + } + else + { + offset -= priv->end - priv->pos; + } + } + + if (g_seekable_seek (base_stream_seekable, offset, type, cancellable, error)) + { + priv->pos = 0; + priv->end = 0; + return TRUE; + } + else + { + return FALSE; + } +} + +static gboolean +g_buffered_input_stream_can_truncate (GSeekable *seekable) +{ + return FALSE; +} + +static gboolean +g_buffered_input_stream_truncate (GSeekable *seekable, + goffset offset, + GCancellable *cancellable, + GError **error) +{ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("Cannot truncate GBufferedInputStream")); + return FALSE; +} + /** * g_buffered_input_stream_read_byte: * @stream: a #GBufferedInputStream diff --git a/gio/tests/buffered-input-stream.c b/gio/tests/buffered-input-stream.c index 851535179..7039367c8 100644 --- a/gio/tests/buffered-input-stream.c +++ b/gio/tests/buffered-input-stream.c @@ -283,6 +283,91 @@ test_close (void) g_object_unref (base); } +static void +test_seek (void) +{ + GInputStream *base; + GInputStream *in; + GError *error; + gint byte; + gboolean ret; + + base = g_memory_input_stream_new_from_data ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ", -1, NULL); + in = g_buffered_input_stream_new_sized (base, 4); + error = NULL; + + /* Seek by read */ + g_assert_cmpstr (g_seekable_tell (G_SEEKABLE (in)), ==, 0); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'a'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 1); + + /* Seek forward (in buffer) */ + ret = g_seekable_seek (G_SEEKABLE (in), 1, G_SEEK_CUR, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 2); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'c'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 3); + + /* Seek backward (in buffer) */ + ret = g_seekable_seek (G_SEEKABLE (in), -2, G_SEEK_CUR, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 1); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'b'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 2); + + /* Seek forward (outside buffer) */ + ret = g_seekable_seek (G_SEEKABLE (in), 6, G_SEEK_CUR, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 8); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'i'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 9); + + /* Seek backward (outside buffer) */ + ret = g_seekable_seek (G_SEEKABLE (in), -6, G_SEEK_CUR, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 3); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'd'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 4); + + /* Seek from beginning */ + ret = g_seekable_seek (G_SEEKABLE (in), 8, G_SEEK_SET, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 8); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'i'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 9); + + /* Seek from end */ + ret = g_seekable_seek (G_SEEKABLE (in), -1, G_SEEK_END, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 50); + byte = g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (byte, ==, 'Z'); + g_assert_cmpint (g_seekable_tell (G_SEEKABLE (in)), ==, 51); + + /* Cleanup */ + g_object_unref (in); + g_object_unref (base); +} + int main (int argc, char *argv[]) @@ -297,6 +382,7 @@ main (int argc, g_test_add_func ("/buffered-input-stream/read-byte", test_read_byte); g_test_add_func ("/buffered-input-stream/read", test_read); g_test_add_func ("/buffered-input-stream/skip", test_skip); + g_test_add_func ("/buffered-input-stream/seek", test_seek); g_test_add_func ("/filter-input-stream/close", test_close); return g_test_run();