From c64602b39797e4d1ffdc23766b2caaf5a0be000b Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 23 Apr 2013 15:06:01 -0400 Subject: 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. --- e-util/Makefile.am | 2 + e-util/e-data-capture.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++++ e-util/e-data-capture.h | 79 +++++++++++ e-util/e-util.h | 1 + 4 files changed, 441 insertions(+) create mode 100644 e-util/e-data-capture.c create mode 100644 e-util/e-data-capture.h (limited to 'e-util') 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 + * + */ + +/** + * 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 + +#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 + * + */ + +#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION) +#error "Only should be included directly." +#endif + +#ifndef E_DATA_CAPTURE_H +#define E_DATA_CAPTURE_H + +#include + +/* 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 #include #include +#include #include #include #include -- cgit v1.2.3