aboutsummaryrefslogtreecommitdiffstats
path: root/e-util
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2013-04-24 03:06:01 +0800
committerMatthew Barnes <mbarnes@redhat.com>2013-04-24 08:06:01 +0800
commitc64602b39797e4d1ffdc23766b2caaf5a0be000b (patch)
tree93ee3eced216fe28437ec54eeab23ea0396ff746 /e-util
parentea7e2ab44b9c8d03c7cee3569becb3dcf15e8bb1 (diff)
downloadgsoc2013-evolution-c64602b39797e4d1ffdc23766b2caaf5a0be000b.tar.gz
gsoc2013-evolution-c64602b39797e4d1ffdc23766b2caaf5a0be000b.tar.zst
gsoc2013-evolution-c64602b39797e4d1ffdc23766b2caaf5a0be000b.zip
Add EDataCapture.
EDataCapture is a GConverter that captures data until the end of the input data is seen, then emits a "finished" signal with the captured data in a GBytes instance. When used with GConverterInputStream or GConverterOutputStream, an EDataCapture can discreetly capture stream content for the purpose of caching.
Diffstat (limited to 'e-util')
-rw-r--r--e-util/Makefile.am2
-rw-r--r--e-util/e-data-capture.c359
-rw-r--r--e-util/e-data-capture.h79
-rw-r--r--e-util/e-util.h1
4 files changed, 441 insertions, 0 deletions
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index 84331e24aa..e7a8f794cc 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -168,6 +168,7 @@ eutilinclude_HEADERS = \
e-client-selector.h \
e-config.h \
e-contact-store.h \
+ e-data-capture.h \
e-dateedit.h \
e-datetime-format.h \
e-destination-store.h \
@@ -415,6 +416,7 @@ libeutil_la_SOURCES = \
e-client-selector.c \
e-config.c \
e-contact-store.c \
+ e-data-capture.c \
e-dateedit.c \
e-datetime-format.c \
e-destination-store.c \
diff --git a/e-util/e-data-capture.c b/e-util/e-data-capture.c
new file mode 100644
index 0000000000..098b18ba43
--- /dev/null
+++ b/e-util/e-data-capture.c
@@ -0,0 +1,359 @@
+/*
+ * e-data-capture.c
+ *
+ * This program 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 of the License, or (at your option) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-data-capture
+ * @include: e-util/e-util.h
+ * @short_description: Capture data from streams
+ *
+ * #EDataCapture is a #GConverter that captures data until the end of
+ * the input data is seen, then emits a #EDataCapture:finished signal
+ * with the captured data in a #GBytes instance.
+ *
+ * When used with #GConverterInputStream or #GConverterOutputStream,
+ * an #EDataCapture can discreetly capture the stream content for the
+ * purpose of caching.
+ **/
+
+#include "e-data-capture.h"
+
+#include <string.h>
+
+#define E_DATA_CAPTURE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_DATA_CAPTURE, EDataCapturePrivate))
+
+typedef struct _SignalClosure SignalClosure;
+
+struct _EDataCapturePrivate {
+ GMainContext *main_context;
+ GByteArray *byte_array;
+ GMutex byte_array_lock;
+};
+
+struct _SignalClosure {
+ GWeakRef data_capture;
+ GBytes *data;
+};
+
+enum {
+ PROP_0,
+ PROP_MAIN_CONTEXT
+};
+
+enum {
+ FINISHED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+/* Forward Declarations */
+static void e_data_capture_converter_init (GConverterIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ EDataCapture,
+ e_data_capture,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (
+ G_TYPE_CONVERTER,
+ e_data_capture_converter_init))
+
+static void
+signal_closure_free (SignalClosure *signal_closure)
+{
+ g_weak_ref_set (&signal_closure->data_capture, NULL);
+ g_bytes_unref (signal_closure->data);
+
+ g_slice_free (SignalClosure, signal_closure);
+}
+
+static gboolean
+data_capture_emit_finished_idle_cb (gpointer user_data)
+{
+ SignalClosure *signal_closure = user_data;
+ EDataCapture *data_capture;
+
+ data_capture = g_weak_ref_get (&signal_closure->data_capture);
+
+ if (data_capture != NULL) {
+ g_signal_emit (
+ data_capture,
+ signals[FINISHED], 0,
+ signal_closure->data);
+ g_object_unref (data_capture);
+ }
+
+ return FALSE;
+}
+
+static void
+data_capture_set_main_context (EDataCapture *data_capture,
+ GMainContext *main_context)
+{
+ g_return_if_fail (data_capture->priv->main_context == NULL);
+
+ if (main_context != NULL)
+ g_main_context_ref (main_context);
+ else
+ main_context = g_main_context_ref_thread_default ();
+
+ data_capture->priv->main_context = main_context;
+}
+
+static void
+data_capture_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_MAIN_CONTEXT:
+ data_capture_set_main_context (
+ E_DATA_CAPTURE (object),
+ g_value_get_boxed (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+data_capture_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_MAIN_CONTEXT:
+ g_value_take_boxed (
+ value,
+ e_data_capture_ref_main_context (
+ E_DATA_CAPTURE (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+data_capture_finalize (GObject *object)
+{
+ EDataCapturePrivate *priv;
+
+ priv = E_DATA_CAPTURE_GET_PRIVATE (object);
+
+ g_main_context_unref (priv->main_context);
+
+ g_byte_array_free (priv->byte_array, TRUE);
+ g_mutex_clear (&priv->byte_array_lock);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_data_capture_parent_class)->finalize (object);
+}
+
+static GConverterResult
+data_capture_convert (GConverter *converter,
+ gconstpointer inbuf,
+ gsize inbuf_size,
+ gpointer outbuf,
+ gsize outbuf_size,
+ GConverterFlags flags,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ EDataCapture *data_capture;
+ GConverterResult result;
+
+ data_capture = E_DATA_CAPTURE (converter);
+
+ /* Output buffer needs to be at least as large as the input buffer.
+ * The error message should never make it to the user interface so
+ * no need to translate. */
+ if (outbuf_size < inbuf_size) {
+ g_set_error_literal (
+ error, G_IO_ERROR,
+ G_IO_ERROR_NO_SPACE,
+ "EDataCapture needs more space");
+ return G_CONVERTER_ERROR;
+ }
+
+ memcpy (outbuf, inbuf, inbuf_size);
+ *bytes_read = *bytes_written = inbuf_size;
+
+ g_mutex_lock (&data_capture->priv->byte_array_lock);
+
+ g_byte_array_append (
+ data_capture->priv->byte_array, inbuf, inbuf_size);
+
+ if ((flags & G_CONVERTER_INPUT_AT_END) != 0) {
+ GSource *idle_source;
+ GMainContext *main_context;
+ SignalClosure *signal_closure;
+
+ signal_closure = g_slice_new0 (SignalClosure);
+ g_weak_ref_set (&signal_closure->data_capture, data_capture);
+ signal_closure->data = g_bytes_new (
+ data_capture->priv->byte_array->data,
+ data_capture->priv->byte_array->len);
+
+ main_context = e_data_capture_ref_main_context (data_capture);
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (
+ idle_source,
+ data_capture_emit_finished_idle_cb,
+ signal_closure,
+ (GDestroyNotify) signal_closure_free);
+ g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE);
+ g_source_attach (idle_source, main_context);
+ g_source_unref (idle_source);
+
+ g_main_context_unref (main_context);
+ }
+
+ g_mutex_unlock (&data_capture->priv->byte_array_lock);
+
+ if ((flags & G_CONVERTER_INPUT_AT_END) != 0)
+ result = G_CONVERTER_FINISHED;
+ else if ((flags & G_CONVERTER_FLUSH) != 0)
+ result = G_CONVERTER_FLUSHED;
+ else
+ result = G_CONVERTER_CONVERTED;
+
+ return result;
+}
+
+static void
+data_capture_reset (GConverter *converter)
+{
+ EDataCapture *data_capture;
+
+ data_capture = E_DATA_CAPTURE (converter);
+
+ g_mutex_lock (&data_capture->priv->byte_array_lock);
+
+ g_byte_array_set_size (data_capture->priv->byte_array, 0);
+
+ g_mutex_unlock (&data_capture->priv->byte_array_lock);
+}
+
+static void
+e_data_capture_class_init (EDataCaptureClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EDataCapturePrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = data_capture_set_property;
+ object_class->get_property = data_capture_get_property;
+ object_class->finalize = data_capture_finalize;
+
+ /**
+ * EDataCapture:main-context:
+ *
+ * The #GMainContext from which to emit the #EDataCapture::finished
+ * signal.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_MAIN_CONTEXT,
+ g_param_spec_boxed (
+ "main-context",
+ "Main Context",
+ "The main loop context from "
+ "which to emit the 'finished' signal",
+ G_TYPE_MAIN_CONTEXT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EDataCapture::finished:
+ * @data_capture: the #EDataCapture that received the signal
+ * @data: the captured data
+ *
+ * The ::finished signal is emitted when there is no more input
+ * data to be captured.
+ **/
+ signals[FINISHED] = g_signal_new (
+ "finished",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EDataCaptureClass, finished),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_BYTES);
+}
+
+static void
+e_data_capture_converter_init (GConverterIface *interface)
+{
+ interface->convert = data_capture_convert;
+ interface->reset = data_capture_reset;
+}
+
+static void
+e_data_capture_init (EDataCapture *data_capture)
+{
+ data_capture->priv = E_DATA_CAPTURE_GET_PRIVATE (data_capture);
+
+ data_capture->priv->byte_array = g_byte_array_new ();
+ g_mutex_init (&data_capture->priv->byte_array_lock);
+}
+
+/**
+ * e_data_capture_new:
+ * @main_context: a #GMainContext, or %NULL
+ *
+ * Creates a new #EDataCapture. If @main_context is %NULL, then the
+ * #EDataCapture:finished signal will be emitted from the thread-default
+ * #GMainContext for this thread.
+ *
+ * Returns: an #EDataCapture
+ **/
+EDataCapture *
+e_data_capture_new (GMainContext *main_context)
+{
+ return g_object_new (
+ E_TYPE_DATA_CAPTURE,
+ "main-context", main_context, NULL);
+}
+
+/**
+ * e_data_capture_ref_main_context:
+ * @data_capture: an #EDataCapture
+ *
+ * Returns the #GMainContext from which the #EDataCapture:finished signal
+ * is emitted.
+ *
+ * The returned #GMainContext is referenced for thread-safety and must be
+ * unreferenced with g_main_context_unref() when finished with it.
+ *
+ * Returns: a #GMainContext
+ **/
+GMainContext *
+e_data_capture_ref_main_context (EDataCapture *data_capture)
+{
+ g_return_val_if_fail (E_IS_DATA_CAPTURE (data_capture), NULL);
+
+ return g_main_context_ref (data_capture->priv->main_context);
+}
+
diff --git a/e-util/e-data-capture.h b/e-util/e-data-capture.h
new file mode 100644
index 0000000000..8949e67fcf
--- /dev/null
+++ b/e-util/e-data-capture.h
@@ -0,0 +1,79 @@
+/*
+ * e-data-capture.h
+ *
+ * This program 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 of the License, or (at your option) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_DATA_CAPTURE_H
+#define E_DATA_CAPTURE_H
+
+#include <gio/gio.h>
+
+/* Standard GObject macros */
+#define E_TYPE_DATA_CAPTURE \
+ (e_data_capture_get_type ())
+#define E_DATA_CAPTURE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_DATA_CAPTURE, EDataCapture))
+#define E_DATA_CAPTURE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_DATA_CAPTURE, EDataCaptureClass))
+#define E_IS_DATA_CAPTURE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_DATA_CAPTURE))
+#define E_IS_DATA_CAPTURE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_DATA_CAPTURE))
+#define E_DATA_CAPTURE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_DATA_CAPTURE, EDataCaptureClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EDataCapture EDataCapture;
+typedef struct _EDataCaptureClass EDataCaptureClass;
+typedef struct _EDataCapturePrivate EDataCapturePrivate;
+
+/**
+ * EDataCapture:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _EDataCapture {
+ GObject parent;
+ EDataCapturePrivate *priv;
+};
+
+struct _EDataCaptureClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (*finished) (EDataCapture *capture,
+ GBytes *data);
+};
+
+GType e_data_capture_get_type (void) G_GNUC_CONST;
+EDataCapture * e_data_capture_new (GMainContext *main_context);
+GMainContext * e_data_capture_ref_main_context (EDataCapture *data_capture);
+
+G_END_DECLS
+
+#endif /* E_DATA_CAPTURE_H */
+
diff --git a/e-util/e-util.h b/e-util/e-util.h
index d60a6f0f62..3870c45c69 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -85,6 +85,7 @@
#include <e-util/e-client-selector.h>
#include <e-util/e-config.h>
#include <e-util/e-contact-store.h>
+#include <e-util/e-data-capture.h>
#include <e-util/e-dateedit.h>
#include <e-util/e-datetime-format.h>
#include <e-util/e-destination-store.h>