registry: use binary format instead of XML for cache
authorDaiki Ueno <ueno@unixuser.org>
Fri, 12 Jul 2013 09:39:54 +0000 (18:39 +0900)
committerDaiki Ueno <ueno@unixuser.org>
Fri, 12 Jul 2013 09:39:54 +0000 (18:39 +0900)
Currently the registry cache is in the XML format, that may cause
unnecessary parsing overhead on ibus-daemon startup.  This patch
changes the format to GVariant based binary representation.

BUG=
R=takao.fujiwara1@gmail.com

Review URL: https://codereview.appspot.com/9963043

src/ibusregistry.c
src/ibusregistry.h
src/ibusshare.c
tools/main.vala

index d239955..c76fae7 100644 (file)
@@ -28,6 +28,9 @@
 #include "ibusmarshalers.h"
 #include "ibusregistry.h"
 
+#define IBUS_CACHE_MAGIC 0x49425553 /* "IBUS" */
+#define IBUS_CACHE_VERSION 0x00010502
+
 enum {
     CHANGED,
     LAST_SIGNAL,
@@ -57,17 +60,30 @@ struct _IBusRegistryPrivate {
 /* functions prototype */
 static void     ibus_registry_destroy        (IBusRegistry           *registry);
 static void     ibus_registry_remove_all     (IBusRegistry           *registry);
+static gboolean ibus_registry_serialize      (IBusRegistry           *registry,
+                                              GVariantBuilder        *builder);
+static gint     ibus_registry_deserialize    (IBusRegistry           *registry,
+                                              GVariant               *variant);
+static gboolean ibus_registry_copy           (IBusRegistry           *dest,
+                                              const IBusRegistry     *src);
 
-G_DEFINE_TYPE (IBusRegistry, ibus_registry, IBUS_TYPE_OBJECT)
+G_DEFINE_TYPE (IBusRegistry, ibus_registry, IBUS_TYPE_SERIALIZABLE)
 
 static void
 ibus_registry_class_init (IBusRegistryClass *class)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
+    IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
 
     ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_registry_destroy;
 
+    serializable_class->serialize =
+        (IBusSerializableSerializeFunc) ibus_registry_serialize;
+    serializable_class->deserialize =
+        (IBusSerializableDeserializeFunc) ibus_registry_deserialize;
+    serializable_class->copy = (IBusSerializableCopyFunc) ibus_registry_copy;
+
     g_type_class_add_private (class, sizeof (IBusRegistryPrivate));
 
     /* install signals */
@@ -124,6 +140,91 @@ ibus_registry_destroy (IBusRegistry *registry)
             destroy (IBUS_OBJECT (registry));
 }
 
+static gboolean
+ibus_registry_serialize (IBusRegistry    *registry,
+                         GVariantBuilder *builder)
+{
+    gboolean retval;
+
+    retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
+        serialize ((IBusSerializable *)registry, builder);
+    g_return_val_if_fail (retval, FALSE);
+
+    GList *p;
+    GVariantBuilder *array;
+
+    array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
+    for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
+        IBusSerializable *serializable = (IBusSerializable *) p->data;
+        g_variant_builder_add (array,
+                               "v",
+                               ibus_serializable_serialize (serializable));
+    }
+    g_variant_builder_add (builder, "av", array);
+    g_variant_builder_unref (array);
+
+    array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
+    for (p = registry->priv->components; p != NULL; p = p->next) {
+        IBusSerializable *serializable = (IBusSerializable *) p->data;
+        g_variant_builder_add (array,
+                               "v",
+                               ibus_serializable_serialize (serializable));
+    }
+    g_variant_builder_add (builder, "av", array);
+    g_variant_builder_unref (array);
+
+    return TRUE;
+}
+
+static gint
+ibus_registry_deserialize (IBusRegistry *registry,
+                           GVariant     *variant)
+{
+    GVariant *var;
+    GVariantIter *iter;
+    gint retval;
+
+    retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
+        deserialize ((IBusSerializable *)registry, variant);
+    g_return_val_if_fail (retval, 0);
+
+    g_variant_get_child (variant, retval++, "av", &iter);
+    while (g_variant_iter_loop (iter, "v", &var)) {
+        IBusSerializable *serializable = ibus_serializable_deserialize (var);
+        registry->priv->observed_paths =
+            g_list_append (registry->priv->observed_paths,
+                           IBUS_OBSERVED_PATH (serializable));
+    }
+    g_variant_iter_free (iter);
+
+    g_variant_get_child (variant, retval++, "av", &iter);
+    while (g_variant_iter_loop (iter, "v", &var)) {
+        IBusSerializable *serializable = ibus_serializable_deserialize (var);
+        registry->priv->components =
+            g_list_append (registry->priv->components,
+                           IBUS_COMPONENT (serializable));
+    }
+    g_variant_iter_free (iter);
+
+    return retval;
+}
+
+static gboolean
+ibus_registry_copy (IBusRegistry       *dest,
+                    const IBusRegistry *src)
+{
+    gboolean retval;
+
+    retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
+        copy ((IBusSerializable *)dest, (IBusSerializable *)src);
+    g_return_val_if_fail (retval, FALSE);
+
+    dest->priv->components = g_list_copy (src->priv->components);
+    dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
+
+    return TRUE;
+}
+
 /**
  * ibus_registry_remove_all:
  *
@@ -182,7 +283,8 @@ ibus_registry_load (IBusRegistry *registry)
 }
 
 gboolean
-ibus_registry_load_cache (IBusRegistry *registry, gboolean is_user)
+ibus_registry_load_cache (IBusRegistry *registry,
+                          gboolean      is_user)
 {
     gchar *filename;
     gboolean retval;
@@ -191,10 +293,10 @@ ibus_registry_load_cache (IBusRegistry *registry, gboolean is_user)
 
     if (is_user) {
         filename = g_build_filename (g_get_user_cache_dir (),
-                                     "ibus", "bus", "registry.xml", NULL);
+                                     "ibus", "bus", "registry", NULL);
     } else {
         filename = g_build_filename (IBUS_CACHE_DIR,
-                                     "bus", "registry.xml", NULL);
+                                     "bus", "registry", NULL);
     }
 
     retval = ibus_registry_load_cache_file (registry, filename);
@@ -204,60 +306,63 @@ ibus_registry_load_cache (IBusRegistry *registry, gboolean is_user)
 }
 
 gboolean
-ibus_registry_load_cache_file (IBusRegistry *registry, const gchar *filename)
+ibus_registry_load_cache_file (IBusRegistry *registry,
+                               const gchar  *filename)
 {
-    XMLNode *node;
-    GList *p;
+    gchar *contents, *p;
+    gsize length;
+    GVariant *variant;
+    GError *error;
 
     g_assert (IBUS_IS_REGISTRY (registry));
     g_assert (filename != NULL);
 
-    node = ibus_xml_parse_file (filename);
+    if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+        return FALSE;
 
-    if (node == NULL) {
+    error = NULL;
+    if (!g_file_get_contents (filename, &contents, &length, &error)) {
+        g_warning ("cannot read %s: %s", filename, error->message);
+        g_error_free (error);
         return FALSE;
     }
 
-    if (g_strcmp0 (node->name, "ibus-registry") != 0) {
-        ibus_xml_free (node);
+    p = contents;
+
+    /* read file header including magic and version */
+    if (length < 8) {
+        g_free (contents);
         return FALSE;
     }
 
