[glib] Make GBufferedOutputStream implement GSeekable



commit 43895e3089ec1ac7af2f77530fe91678b58a3501
Author: Maciej Piechotka <uzytkownik2 gmail com>
Date:   Thu Mar 29 01:50:41 2012 +0200

    Make GBufferedOutputStream implement GSeekable
    
    https://bugzilla.gnome.org/show_bug.cgi?id=673034

 gio/gbufferedoutputstream.c        |  132 ++++++++++++++++++++++++-
 gio/tests/buffered-output-stream.c |  195 ++++++++++++++++++++++++++++++++++++
 2 files changed, 324 insertions(+), 3 deletions(-)
---
diff --git a/gio/gbufferedoutputstream.c b/gio/gbufferedoutputstream.c
index f624d25..f52ad2f 100644
--- a/gio/gbufferedoutputstream.c
+++ b/gio/gbufferedoutputstream.c
@@ -23,8 +23,10 @@
 #include "config.h"
 #include "gbufferedoutputstream.h"
 #include "goutputstream.h"
+#include "gseekable.h"
 #include "gsimpleasyncresult.h"
 #include "string.h"
+#include "gioerror.h"
 #include "glibintl.h"
 
 /**
@@ -105,9 +107,25 @@ static gboolean g_buffered_output_stream_close_finish (GOutputStream        *str
                                                        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_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+						g_buffered_output_stream_seekable_iface_init))
 
 
 static void
@@ -333,6 +351,16 @@ g_buffered_output_stream_init (GBufferedOutputStream *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;
+}
+
 /**
  * g_buffered_output_stream_new:
  * @base_stream: a #GOutputStream.
@@ -501,6 +529,104 @@ g_buffered_output_stream_close (GOutputStream  *stream,
   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_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);
+  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);
+}
+
 /* ************************** */
 /* Async stuff implementation */
 /* ************************** */
diff --git a/gio/tests/buffered-output-stream.c b/gio/tests/buffered-output-stream.c
index bb6159c..d20f319 100644
--- a/gio/tests/buffered-output-stream.c
+++ b/gio/tests/buffered-output-stream.c
@@ -111,6 +111,199 @@ test_close (void)
   g_object_unref (base);
 }
 
