[gtk+/wip/css-memuse: 2/3] Internalize computed css values to save memory



commit b2e36bb85b8d30c0c6d048e89688a099b4d1f861
Author: Alexander Larsson <alexl redhat com>
Date:   Wed Feb 15 13:36:15 2012 +0100

    Internalize computed css values to save memory

 gtk/gtkcsscomputedvalues.c        |  388 ++++++++++++++++++++++++++++++++++---
 gtk/gtkcsscomputedvaluesprivate.h |    2 +-
 2 files changed, 360 insertions(+), 30 deletions(-)
---
diff --git a/gtk/gtkcsscomputedvalues.c b/gtk/gtkcsscomputedvalues.c
index c5b6038..d41b240 100644
--- a/gtk/gtkcsscomputedvalues.c
+++ b/gtk/gtkcsscomputedvalues.c
@@ -20,14 +20,88 @@
 
 #include "config.h"
 
+#include <string.h>
+#include <cairo-gobject.h>
+
 #include "gtkcsscomputedvaluesprivate.h"
 
 #include "gtkcssstylepropertyprivate.h"
 #include "gtkcsstypesprivate.h"
 #include "gtkprivatetypebuiltins.h"
+#include "gtkshadowprivate.h"
+#include "gtkanimationdescription.h"
+#include "gtkcssimageprivate.h"
 
 G_DEFINE_TYPE (GtkCssComputedValues, _gtk_css_computed_values, G_TYPE_OBJECT)
 
+
+typedef GValue GtkCssValue;
+
+static GHashTable *gtk_css_values;
+
+static guint gtk_css_value_hash (GtkCssValue *css_value);
+static gboolean gtk_css_value_equal (GtkCssValue *css_value_a, GtkCssValue *css_value_b);
+
+static GtkCssValue *
+gtk_css_value_ref (GtkCssValue *v)
+{
+  v->data[1].v_int++;
+  return v;
+}
+
+static void
+gtk_css_value_unref (GtkCssValue *v)
+{
+  if (--v->data[1].v_int == 0)
+    {
+      g_hash_table_remove (gtk_css_values, v);
+      g_value_unset ((GValue *)v);
+    }
+}
+
+static GtkCssValue *
+gtk_css_value_dup_value (const GValue *v)
+{
+  GtkCssValue *new;
+
+  if (v == NULL || !G_IS_VALUE (v))
+    return NULL;
+
+  new = g_hash_table_lookup (gtk_css_values, v);
+  if (new)
+    return gtk_css_value_ref (new);
+
+  new = g_new0 (GtkCssValue, 1);
+  g_value_init ((GValue *)new, G_VALUE_TYPE (v));
+  g_value_copy (v, (GValue *)new);
+  new->data[1].v_int = 1;
+
+  g_hash_table_insert (gtk_css_values, new, new);
+
+  return new;
+}
+
+
+static GtkCssValue *
+gtk_css_value_ref_value (GValue *v)
+{
+
+  if (v == NULL || !G_IS_VALUE (v))
+    return NULL;
+
+  /* Some magic to detect if the GValue is already a GtkCssValue so we can just ref it */
+  if (v->data[1].v_int > 0)
+    return gtk_css_value_ref ((GtkCssValue *)v);
+
+  return gtk_css_value_dup_value (v);
+}
+
+const GValue *
+gtk_css_value_peek (GtkCssValue *v)
+{
+  return (const GValue *)v;
+}
+
 static void
 gtk_css_computed_values_dispose (GObject *object)
 {
@@ -35,7 +109,7 @@ gtk_css_computed_values_dispose (GObject *object)
 
   if (values->values)
     {
-      g_array_free (values->values, TRUE);
+      g_ptr_array_unref (values->values);
       values->values = NULL;
     }
   if (values->sections)
@@ -47,18 +121,282 @@ gtk_css_computed_values_dispose (GObject *object)
   G_OBJECT_CLASS (_gtk_css_computed_values_parent_class)->dispose (object);
 }
 