-    for (p = node->sub_nodes; p != NULL; p = p->next) {
-        XMLNode *sub_node = (XMLNode *) p->data;
-
-        if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
-            GList *pp;
-            for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
-                IBusObservedPath *path;
-                path = ibus_observed_path_new_from_xml_node (pp->data, FALSE);
-                if (path) {
-                    g_object_ref_sink (path);
-                    registry->priv->observed_paths =
-                            g_list_append (registry->priv->observed_paths,
-                                           path);
-                }
-            }
-            continue;
-        }
-        if (g_strcmp0 (sub_node->name, "components") == 0) {
-            GList *pp;
-            for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
-                IBusComponent *component;
-                component = ibus_component_new_from_xml_node (pp->data);
-                if (component) {
-                    g_object_ref_sink (component);
-                    registry->priv->components =
-                        g_list_append (registry->priv->components, component);
-                }
-            }
+    if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_MAGIC) {
+        g_free (contents);
+        return FALSE;
+    }
+    p += 4;
 
-            continue;
-        }
-        g_warning ("Unknown element <%s>", sub_node->name);
+    if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_VERSION) {
+        g_free (contents);
+        return FALSE;
+    }
+    p += 4;
+
+    /* read serialized IBusRegistry */
+    variant = g_variant_new_from_data (G_VARIANT_TYPE ("(sa{sv}avav)"),
+                                       p,
+                                       length - (p - contents),
+                                       FALSE,
+                                       (GDestroyNotify) g_free,
+                                       NULL);
+    if (variant == NULL) {
+        g_free (contents);
+        return FALSE;
     }
 
-    ibus_xml_free (node);
+    ibus_registry_deserialize (registry, variant);
+    g_variant_unref (variant);
+    g_free (contents);
+
     return TRUE;
 }
 
@@ -283,7 +388,8 @@ ibus_registry_check_modification (IBusRegistry *registry)
 }
 
 gboolean
-ibus_registry_save_cache (IBusRegistry *registry, gboolean is_user)
+ibus_registry_save_cache (IBusRegistry *registry,
+                          gboolean      is_user)
 {
     gchar *filename;
     gboolean retval;
@@ -292,10 +398,10 @@ ibus_registry_save_cache (IBusRegistry *registry, gboolean is_user)
 
     if (is_user) {
         filename = g_build_filename (g_get_user_cache_dir (),
-                                     "ibus", "bus", "registry.xml", NULL);
+                                     "ibus", "bus", "registry", NULL);
     } else {
         filename = g_build_filename (IBUS_CACHE_DIR,
-                                     "bus", "registry.xml", NULL);
+                                     "bus", "registry", NULL);
     }
 
     retval = ibus_registry_save_cache_file (registry, filename);