+static void
+test_seek (void)
+{
+  GMemoryOutputStream *base;
+  GOutputStream *out;
+  GSeekable *seekable;
+  GError *error;
+  gsize bytes_written;
+  gboolean ret;
+  const gchar buffer[] = "abcdefghijklmnopqrstuvwxyz";
+
+  base = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (g_malloc0 (30), 30, g_realloc, g_free));
+  out = g_buffered_output_stream_new_sized (G_OUTPUT_STREAM (base), 8);
+  seekable = G_SEEKABLE (out);
+  error = NULL;
+
+  /* Write data */
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 0);
+  ret = g_output_stream_write_all (out, buffer, 4, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (bytes_written, ==, 4);
+  g_assert (ret);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 4);
+  g_assert_cmpint (g_memory_output_stream_get_data_size (base), ==, 0);
+
+  /* Forward relative seek */
+  ret = g_seekable_seek (seekable, 2, G_SEEK_CUR, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 6);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[0]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[1]);
+  g_assert_cmpint ('c', ==, ((gchar *)g_memory_output_stream_get_data (base))[2]);
+  g_assert_cmpint ('d', ==, ((gchar *)g_memory_output_stream_get_data (base))[3]);
+  ret = g_output_stream_write_all (out, buffer, 2, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (bytes_written, ==, 2);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 8);
+
+  /* Backward relative seek */
+  ret = g_seekable_seek (seekable, -4, G_SEEK_CUR, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 4);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[0]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[1]);
+  g_assert_cmpint ('c', ==, ((gchar *)g_memory_output_stream_get_data (base))[2]);
+  g_assert_cmpint ('d', ==, ((gchar *)g_memory_output_stream_get_data (base))[3]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[6]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[7]);
+  ret = g_output_stream_write_all (out, buffer, 2, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (bytes_written, ==, 2);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 6);
+
+  /* From start */
+  ret = g_seekable_seek (seekable, 2, G_SEEK_SET, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 2);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[0]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[1]);
+  g_assert_cmpint ('c', ==, ((gchar *)g_memory_output_stream_get_data (base))[2]);
+  g_assert_cmpint ('d', ==, ((gchar *)g_memory_output_stream_get_data (base))[3]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[4]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[5]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[6]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[7]);
+  ret = g_output_stream_write_all (out, buffer, 2, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (bytes_written, ==, 2);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 4);
+
+  /* From end */
+  ret = g_seekable_seek (seekable, 6 - 30, G_SEEK_END, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 6);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[0]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[1]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[2]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[3]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[4]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[5]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[6]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[7]);
+  ret = g_output_stream_write_all (out, buffer + 2, 2, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (bytes_written, ==, 2);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 8);
+
+  /* Check flush */
+  ret = g_output_stream_flush (out, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (g_seekable_tell (G_SEEKABLE (out)), ==, 8);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[0]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[1]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[2]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[3]);
+  g_assert_cmpint ('a', ==, ((gchar *)g_memory_output_stream_get_data (base))[4]);
+  g_assert_cmpint ('b', ==, ((gchar *)g_memory_output_stream_get_data (base))[5]);
+  g_assert_cmpint ('c', ==, ((gchar *)g_memory_output_stream_get_data (base))[6]);
+  g_assert_cmpint ('d', ==, ((gchar *)g_memory_output_stream_get_data (base))[7]);
+
+  g_object_unref (out);
+  g_object_unref (base);
+}
+
+static void
+test_truncate(void)
+{
+  GMemoryOutputStream *base_stream;
+  GOutputStream *stream;
+  GSeekable *seekable;
+  GError *error;
+  gsize bytes_written;
+  guchar *stream_data;
+  gsize len;
+  gboolean res;
+
+  len = 8;
+
+  /* Create objects */
+  stream_data = g_malloc0 (len);
+  base_stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (stream_data, len, g_realloc, g_free));
+  stream = g_buffered_output_stream_new_sized (G_OUTPUT_STREAM (base_stream), 8);
+  seekable = G_SEEKABLE (stream);
+
+  g_assert (g_seekable_can_truncate (seekable));
+
+  /* Write */
+  g_assert_cmpint (g_memory_output_stream_get_size (base_stream), ==, len);
+  g_assert_cmpint (g_memory_output_stream_get_data_size (base_stream), ==, 0);
+
+  error = NULL;
+  res = g_output_stream_write_all (stream, "ab", 2, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (res);
+  res = g_output_stream_write_all (stream, "cd", 2, &bytes_written, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (res);
+
+  res = g_output_stream_flush (stream, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (res);
+
+  g_assert_cmpint (g_memory_output_stream_get_size (base_stream), ==, len);
+  g_assert_cmpint (g_memory_output_stream_get_data_size (base_stream), ==, 4);
+  g_assert_cmpint (stream_data[0], ==, 'a');
+  g_assert_cmpint (stream_data[1], ==, 'b');
+  g_assert_cmpint (stream_data[2], ==, 'c');
+  g_assert_cmpint (stream_data[3], ==, 'd');
+
+  /* Truncate at position */
+  res = g_seekable_truncate (seekable, 4, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (res);
+  g_assert_cmpint (g_memory_output_stream_get_size (base_stream), ==, 4);
+  g_assert_cmpint (g_memory_output_stream_get_data_size (base_stream), ==, 4);
+  g_assert_cmpint (stream_data[0], ==, 'a');
+  g_assert_cmpint (stream_data[1], ==, 'b');
+  g_assert_cmpint (stream_data[2], ==, 'c');
+  g_assert_cmpint (stream_data[3], ==, 'd');
+
+  /* Truncate beyond position */
+  res = g_seekable_truncate (seekable, 6, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (res);
+  g_assert_cmpint (g_memory_output_stream_get_size (base_stream), ==, 6);
+  g_assert_cmpint (g_memory_output_stream_get_data_size (base_stream), ==, 4);
+  g_assert_cmpint (stream_data[0], ==, 'a');
+  g_assert_cmpint (stream_data[1], ==, 'b');
+  g_assert_cmpint (stream_data[2], ==, 'c');
+  g_assert_cmpint (stream_data[3], ==, 'd');
+
+  /* Truncate before position */
+  res = g_seekable_truncate (seekable, 2, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (res);
+  g_assert_cmpint (g_memory_output_stream_get_size (base_stream), ==, 2);
+  g_assert_cmpint (g_memory_output_stream_get_data_size (base_stream), ==, 2);
+  g_assert_cmpint (stream_data[0], ==, 'a');
+  g_assert_cmpint (stream_data[1], ==, 'b');
+
+  g_object_unref (stream);
+  g_object_unref (base_stream);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -120,6 +313,8 @@ main (int argc, char *argv[])
 
   g_test_add_func ("/buffered-output-stream/write", test_write);
   g_test_add_func ("/buffered-output-stream/grow", test_grow);
+  g_test_add_func ("/buffered-output-stream/seek", test_seek);
+  g_test_add_func ("/buffered-output-stream/truncate", test_truncate);
   g_test_add_func ("/filter-output-stream/close", test_close);
 
   return g_test_run ();



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