+static gboolean
+strv_equal (char **a, char **b)
+{
+  int i;
+
+  if (a == b)
+    return TRUE;
+
+  if (a == NULL || b == NULL)
+    return FALSE;
+
+  for (i = 0; a[i] != NULL && b[i] != NULL; i++)
+    {
+      if (strcmp (a[i], b[i]) != 0)
+	return FALSE;
+    }
+  return a[i] == NULL && b[i] == NULL;
+}
+
+static guint
+strv_hash (char **v)
+{
+  int i;
+  guint hash;
+
+  if (v == NULL)
+    return 0;
+
+  hash = 0;
+  for (i = 0; v[i] != NULL; i++)
+    hash ^= g_str_hash (v[i]);
+  return hash;
+}
+
+static gboolean
+gtk_css_value_equal (GtkCssValue *css_value_a, GtkCssValue *css_value_b)
+{
+  GType type;
+  const GValue *a = gtk_css_value_peek (css_value_a);
+  const GValue *b = gtk_css_value_peek (css_value_b);
+
+  if (a == b)
+    return TRUE;
+
+  if (a == NULL || b == NULL)
+    return FALSE;
+
+  if (a->g_type != b->g_type)
+    return FALSE;
+
+  type = a->g_type;
+  if (type == G_TYPE_INT || type == G_TYPE_BOOLEAN)
+    {
+      return a->data[0].v_int == b->data[0].v_int;
+    }
+  else if (type == G_TYPE_DOUBLE)
+    {
+      return a->data[0].v_double == b->data[0].v_double;
+    }
+  else if (type == G_TYPE_LONG ||
+	   G_TYPE_IS_ENUM (type) ||
+	   G_TYPE_IS_FLAGS (type))
+    {
+      return a->data[0].v_long == b->data[0].v_long;
+    }
+  else if (type == GDK_TYPE_RGBA)
+    {
+      return gdk_rgba_equal (a->data[0].v_pointer,
+			     b->data[0].v_pointer);
+    }
+  else if (type == G_TYPE_STRV)
+    {
+      return strv_equal (a->data[0].v_pointer,
+			 b->data[0].v_pointer);
+    }
+  else if (type == GTK_TYPE_CSS_NUMBER)
+    {
+      return _gtk_css_number_equal (a->data[0].v_pointer,
+				    b->data[0].v_pointer);
+    }
+  else if (type == GTK_TYPE_CSS_BORDER_IMAGE_REPEAT)
+    {
+      GtkCssBorderImageRepeat *aa = a->data[0].v_pointer;
+      GtkCssBorderImageRepeat *bb = b->data[0].v_pointer;
+
+      if (aa == bb)
+	return TRUE;
+      if (aa == NULL || bb == NULL)
+	return FALSE;
+
+      return
+	aa->vrepeat == bb->vrepeat &&
+	aa->hrepeat == bb->hrepeat;
+    }
+  else if (type == GTK_TYPE_CSS_BORDER_CORNER_RADIUS)
+    {
+      GtkCssBorderCornerRadius *aa = a->data[0].v_pointer;
+      GtkCssBorderCornerRadius *bb = b->data[0].v_pointer;
+
+      if (aa == bb)
+	return TRUE;
+      if (aa == NULL || bb == NULL)
+	return FALSE;
+
+      return
+	_gtk_css_number_equal (&aa->horizontal, &bb->horizontal) &&
+	_gtk_css_number_equal (&aa->vertical, &bb->vertical);
+    }
+  else if (type == GTK_TYPE_BORDER)
+    {
+      GtkBorder *aa = a->data[0].v_pointer;
+      GtkBorder *bb = b->data[0].v_pointer;
+
+      if (aa == bb)
+	return TRUE;
+      if (aa == NULL || bb == NULL)
+	return FALSE;
+
+      return
+	aa->left == bb->left &&
+	aa->right == bb->right &&
+	aa->top == bb->top &&
+	aa->bottom == bb->bottom;
+    }
+  else if (type == GTK_TYPE_CSS_BACKGROUND_SIZE)
+    {
+      GtkCssBackgroundSize *aa = a->data[0].v_pointer;
+      GtkCssBackgroundSize *bb = b->data[0].v_pointer;
+
+      if (aa == bb)
+	return TRUE;
+      if (aa == NULL || bb == NULL)
+	return FALSE;
+
+      return
+	_gtk_css_number_equal (&aa->width, &bb->width) &&
+	_gtk_css_number_equal (&aa->height, &bb->height) &&
+	aa->cover == bb->cover &&
+	aa->contain == bb->contain;
+    }
+  else if (type == GTK_TYPE_SHADOW ||
+	   type == G_TYPE_PTR_ARRAY ||
+	   type == CAIRO_GOBJECT_TYPE_PATTERN ||
+	   type == GTK_TYPE_THEMING_ENGINE ||
+	   type == GTK_TYPE_ANIMATION_DESCRIPTION||
+	   type == GTK_TYPE_CSS_IMAGE)
+    {
+      /* These are refcounted, compare by pointer */
+      return a->data[0].v_pointer == b->data[0].v_pointer;
+    }
+  else
+    {
+      g_error ("Can't handle CSS type %s\n", g_type_name (type));
+    }
+
+  return FALSE;
+}
+
+
+static guint
+gtk_css_value_hash (GtkCssValue *css_value)
+{
+  GType type;
+  const GValue *v = gtk_css_value_peek (css_value);
+
+  if (v == NULL)
+    return 0;
+
+  if (v->g_type == 0)
+    return 0;
+
+  type = v->g_type;
+
+  if (type == G_TYPE_INT || type == G_TYPE_BOOLEAN)
+    {
+      return (guint)v->data[0].v_int;
+    }
+  else if (type == G_TYPE_DOUBLE)
+    {
+      return (guint)v->data[0].v_double;
+    }
+  else if (type == G_TYPE_LONG ||
+	   G_TYPE_IS_ENUM (type) ||
+	   G_TYPE_IS_FLAGS (type))
+    {
+      return (guint)v->data[0].v_long;
+    }
+  else if (type == GDK_TYPE_RGBA)
+    {
+      return gdk_rgba_hash (v->data[0].v_pointer);
+    }
+  else if (type == G_TYPE_STRV)
+    {
+      return strv_hash (v->data[0].v_pointer);
+    }
+  else if (type == GTK_TYPE_CSS_NUMBER)
+    {
+      return _gtk_css_number_hash (v->data[0].v_pointer);
+    }
+  else if (type == GTK_TYPE_CSS_BORDER_IMAGE_REPEAT)
+    {
+      GtkCssBorderImageRepeat *vv = v->data[0].v_pointer;
+
+      if (vv == NULL)
+	return 0;
+
+      return ((guint)vv->vrepeat) ^ ((guint)vv->hrepeat);
+    }
+  else if (type == GTK_TYPE_CSS_BORDER_CORNER_RADIUS)
+    {
+      GtkCssBorderCornerRadius *vv = v->data[0].v_pointer;
+
+      if (vv == NULL)
+	return 0;
+
+      return
+	_gtk_css_number_hash (&vv->horizontal) ^
+	_gtk_css_number_hash (&vv->vertical);
+    }
+  else if (type == GTK_TYPE_BORDER)
+    {
+      GtkBorder *vv = v->data[0].v_pointer;
+
+      if (vv == NULL)
+	return 0;
+
+      return
+	((guint)vv->left) ^
+	(((guint)vv->right) << 16) ^
+	((guint)vv->top) ^
+	(((guint)vv->bottom) << 16);
+    }
+  else if (type == GTK_TYPE_CSS_BACKGROUND_SIZE)
+    {
+      GtkCssBackgroundSize *vv = v->data[0].v_pointer;
+
+      if (vv == NULL)
+	return 0;
+
+      return
+	_gtk_css_number_hash (&vv->width) ^
+	_gtk_css_number_hash (&vv->height) ^
+	(((guint)vv->cover) << 9) ^
+	(((guint)vv->contain) << 9);
+    }
+  else if (type == GTK_TYPE_SHADOW ||
+	   type == G_TYPE_PTR_ARRAY ||
+	   type == CAIRO_GOBJECT_TYPE_PATTERN ||
+	   type == GTK_TYPE_THEMING_ENGINE ||
+	   type == GTK_TYPE_ANIMATION_DESCRIPTION||
+	   type == GTK_TYPE_CSS_IMAGE)
+    {
+      /* These are refcounted, compare by pointer */
+      return GPOINTER_TO_INT (v->data[0].v_pointer);
+    }
+  else
+    {
+      g_error ("Can't handle CSS type %s\n", g_type_name (type));
+    }
+
+  return FALSE;
+}
+
 static void
 _gtk_css_computed_values_class_init (GtkCssComputedValuesClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->dispose = gtk_css_computed_values_dispose;
+
+  gtk_css_values = g_hash_table_new ((GHashFunc)gtk_css_value_hash, (GEqualFunc)gtk_css_value_equal);
 }
 
 static void
 _gtk_css_computed_values_init (GtkCssComputedValues *computed_values)
 {
-  
 }
 
 GtkCssComputedValues *