@@ -305,14 +411,16 @@ ibus_registry_save_cache (IBusRegistry *registry, gboolean is_user)
 }
 
 gboolean
-ibus_registry_save_cache_file (IBusRegistry *registry, const gchar *filename)
+ibus_registry_save_cache_file (IBusRegistry *registry,
+                               const gchar  *filename)
 {
     gchar *cachedir;
-    const gchar *user_cachedir;
-    gboolean is_user = TRUE;
-    GString *output;
-    FILE *pf;
-    size_t items = 0;
+    GVariant *variant;
+    gchar *contents, *p;
+    gsize length;
+    gboolean retval;
+    guint32 intval;
+    GError *error;
 
     g_assert (IBUS_IS_REGISTRY (registry));
     g_assert (filename != NULL);
@@ -320,29 +428,40 @@ ibus_registry_save_cache_file (IBusRegistry *registry, const gchar *filename)
     cachedir = g_path_get_dirname (filename);
     g_mkdir_with_parents (cachedir, 0775);
     g_free (cachedir);
-    pf = g_fopen (filename, "w");
 
-    if (pf == NULL) {
-        g_warning ("create %s failed", filename);
-        return FALSE;
-    }
+    variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (registry));
+    length = 8 + g_variant_get_size (variant);
+    p = contents = g_slice_alloc (length);
+
+    /* write file header */
+    intval = GUINT32_TO_BE (IBUS_CACHE_MAGIC);
+    memcpy (p, (gchar *) &intval, 4);
+    p += 4;
 
-    output = g_string_new ("");
+    intval = GUINT32_TO_BE (IBUS_CACHE_VERSION);
+    memcpy (p, (gchar *) &intval, 4);
+    p += 4;
 
-    ibus_registry_output (registry, output, 1);
+    /* write serialized IBusRegistry */
+    g_variant_store (variant, p);
 
-    items = fwrite (output->str, output->len, 1, pf);
-    g_string_free (output, TRUE);
-    fclose (pf);
+    error = NULL;
+    retval = g_file_set_contents (filename, contents, length, &error);
 
-    user_cachedir = g_get_user_cache_dir ();
-    is_user = (strncmp (user_cachedir, filename, strlen (user_cachedir)) == 0);
+    g_variant_unref (variant);
+    g_slice_free1 (length, contents);
 
-    if (!is_user) {
+    if (!retval) {
+        g_warning ("cannot write %s: %s", filename, error->message);
+        g_error_free (error);
+        return FALSE;
+    }
+
+    if (g_str_has_prefix (filename, g_get_user_cache_dir ())) {
         g_chmod (filename, 0644);
     }
 
-    return (items == 1 ? TRUE : FALSE);
+    return TRUE;
 }
 
 #define g_string_append_indent(string, indent)  \
@@ -354,7 +473,9 @@ ibus_registry_save_cache_file (IBusRegistry *registry, const gchar *filename)
     }
 
 void
-ibus_registry_output (IBusRegistry *registry, GString *output, int indent)
+ibus_registry_output (IBusRegistry *registry,
+                      GString      *output,
+                      int           indent)
 {
     GList *p;
 
@@ -362,10 +483,6 @@ ibus_registry_output (IBusRegistry *registry, GString *output, int indent)
     g_return_if_fail (output != NULL);
 
     g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
-    g_string_append (output, "<!-- \n"
-                             "    This file was generated by ibus-daemon. "
-                             "Please don't modify it.\n"
-                             "    -->\n");
     g_string_append (output, "<ibus-registry>\n");
 
     if (registry->priv->observed_paths) {
index 288dc07..3369447 100644 (file)
@@ -74,14 +74,14 @@ typedef struct _IBusRegistryClass IBusRegistryClass;
 struct _IBusRegistry {
     /* instance members */
     /*< private >*/
-    IBusObject parent;
+    IBusSerializable parent;
     IBusRegistryPrivate *priv;
 };
 
 struct _IBusRegistryClass {
     /* class members */
     /*< private >*/
-    IBusObjectClass parent;
+    IBusSerializableClass parent;
 };
 
 GType            ibus_registry_get_type         (void);
index 79c2e08..20a144f 100644 (file)
@@ -285,6 +285,8 @@ ibus_init (void)
     IBUS_TYPE_LOOKUP_TABLE;
     IBUS_TYPE_COMPONENT;
     IBUS_TYPE_ENGINE_DESC;
+    IBUS_TYPE_OBSERVED_PATH;
+    IBUS_TYPE_REGISTRY;
 }
 
 static GMainLoop *main_loop = NULL;
index b358974..f3959af 100644 (file)
@@ -30,7 +30,6 @@ class EngineList {
 }
 
 IBus.Bus? get_bus() {
-    IBus.init();
     var bus = new IBus.Bus();
     if (!bus.is_connected ())
         return null;
@@ -310,6 +309,8 @@ public int main(string[] argv) {
     GLib.Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GLIB_LOCALE_DIR);
     GLib.Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
 
+    IBus.init();
+
     program_name = Path.get_basename(argv[0]);
     if (argv.length < 2) {
         print_usage(stderr);