From 85b1b9b77816198040d9437ea2e1231c47e41e68 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 15 Feb 2012 17:06:30 +0100 Subject: [PATCH] Don't use hashtables for storing css rule properties This saves a lot of memory --- gtk/gtkcssprovider.c | 291 ++++++++++++++++++++++++++++++------------- 1 file changed, 201 insertions(+), 90 deletions(-) diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c index 32bdd3a56..26182e020 100644 --- a/gtk/gtkcssprovider.c +++ b/gtk/gtkcssprovider.c @@ -962,15 +962,19 @@ typedef struct GtkCssRuleset GtkCssRuleset; typedef struct _GtkCssScanner GtkCssScanner; +typedef struct _PropertyValue PropertyValue; +typedef struct _WidgetPropertyValue WidgetPropertyValue; typedef enum ParserScope ParserScope; typedef enum ParserSymbol ParserSymbol; struct GtkCssRuleset { GtkCssSelector *selector; - GHashTable *widget_style; - GHashTable *style; + WidgetPropertyValue *widget_style; + PropertyValue *style; GtkBitmask *set_styles; + guint owns_style : 1; + guint owns_widget_style : 1; }; struct _GtkCssScanner @@ -1004,6 +1008,8 @@ static guint css_provider_signals[LAST_SIGNAL] = { 0 }; static void gtk_css_provider_finalize (GObject *object); static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface); static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface); +static void property_value_list_free (PropertyValue *head); +static void widget_property_value_list_free (WidgetPropertyValue *head); static gboolean gtk_css_provider_load_internal (GtkCssProvider *css_provider, @@ -1112,16 +1118,17 @@ gtk_css_provider_class_init (GtkCssProviderClass *klass) static void gtk_css_ruleset_init_copy (GtkCssRuleset *new, - const GtkCssRuleset *ruleset, + GtkCssRuleset *ruleset, GtkCssSelector *selector) { memcpy (new, ruleset, sizeof (GtkCssRuleset)); new->selector = selector; - if (new->widget_style) - g_hash_table_ref (new->widget_style); - if (new->style) - g_hash_table_ref (new->style); + /* First copy takes over ownership */ + if (ruleset->owns_style) + ruleset->owns_style = FALSE; + if (ruleset->owns_widget_style) + ruleset->owns_widget_style = FALSE; if (new->set_styles) new->set_styles = _gtk_bitmask_copy (new->set_styles); } @@ -1129,31 +1136,34 @@ gtk_css_ruleset_init_copy (GtkCssRuleset *new, static void gtk_css_ruleset_clear (GtkCssRuleset *ruleset) { - if (ruleset->style) - g_hash_table_unref (ruleset->style); + if (ruleset->owns_style) + property_value_list_free (ruleset->style); if (ruleset->set_styles) _gtk_bitmask_free (ruleset->set_styles); - if (ruleset->widget_style) - g_hash_table_unref (ruleset->widget_style); + if (ruleset->owns_widget_style) + widget_property_value_list_free (ruleset->widget_style); if (ruleset->selector) _gtk_css_selector_free (ruleset->selector); memset (ruleset, 0, sizeof (GtkCssRuleset)); } -typedef struct _PropertyValue PropertyValue; struct _PropertyValue { - GtkCssSection *section; + GtkStyleProperty *property; + PropertyValue *next; GValue value; + + GtkCssSection *section; }; static PropertyValue * -property_value_new (GtkCssSection *section) +property_value_new (GtkStyleProperty *property, GtkCssSection *section) { PropertyValue *value; value = g_slice_new0 (PropertyValue); + value->property = property; value->section = gtk_css_section_ref (section); return value; @@ -1170,18 +1180,113 @@ property_value_free (PropertyValue *value) g_slice_free (PropertyValue, value); } +static void +property_value_list_free (PropertyValue *head) +{ + PropertyValue *l, *next; + for (l = head; l != NULL; l = next) + { + next = l->next; + property_value_free (l); + } +} + +static PropertyValue * +property_value_list_remove_property (PropertyValue *head, GtkStyleProperty *property) +{ + PropertyValue *l, **last; + + last = &head; + + for (l = head; l != NULL; l = l->next) + { + if (l->property == property) + { + *last = l->next; + property_value_free (l); + break; + } + + last = &l->next; + } + + return head; +} + +struct _WidgetPropertyValue { + char *name; + WidgetPropertyValue *next; + GValue value; + + GtkCssSection *section; +}; + +static WidgetPropertyValue * +widget_property_value_new (char *name, GtkCssSection *section) +{ + WidgetPropertyValue *value; + + value = g_slice_new0 (WidgetPropertyValue); + + value->name = name; + value->section = gtk_css_section_ref (section); + + return value; +} + +static void +widget_property_value_free (WidgetPropertyValue *value) +{ + if (G_IS_VALUE (&value->value)) + g_value_unset (&value->value); + + g_free (value->name); + gtk_css_section_unref (value->section); + + g_slice_free (WidgetPropertyValue, value); +} + +static void +widget_property_value_list_free (WidgetPropertyValue *head) +{ + WidgetPropertyValue *l, *next; + for (l = head; l != NULL; l = next) + { + next = l->next; + widget_property_value_free (l); + } +} + +static WidgetPropertyValue * +widget_property_value_list_remove_name (WidgetPropertyValue *head, const char *name) +{ + WidgetPropertyValue *l, **last; + + last = &head; + + for (l = head; l != NULL; l = l->next) + { + if (strcmp (l->name, name) == 0) + { + *last = l->next; + widget_property_value_free (l); + break; + } + + last = &l->next; + } + + return head; +} + static void gtk_css_ruleset_add_style (GtkCssRuleset *ruleset, char *name, - PropertyValue *value) + WidgetPropertyValue *value) { - if (ruleset->widget_style == NULL) - ruleset->widget_style = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) property_value_free); - - g_hash_table_insert (ruleset->widget_style, name, value); + value->next = widget_property_value_list_remove_name (ruleset->widget_style, name); + ruleset->widget_style = value; + ruleset->owns_widget_style = TRUE; } static void @@ -1189,14 +1294,8 @@ gtk_css_ruleset_add (GtkCssRuleset *ruleset, GtkStyleProperty *prop, PropertyValue *value) { - if (ruleset->style == NULL) - { - ruleset->style = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) property_value_free); - ruleset->set_styles = _gtk_bitmask_new (); - } + if (ruleset->set_styles == NULL) + ruleset->set_styles = _gtk_bitmask_new (); if (GTK_IS_CSS_SHORTHAND_PROPERTY (prop)) { @@ -1210,7 +1309,7 @@ gtk_css_ruleset_add (GtkCssRuleset *ruleset, const GValue *sub = &g_array_index (array, GValue, i); PropertyValue *val; - val = property_value_new (value->section); + val = property_value_new (child, value->section); g_value_init (&val->value, G_VALUE_TYPE (sub)); g_value_copy (sub, &val->value); gtk_css_ruleset_add (ruleset, GTK_STYLE_PROPERTY (child), val); @@ -1225,7 +1324,10 @@ gtk_css_ruleset_add (GtkCssRuleset *ruleset, _gtk_bitmask_set (ruleset->set_styles, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)), TRUE); - g_hash_table_insert (ruleset->style, prop, value); + + value->next = property_value_list_remove_property (ruleset->style, prop); + ruleset->style = value; + ruleset->owns_style = TRUE; } else { @@ -1424,8 +1526,7 @@ gtk_css_provider_get_style (GtkStyleProvider *provider, for (i = 0; i < priv->rulesets->len; i++) { GtkCssRuleset *ruleset; - GHashTableIter iter; - gpointer key, val; + PropertyValue *value; ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); @@ -1435,18 +1536,11 @@ gtk_css_provider_get_style (GtkStyleProvider *provider, if (!gtk_css_ruleset_matches (ruleset, path, 0)) continue; - g_hash_table_iter_init (&iter, ruleset->style); - - while (g_hash_table_iter_next (&iter, &key, &val)) - { - GtkCssStyleProperty *prop = key; - PropertyValue *value = val; - - _gtk_style_properties_set_property_by_property (props, - prop, - _gtk_css_selector_get_state_flags (ruleset->selector), - &value->value); - } + for (value = ruleset->style; value != NULL; value = value->next) + _gtk_style_properties_set_property_by_property (props, + GTK_CSS_STYLE_PROPERTY (value->property), + _gtk_css_selector_get_state_flags (ruleset->selector), + &value->value); } return props; @@ -1461,7 +1555,7 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, { GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider); GtkCssProviderPrivate *priv = css_provider->priv; - PropertyValue *val; + WidgetPropertyValue *val; gboolean found = FALSE; gchar *prop_name; gint i; @@ -1482,27 +1576,30 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider, if (!gtk_css_ruleset_matches (ruleset, path, state)) continue; - val = g_hash_table_lookup (ruleset->widget_style, prop_name); + for (val = ruleset->widget_style; val != NULL; val = val->next) + { + if (strcmp (val->name, prop_name) == 0) + { + GtkCssScanner *scanner; - if (val) - { - GtkCssScanner *scanner; + scanner = gtk_css_scanner_new (css_provider, + NULL, + val->section, + gtk_css_section_get_file (val->section), + g_value_get_string (&val->value)); - scanner = gtk_css_scanner_new (css_provider, - NULL, - val->section, - gtk_css_section_get_file (val->section), - g_value_get_string (&val->value)); + found = _gtk_css_style_parse_value (value, + scanner->parser, + NULL); - found = _gtk_css_style_parse_value (value, - scanner->parser, - NULL); + gtk_css_scanner_destroy (scanner); - gtk_css_scanner_destroy (scanner); + break; + } + } - if (found) - break; - } + if (found) + break; } g_free (prop_name); @@ -1542,8 +1639,7 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, for (i = priv->rulesets->len - 1; i >= 0; i--) { GtkCssRuleset *ruleset; - GHashTableIter iter; - gpointer key, val; + PropertyValue *value; ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i); @@ -1557,12 +1653,9 @@ gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider, if (!gtk_css_ruleset_matches (ruleset, path, state)) continue; - g_hash_table_iter_init (&iter, ruleset->style); - - while (g_hash_table_iter_next (&iter, &key, &val)) + for (value = ruleset->style; value != NULL; value = value->next) { - GtkCssStyleProperty *prop = key; - PropertyValue *value = val; + GtkCssStyleProperty *prop = GTK_CSS_STYLE_PROPERTY (value->property); guint id = _gtk_css_style_property_get_id (prop); if (!_gtk_css_lookup_is_missing (lookup, id)) @@ -2383,7 +2476,7 @@ parse_declaration (GtkCssScanner *scanner, gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE); - val = property_value_new (scanner->section); + val = property_value_new (property, scanner->section); if (_gtk_style_property_parse_value (property, &val->value, @@ -2429,9 +2522,9 @@ parse_declaration (GtkCssScanner *scanner, value_str = _gtk_css_parser_read_value (scanner->parser); if (value_str) { - PropertyValue *val; + WidgetPropertyValue *val; - val = property_value_new (scanner->section); + val = widget_property_value_new (name, scanner->section); g_value_init (&val->value, G_TYPE_STRING); g_value_take_string (&val->value, value_str); @@ -2970,15 +3063,27 @@ gtk_css_provider_get_named (const gchar *name, static int compare_properties (gconstpointer a, gconstpointer b) { - return strcmp (_gtk_style_property_get_name ((GtkStyleProperty *) a), - _gtk_style_property_get_name ((GtkStyleProperty *) b)); + const PropertyValue *aa = a; + const PropertyValue *bb = b; + return strcmp (_gtk_style_property_get_name ((GtkStyleProperty *)aa->property), + _gtk_style_property_get_name ((GtkStyleProperty *)bb->property)); +} + +static int +compare_names (gconstpointer a, gconstpointer b) +{ + const WidgetPropertyValue *aa = a; + const WidgetPropertyValue *bb = b; + return strcmp (aa->name, bb->name); } static void gtk_css_ruleset_print (const GtkCssRuleset *ruleset, GString *str) { - GList *keys, *walk; + GList *values, *walk; + PropertyValue *value; + WidgetPropertyValue *widget_value; _gtk_css_selector_print (ruleset->selector, str); @@ -2986,14 +3091,18 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset, if (ruleset->style) { - keys = g_hash_table_get_keys (ruleset->style); + values = NULL; + for (value = ruleset->style; value != NULL; value = value->next) + values = g_list_prepend (values, value); + /* so the output is identical for identical selector styles */ - keys = g_list_sort (keys, compare_properties); + values = g_list_sort (values, compare_properties); - for (walk = keys; walk; walk = walk->next) + for (walk = values; walk; walk = walk->next) { - GtkCssStyleProperty *prop = walk->data; - const PropertyValue *value = g_hash_table_lookup (ruleset->style, prop); + GtkCssStyleProperty *prop; + value = walk->data; + prop = GTK_CSS_STYLE_PROPERTY (value->property); g_string_append (str, " "); g_string_append (str, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop))); @@ -3002,28 +3111,30 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset, g_string_append (str, ";\n"); } - g_list_free (keys); + g_list_free (values); } if (ruleset->widget_style) { - keys = g_hash_table_get_keys (ruleset->widget_style); + values = NULL; + for (widget_value = ruleset->widget_style; widget_value != NULL; widget_value = widget_value->next) + values = g_list_prepend (values, widget_value); + /* so the output is identical for identical selector styles */ - keys = g_list_sort (keys, (GCompareFunc) strcmp); + values = g_list_sort (values, compare_names); - for (walk = keys; walk; walk = walk->next) + for (walk = values; walk; walk = walk->next) { - const char *name = walk->data; - const PropertyValue *value = g_hash_table_lookup (ruleset->widget_style, (gpointer) name); + widget_value = walk->data; g_string_append (str, " "); - g_string_append (str, name); + g_string_append (str, widget_value->name); g_string_append (str, ": "); - g_string_append (str, g_value_get_string (&value->value)); + g_string_append (str, g_value_get_string (&widget_value->value)); g_string_append (str, ";\n"); } - g_list_free (keys); + g_list_free (values); } g_string_append (str, "}\n"); -- 2.43.2