@@ -92,12 +430,9 @@ _gtk_css_computed_values_compute_value (GtkCssComputedValues *values,
   parent = gtk_style_context_get_parent (context);
 
   if (values->values == NULL)
-    {
-      values->values = g_array_new (FALSE, TRUE, sizeof (GValue));
-      g_array_set_clear_func (values->values, (GDestroyNotify) g_value_unset);
-    }
+    values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_css_value_unref);
   if (id <= values->values->len)
-   g_array_set_size (values->values, id + 1);
+   g_ptr_array_set_size (values->values, id + 1);
 
   /* http://www.w3.org/TR/css3-cascade/#cascade
    * Then, for every element, the value for each property can be found
@@ -158,22 +493,27 @@ _gtk_css_computed_values_compute_value (GtkCssComputedValues *values,
       specified = _gtk_css_style_property_get_initial_value (prop);
     }
 
+
+  /* Clear existing value, can't reuse as it may be shared */
+  if (g_ptr_array_index (values->values, id) != NULL)
+    gtk_css_value_unref (g_ptr_array_index (values->values, id));
+
   if (specified)
     {
+      GValue value = G_VALUE_INIT;
       _gtk_css_style_property_compute_value (prop,
-                                             &g_array_index (values->values, GValue, id),
+                                             &value,
                                              context,
                                              specified);
+      g_ptr_array_index (values->values, id) = gtk_css_value_dup_value (&value);
+      g_value_unset (&value);
     }
   else
     {
       const GValue *parent_value;
-      GValue *value = &g_array_index (values->values, GValue, id);
-      /* Set NULL here and do the inheritance upon lookup? */
       parent_value = _gtk_style_context_peek_property (parent,
                                                        _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop)));
