Make GBufferedInputStream implement GSeekable
authorMaciej Piechotka <uzytkownik2@gmail.com>
Wed, 28 Mar 2012 12:12:44 +0000 (14:12 +0200)
committerAlexander Larsson <alexl@redhat.com>
Mon, 23 Apr 2012 08:57:01 +0000 (10:57 +0200)
https://bugzilla.gnome.org/show_bug.cgi?id=673034

gio/gbufferedinputstream.c
gio/tests/buffered-input-stream.c

index e62a3de..166bd41 100644 (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 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
index 8515351..7039367 100644 (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();