[glib] Make GBufferedInputStream implement GSeekable



commit 90739baec071f4bba19558a3e08a9f330f78070e
Author: Maciej Piechotka <uzytkownik2 gmail com>
Date:   Wed Mar 28 14:12:44 2012 +0200

    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 e62a3de..166bd41 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 <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 compact_buffer (GBufferedInputStream *stream);
+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);
 
-G_DEFINE_TYPE (GBufferedInputStream,
-               g_buffered_input_stream,
-               G_TYPE_FILTER_INPUT_STREAM)
+static void compact_buffer (GBufferedInputStream *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)
@@ -287,6 +305,16 @@ g_buffered_input_stream_finalize (GObject *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)
 {
   stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (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 8515351..7039367 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();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]