-      g_value_init (value, G_VALUE_TYPE (parent_value));
-      g_value_copy (parent_value, value);
+      g_ptr_array_index (values->values, id) = gtk_css_value_ref_value ((GValue *)parent_value);
     }
 
   if (section)
@@ -193,23 +533,19 @@ _gtk_css_computed_values_set_value (GtkCssComputedValues *values,
                                     const GValue         *value,
                                     GtkCssSection        *section)
 {
-  GValue *set;
-
   g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values));
   g_return_if_fail (value == NULL || G_IS_VALUE (value));
 
   if (values->values == NULL)
-    {
-      values->values = g_array_new (FALSE, TRUE, sizeof (GValue));
-      g_array_set_clear_func (values->values, (GDestroyNotify) g_value_unset);
-    }
+    values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_css_value_unref);
   if (id <= values->values->len)
-   g_array_set_size (values->values, id + 1);
+   g_ptr_array_set_size (values->values, id + 1);
 
+  /* Clear existing value, can't reuse as it may be shared */
+  if (g_ptr_array_index (values->values, id) != NULL)
+    gtk_css_value_unref (g_ptr_array_index (values->values, id));
 
-  set = &g_array_index (values->values, GValue, id);
-  g_value_init (set, G_VALUE_TYPE (value));
-  g_value_copy (value, set);
+  g_ptr_array_index (values->values, id) = gtk_css_value_ref_value ((GValue *)value);
 
   if (section)
     {
@@ -226,19 +562,13 @@ const GValue *
 _gtk_css_computed_values_get_value (GtkCssComputedValues *values,
                                     guint                 id)
 {
-  const GValue *v;
-
   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
 
   if (values->values == NULL ||
       id >= values->values->len)
     return NULL;
 
-  v = &g_array_index (values->values, GValue, id);
-  if (!G_IS_VALUE (v))
-    return NULL;
-
-  return v;
+  return gtk_css_value_peek (g_ptr_array_index (values->values, id));
 }
 
 const GValue *
diff --git a/gtk/gtkcsscomputedvaluesprivate.h b/gtk/gtkcsscomputedvaluesprivate.h
index 74a15ec..dff18ae 100644
--- a/gtk/gtkcsscomputedvaluesprivate.h
+++ b/gtk/gtkcsscomputedvaluesprivate.h
@@ -42,7 +42,7 @@ struct _GtkCssComputedValues
 {
   GObject parent;
 
-  GArray                *values;
+  GPtrArray             *values;
   GPtrArray             *sections;
 };
 



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