Make GBufferedInputStream implement GSeekable

https://bugzilla.gnome.org/show_bug.cgi?id=673034
This commit is contained in:
Maciej Piechotka 2012-03-28 14:12:44 +02:00 committed by Alexander Larsson
parent 86abe5163f
commit 90739baec0
2 changed files with 220 additions and 4 deletions

View File

@ -27,6 +27,7 @@
#include "gcancellable.h"
#include "gasyncresult.h"
#include "gsimpleasyncresult.h"
#include "gseekable.h"
#include "gioerror.h"
#include <string.h>
#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

View File

@ -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();