[libgit2-glib] Added writing blob using output stream
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgit2-glib] Added writing blob using output stream
- Date: Sat, 29 Jun 2013 15:19:56 +0000 (UTC)
commit b528200f9cc74a0bf4d2a1f00d58626f620b2f2e
Author: Jesse van den Kieboom <jessevdk gmail com>
Date: Sat Jun 29 17:19:28 2013 +0200
Added writing blob using output stream
libgit2-glib/Makefile.am | 2 +
libgit2-glib/ggit-blob-output-stream.c | 372 ++++++++++++++++++++++++++++++++
libgit2-glib/ggit-blob-output-stream.h | 73 +++++++
libgit2-glib/ggit-repository.c | 19 ++
libgit2-glib/ggit-repository.h | 5 +
libgit2-glib/ggit-types.h | 7 +
libgit2-glib/ggit.h | 1 +
tests/repository.c | 61 ++++++
8 files changed, 540 insertions(+), 0 deletions(-)
---
diff --git a/libgit2-glib/Makefile.am b/libgit2-glib/Makefile.am
index 1c6a5e4..9cd2224 100644
--- a/libgit2-glib/Makefile.am
+++ b/libgit2-glib/Makefile.am
@@ -19,6 +19,7 @@ libgit2_glib_1_0_la_LIBADD = $(LIBGIT2_GLIB_LIBS)
INST_H_FILES = \
ggit-blob.h \
+ ggit-blob-output-stream.h \
ggit-branch.h \
ggit-clone-options.h \
ggit-commit.h \
@@ -68,6 +69,7 @@ NOINST_H_FILES = \
C_FILES = \
ggit-blob.c \
+ ggit-blob-output-stream.c \
ggit-branch.c \
ggit-clone-options.c \
ggit-commit.c \
diff --git a/libgit2-glib/ggit-blob-output-stream.c b/libgit2-glib/ggit-blob-output-stream.c
new file mode 100644
index 0000000..3d2077e
--- /dev/null
+++ b/libgit2-glib/ggit-blob-output-stream.c
@@ -0,0 +1,372 @@
+/*
+ * ggit-blob-output-stream.c
+ * This file is part of libgit2-glib
+ *
+ * Copyright (C) 2013 - Jesse van den Kieboom
+ *
+ * libgit2-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libgit2-glib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libgit2-glib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ggit-blob-output-stream.h"
+#include "ggit-repository.h"
+#include "ggit-oid.h"
+#include "ggit-error.h"
+
+#include <git2.h>
+#include <string.h>
+
+#define GGIT_BLOB_OUTPUT_STREAM_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object),
GGIT_TYPE_BLOB_OUTPUT_STREAM, GgitBlobOutputStreamPrivate))
+
+struct _GgitBlobOutputStreamPrivate
+{
+ GgitRepository *repository;
+ GThread *thread;
+
+ GMutex mutex;
+ GCond cond;
+
+ GMutex reply_mutex;
+ GCond reply_cond;
+
+ gint written;
+
+ const gchar *writebuf;
+ gsize bufsize;
+
+ gint ret;
+ GgitOId *oid;
+
+ guint closing : 1;
+ guint cancelled : 1;
+};
+
+G_DEFINE_TYPE (GgitBlobOutputStream, ggit_blob_output_stream, G_TYPE_OUTPUT_STREAM)
+
+enum
+{
+ PROP_0,
+ PROP_REPOSITORY
+};
+
+static gboolean
+ggit_blob_output_stream_close (GOutputStream *object,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GgitBlobOutputStream *stream = GGIT_BLOB_OUTPUT_STREAM (object);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ {
+ return FALSE;
+ }
+
+ g_mutex_lock (&stream->priv->mutex);
+ stream->priv->closing = TRUE;
+
+ g_cond_signal (&stream->priv->cond);
+ g_mutex_lock (&stream->priv->reply_mutex);
+ g_mutex_unlock (&stream->priv->mutex);
+ g_cond_wait (&stream->priv->reply_cond, &stream->priv->reply_mutex);
+ g_mutex_unlock (&stream->priv->reply_mutex);
+
+ g_thread_join (stream->priv->thread);
+ return TRUE;
+}
+
+static gboolean
+ggit_blob_output_stream_flush (GOutputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gssize
+ggit_blob_output_stream_write (GOutputStream *object,
+ const void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GgitBlobOutputStream *stream = GGIT_BLOB_OUTPUT_STREAM (object);
+
+ if (stream->priv->closing)
+ {
+ return 0;
+ }
+
+ while (TRUE)
+ {
+ g_mutex_lock (&stream->priv->mutex);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ {
+ stream->priv->cancelled = TRUE;
+ }
+
+ stream->priv->writebuf = buffer;
+ stream->priv->bufsize = count;
+ stream->priv->written = 0;
+
+ g_cond_signal (&stream->priv->cond);
+ g_mutex_lock (&stream->priv->reply_mutex);
+ g_mutex_unlock (&stream->priv->mutex);
+ g_cond_wait (&stream->priv->reply_cond, &stream->priv->reply_mutex);
+
+ if (stream->priv->written == count || stream->priv->cancelled)
+ {
+ g_mutex_unlock (&stream->priv->reply_mutex);
+ break;
+ }
+
+ g_mutex_unlock (&stream->priv->reply_mutex);
+ }
+
+ if (stream->priv->cancelled)
+ {
+ g_cancellable_set_error_if_cancelled (cancellable, error);
+ return -1;
+ }
+
+ return stream->priv->written;
+}
+
+static void
+ggit_blob_output_stream_finalize (GObject *object)
+{
+ GgitBlobOutputStream *stream;
+
+ stream = GGIT_BLOB_OUTPUT_STREAM (object);
+
+ G_OBJECT_CLASS (ggit_blob_output_stream_parent_class)->finalize (object);
+
+ g_mutex_clear (&stream->priv->mutex);
+ g_cond_clear (&stream->priv->cond);
+
+ g_mutex_clear (&stream->priv->reply_mutex);
+ g_cond_clear (&stream->priv->reply_cond);
+
+ if (stream->priv->oid)
+ {
+ ggit_oid_free (stream->priv->oid);
+ }
+}
+
+static void
+ggit_blob_output_stream_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GgitBlobOutputStream *stream = GGIT_BLOB_OUTPUT_STREAM (object);
+
+ switch (prop_id)
+ {
+ case PROP_REPOSITORY:
+ g_clear_object (&stream->priv->repository);
+ stream->priv->repository = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ggit_blob_output_stream_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ggit_blob_output_stream_class_init (GgitBlobOutputStreamClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+ object_class->finalize = ggit_blob_output_stream_finalize;
+
+ object_class->get_property = ggit_blob_output_stream_get_property;
+ object_class->set_property = ggit_blob_output_stream_set_property;
+
+ stream_class->write_fn = ggit_blob_output_stream_write;
+ stream_class->close_fn = ggit_blob_output_stream_close;
+ stream_class->flush = ggit_blob_output_stream_flush;
+
+ g_object_class_install_property (object_class,
+ PROP_REPOSITORY,
+ g_param_spec_object ("repository",
+ "Repository",
+ "Repository",
+ GGIT_TYPE_REPOSITORY,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+
+ g_type_class_add_private (object_class, sizeof (GgitBlobOutputStreamPrivate));
+}
+
+static int
+blob_chunk_cb (char *content,
+ size_t maxlen,
+ void *payload)
+{
+ GgitBlobOutputStream *stream = payload;
+ gint written = 0;
+
+ // block until we got something to write
+ while (written == 0)
+ {
+ g_cond_wait (&stream->priv->cond, &stream->priv->mutex);
+
+ if (stream->priv->closing)
+ {
+ return 0;
+ }
+
+ if (stream->priv->cancelled)
+ {
+ return -1;
+ }
+
+ if (stream->priv->bufsize > maxlen)
+ {
+ stream->priv->written = maxlen;
+ }
+ else
+ {
+ stream->priv->written = stream->priv->bufsize;
+ }
+
+ if (stream->priv->written > 0)
+ {
+ memcpy (content, stream->priv->writebuf, stream->priv->written);
+ written = stream->priv->written;
+ }
+
+ g_mutex_lock (&stream->priv->reply_mutex);
+ g_cond_signal (&stream->priv->reply_cond);
+ g_mutex_unlock (&stream->priv->mutex);
+ g_mutex_unlock (&stream->priv->reply_mutex);
+ }
+
+ return written;
+}
+
+static gpointer
+chunk_blob_in_thread (gpointer data)
+{
+ GgitBlobOutputStream *stream = data;
+ git_oid oid;
+ gint ret;
+
+ ret = git_blob_create_fromchunks (&oid,
+ _ggit_native_get (stream->priv->repository),
+ NULL,
+ blob_chunk_cb,
+ data);
+
+ stream->priv->ret = ret;
+ stream->priv->closing = TRUE;
+
+ if (ret == GIT_OK)
+ {
+ stream->priv->oid = _ggit_oid_wrap (&oid);
+ }
+ else
+ {
+ stream->priv->cancelled = TRUE;
+ }
+
+ g_mutex_lock (&stream->priv->reply_mutex);
+ g_cond_signal (&stream->priv->reply_cond);
+
+ g_mutex_unlock (&stream->priv->mutex);
+ g_mutex_unlock (&stream->priv->reply_mutex);
+
+ return NULL;
+}
+
+static void
+ggit_blob_output_stream_init (GgitBlobOutputStream *self)
+{
+ self->priv = GGIT_BLOB_OUTPUT_STREAM_GET_PRIVATE (self);
+
+ g_mutex_init (&self->priv->mutex);
+ g_cond_init (&self->priv->cond);
+
+ g_mutex_init (&self->priv->reply_mutex);
+ g_cond_init (&self->priv->reply_cond);
+
+ g_mutex_lock (&self->priv->mutex);
+
+ self->priv->thread = g_thread_new ("ggit-blob-output-stream",
+ chunk_blob_in_thread,
+ self);
+}
+
+GgitBlobOutputStream *
+_ggit_blob_output_stream_new (GgitRepository *repository)
+{
+ GgitBlobOutputStream *s;
+
+ s = g_object_new (GGIT_TYPE_BLOB_OUTPUT_STREAM,
+ "repository", repository,
+ NULL);
+
+ return s;
+}
+
+/**
+ * ggit_blob_output_stream_get_id:
+ * @stream: a #GgitBlobOutputStream.
+ * @error: a #GError for error reporting, or %NULL.
+ *
+ * Get the id of the written blob. The blob id is only available after the
+ * stream has been properly closed. If an error occurred while writing the blob,
+ * the %NULL is returned and @error is set accordingly.
+ *
+ * Returns: (transfer full): a #GgitOId or %NULL.
+ *
+ **/
+GgitOId *
+ggit_blob_output_stream_get_id (GgitBlobOutputStream *stream,
+ GError **error)
+{
+ g_return_val_if_fail (GGIT_IS_BLOB_OUTPUT_STREAM (stream), NULL);
+
+ if (stream->priv->ret != GIT_OK)
+ {
+ _ggit_error_set (error, stream->priv->ret);
+ return NULL;
+ }
+
+ return ggit_oid_copy (stream->priv->oid);
+}
+
+/* ex:set ts=8 noet: */
diff --git a/libgit2-glib/ggit-blob-output-stream.h b/libgit2-glib/ggit-blob-output-stream.h
new file mode 100644
index 0000000..76d85f7
--- /dev/null
+++ b/libgit2-glib/ggit-blob-output-stream.h
@@ -0,0 +1,73 @@
+/*
+ * ggit-blob-output-stream.h
+ * This file is part of libgit2-glib
+ *
+ * Copyright (C) 2013 - Jesse van den Kieboom
+ *
+ * libgit2-glib is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libgit2-glib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libgit2-glib. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GGIT_BLOB_OUTPUT_STREAM_H__
+#define __GGIT_BLOB_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "ggit-types.h"
+
+G_BEGIN_DECLS
+
+#define GGIT_TYPE_BLOB_OUTPUT_STREAM (ggit_blob_output_stream_get_type ())
+#define GGIT_BLOB_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GGIT_TYPE_BLOB_OUTPUT_STREAM, GgitBlobOutputStream))
+#define GGIT_BLOB_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GGIT_TYPE_BLOB_OUTPUT_STREAM, GgitBlobOutputStreamClass))
+#define GGIT_IS_BLOB_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GGIT_TYPE_BLOB_OUTPUT_STREAM))
+#define GGIT_IS_BLOB_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GGIT_TYPE_BLOB_OUTPUT_STREAM))
+#define GGIT_BLOB_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GGIT_TYPE_BLOB_OUTPUT_STREAM, GgitBlobOutputStreamClass))
+
+typedef struct _GgitBlobOutputStreamClass GgitBlobOutputStreamClass;
+typedef struct _GgitBlobOutputStreamPrivate GgitBlobOutputStreamPrivate;
+
+struct _GgitBlobOutputStream
+{
+ /*< private >*/
+ GOutputStream parent;
+
+ GgitBlobOutputStreamPrivate *priv;
+};
+
+/**
+ * GgitBlobOutputStreamClass:
+ * @parent_class: The parent class.
+ *
+ * The class structure for #GgitBlobOutputStreamClass.
+ */
+struct _GgitBlobOutputStreamClass
+{
+ /*< private >*/
+ GOutputStreamClass parent_class;
+};
+
+GType ggit_blob_output_stream_get_type (void) G_GNUC_CONST;
+
+GgitBlobOutputStream *_ggit_blob_output_stream_new (GgitRepository *repository);
+
+GgitOId *ggit_blob_output_stream_get_id (GgitBlobOutputStream *stream,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GGIT_BLOB_OUTPUT_STREAM_H__ */
+
+/* ex:set ts=8 noet: */
diff --git a/libgit2-glib/ggit-repository.c b/libgit2-glib/ggit-repository.c
index 45d6627..bc29255 100644
--- a/libgit2-glib/ggit-repository.c
+++ b/libgit2-glib/ggit-repository.c
@@ -1897,6 +1897,25 @@ ggit_repository_get_ahead_behind (GgitRepository *repository,
}
/**
+ * ggit_repository_create_blob:
+ * @repository: a #GgitRepository.
+ *
+ * Create a new blob and return a #GOutputStream to write contents to the blob.
+ * This is an efficient way to create new blobs without copying data. The
+ * blob id can be obtained from the blob output stream using
+ * #ggit_blob_output_stream_get_id, after you close the stream.
+ *
+ * Returns: (transfer full): a #GgitBlobOutputStream.
+ *
+ **/
+GgitBlobOutputStream *
+ggit_repository_create_blob (GgitRepository *repository)
+{
+ g_return_val_if_fail (GGIT_IS_REPOSITORY (repository), NULL);
+ return _ggit_blob_output_stream_new (repository);
+}
+
+/**
* ggit_repository_create_blob_from_buffer:
* @repository: a #GgitRepository.
* @buffer: (array length=size) (element-type guint8): the data.
diff --git a/libgit2-glib/ggit-repository.h b/libgit2-glib/ggit-repository.h
index b7ca515..8e5df44 100644
--- a/libgit2-glib/ggit-repository.h
+++ b/libgit2-glib/ggit-repository.h
@@ -32,6 +32,7 @@
#include <libgit2-glib/ggit-object.h>
#include <libgit2-glib/ggit-tree.h>
#include <libgit2-glib/ggit-branch.h>
+#include <libgit2-glib/ggit-blob-output-stream.h>
G_BEGIN_DECLS
@@ -104,6 +105,10 @@ GgitRef *ggit_repository_create_symbolic_reference
const gchar *target,
GError **error);
+GgitBlobOutputStream *
+ ggit_repository_create_blob (GgitRepository *repository);
+
+
GgitOId *ggit_repository_create_blob_from_buffer (
GgitRepository *repository,
gconstpointer buffer,
diff --git a/libgit2-glib/ggit-types.h b/libgit2-glib/ggit-types.h
index 4a85136..9fc98a3 100644
--- a/libgit2-glib/ggit-types.h
+++ b/libgit2-glib/ggit-types.h
@@ -33,6 +33,13 @@ G_BEGIN_DECLS
typedef struct _GgitBlob GgitBlob;
/**
+ * GgitBlobOutputStream:
+ *
+ * Represents a blob stream object.
+ */
+typedef struct _GgitBlobOutputStream GgitBlobOutputStream;
+
+/**
* GgitBranch:
*
* Represents a branch object.
diff --git a/libgit2-glib/ggit.h b/libgit2-glib/ggit.h
index 8f6fd52..7bb16fd 100644
--- a/libgit2-glib/ggit.h
+++ b/libgit2-glib/ggit.h
@@ -22,6 +22,7 @@
#define __GGIT_H__
#include <libgit2-glib/ggit-blob.h>
+#include <libgit2-glib/ggit-blob-output-stream.h>
#include <libgit2-glib/ggit-branch.h>
#include <libgit2-glib/ggit-clone-options.h>
#include <libgit2-glib/ggit-commit.h>
diff --git a/tests/repository.c b/tests/repository.c
index 630f0b0..9e01abc 100644
--- a/tests/repository.c
+++ b/tests/repository.c
@@ -200,6 +200,66 @@ test_repository_init_bare (const gchar *git_dir)
do_test_init (git_dir, TRUE);
}
+static void
+test_repository_blob_stream (const gchar *git_dir)
+{
+ GFile *f;
+ GgitRepository *repo;
+ GError *err = NULL;
+ GgitBlobOutputStream *stream;
+ GgitOId *oid;
+ const gchar *msg = "hello world\n";
+ gsize written;
+ gsize msglen;
+ GgitBlob *blob;
+ gsize rl;
+ const guchar *content;
+
+ f = g_file_new_for_path (git_dir);
+ repo = ggit_repository_init_repository (f, FALSE, &err);
+ g_object_unref (f);
+
+ g_assert_no_error (err);
+
+ stream = ggit_repository_create_blob (repo);
+
+ msglen = strlen (msg);
+
+ written = g_output_stream_write (G_OUTPUT_STREAM (stream),
+ msg,
+ msglen,
+ NULL,
+ &err);
+
+ g_assert_no_error (err);
+ g_assert_cmpint (written, ==, msglen);
+
+ g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &err);
+
+ g_assert_no_error (err);
+ oid = ggit_blob_output_stream_get_id (stream, &err);
+
+ g_assert_no_error (err);
+ g_assert (oid);
+
+ blob = (GgitBlob *)ggit_repository_lookup (repo,
+ oid,
+ GGIT_TYPE_BLOB,
+ &err);
+
+ g_assert_no_error (err);
+ g_assert (blob);
+
+ content = ggit_blob_get_raw_content (blob, &rl);
+ g_assert_cmpint (rl, ==, msglen);
+ g_assert_cmpint (memcmp (content, msg, msglen), ==, 0);
+
+ ggit_oid_free (oid);
+
+ g_object_unref (stream);
+ g_object_unref (repo);
+}
+
int
main (int argc,
char **argv)
@@ -215,6 +275,7 @@ main (int argc,
TEST ("init", init);
TEST ("init-bare", init_bare);
+ TEST ("blob-stream", blob_stream);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]