icontheme: Keep a single string set
authorMatthias Clasen <mclasen@redhat.com>
Sat, 14 Jan 2023 01:52:30 +0000 (20:52 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Sat, 14 Jan 2023 05:14:43 +0000 (00:14 -0500)
Instead of keeping a GtkStringSet per IconTheme,
just make one for the whole GtkIconTheme.

This avoids loops of the themes in some places, and
due to the overlap in icon names between the themes,
it reduces the amount of memory we use for the icon
names with Adwaita+hicolor from 5+4 chunks to 6 chunks.

gtk/gtkicontheme.c

index 6340186fca1dde59500ca7c07c425b5e8e91d3b5..517a0f781949e5f4e3fa245d6c00943a31370674 100644 (file)
@@ -130,10 +130,35 @@ struct _GtkStringSet {
   int used_in_chunk;
 };
 
+#ifdef G_ENABLE_DEBUG
+static void
+dump_string_set (GtkStringSet *set)
+{
+  GtkStringSetChunk *chunk = set->chunks;
+  unsigned int n_chunks = 0;
+  GHashTableIter iter;
+  gpointer key;
+
+  while (chunk)
+    {
+      n_chunks++;
+      chunk = chunk->next;
+    }
+  g_print ("%u strings, %u chunks\n", g_hash_table_size (set->hash), n_chunks);
+
+  g_hash_table_iter_init (&iter, set->hash);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    {
+      char *string = key;
+      g_print ("%s\n", string);
+    }
+}
+#endif
+
 static void
 gtk_string_set_init (GtkStringSet *set)
 {
-  set->hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+  set->hash = g_hash_table_new (g_str_hash, g_str_equal);
   set->chunks = NULL;
   set->used_in_chunk = STRING_SET_CHUNK_SIZE; /* To trigger a grow directly */
 }
@@ -306,6 +331,8 @@ struct _GtkIconTheme
   GtkIconPaintable *lru_cache[LRU_CACHE_SIZE];  /* Protected by icon_cache lock */
   int lru_cache_current;                        /* Protected by icon_cache lock */
 
+  GtkStringSet icons;
+
   char *current_theme;
   char **search_path;
   char **resource_path;
@@ -409,7 +436,6 @@ typedef struct
 
   GArray *dir_sizes;     /* IconThemeDirSize */
   GArray *dirs;          /* IconThemeDir */
-  GtkStringSet icons;
 } IconTheme;
 
 typedef struct
@@ -465,8 +491,6 @@ static GtkIconPaintable *theme_lookup_icon                (IconTheme        *the
                                                            int               size,
                                                            int               scale,
                                                            gboolean          allow_svg);
-static gboolean          theme_has_icon                   (IconTheme        *theme,
-                                                           const char       *icon_name);
 static void              theme_subdir_load                (GtkIconTheme     *self,
                                                            IconTheme        *theme,
                                                            GKeyFile         *theme_file,
@@ -1358,6 +1382,7 @@ blow_themes (GtkIconTheme *self)
       g_list_free_full (self->themes, (GDestroyNotify) theme_destroy);
       g_array_set_size (self->dir_mtimes, 0);
       g_hash_table_destroy (self->unthemed_icons);
+      gtk_string_set_destroy (&self->icons);
     }
   self->themes = NULL;
   self->unthemed_icons = NULL;
@@ -1939,6 +1964,8 @@ load_themes (GtkIconTheme *self)
   GStatBuf stat_buf;
   int j;
 
+  gtk_string_set_init (&self->icons);
+
   if (self->current_theme)
     insert_theme (self, self->current_theme);
 
@@ -2014,6 +2041,8 @@ load_themes (GtkIconTheme *self)
         }
       gdk_debug_message ("%s", s->str);
       g_string_free (s, TRUE);
+
+      dump_string_set (&self->icons);
     }
 #endif
 }
@@ -2159,10 +2188,13 @@ real_choose_icon (GtkIconTheme      *self,
       theme = l->data;
       for (i = 0; icon_names[i] && icon_name_is_symbolic (icon_names[i], -1); i++)
         {
-          icon_name = icon_names[i];
-          icon = theme_lookup_icon (theme, icon_name, size, scale, self->pixbuf_supports_svg);
-          if (icon)
-            goto out;
+          icon_name = gtk_string_set_lookup (&self->icons, icon_names[i]);
+          if (icon_name)
+            {
+              icon = theme_lookup_icon (theme, icon_name, size, scale, self->pixbuf_supports_svg);
+              if (icon)
+                goto out;
+            }
         }
     }
 
@@ -2172,10 +2204,13 @@ real_choose_icon (GtkIconTheme      *self,
 
       for (i = 0; icon_names[i]; i++)
         {
-          icon_name = icon_names[i];
-          icon = theme_lookup_icon (theme, icon_name, size, scale, self->pixbuf_supports_svg);
-          if (icon)
-            goto out;
+          icon_name = gtk_string_set_lookup (&self->icons, icon_names[i]);
+          if (icon_name)
+            {
+              icon = theme_lookup_icon (theme, icon_name, size, scale, self->pixbuf_supports_svg);
+              if (icon)
+                goto out;
+            }
         }
     }
 
@@ -2556,7 +2591,6 @@ gboolean
 gtk_icon_theme_has_icon (GtkIconTheme *self,
                          const char   *icon_name)
 {
-  GList *l;
   gboolean res = FALSE;
 
   g_return_val_if_fail (GTK_IS_ICON_THEME (self), FALSE);
@@ -2566,13 +2600,10 @@ gtk_icon_theme_has_icon (GtkIconTheme *self,
 
   ensure_valid_themes (self, FALSE);
 
-  for (l = self->themes; l; l = l->next)
+  if (gtk_string_set_lookup (&self->icons, icon_name) != NULL)
     {
-      if (theme_has_icon (l->data, icon_name))
-        {
-          res = TRUE;
-          goto out;
-        }
+      res = TRUE;
+      goto out;
     }
 
  out:
@@ -2611,13 +2642,10 @@ gtk_icon_theme_has_gicon (GtkIconTheme *self,
 
   for (int i = 0; names[i]; i++)
     {
-      for (GList *l = self->themes; l; l = l->next)
+      if (gtk_string_set_lookup (&self->icons, names[i]) != NULL)
         {
-          if (theme_has_icon (l->data, names[i]))
-            {
-              res = TRUE;
-              goto out;
-            }
+          res = TRUE;
+          goto out;
         }
     }
 
@@ -2663,6 +2691,7 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *self,
   int i;
   GHashTable *sizes;
   int *result, *r;
+  const char *interned_icon_name;
 
   g_return_val_if_fail (GTK_IS_ICON_THEME (self), NULL);
 
@@ -2672,10 +2701,11 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *self,
 
   sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
 
+  interned_icon_name = gtk_string_set_lookup (&self->icons, icon_name);
+
   for (l = self->themes; l; l = l->next)
     {
       IconTheme *theme = l->data;
-      const char *interned_icon_name = gtk_string_set_lookup (&theme->icons, icon_name);
 
       for (i = 0; i < theme->dir_sizes->len; i++)
         {
@@ -2732,25 +2762,15 @@ gtk_icon_theme_get_icon_names (GtkIconTheme *self)
   char **names;
   char *key;
   int i;
-  GList *l;
 
   gtk_icon_theme_lock (self);
 
   ensure_valid_themes (self, FALSE);
 
   icons = g_hash_table_new (g_str_hash, g_str_equal);
+  gtk_string_set_list (&self->icons, icons);
 
-  l = self->themes;
-  while (l != NULL)
-    {
-      IconTheme *theme = l->data;
-      gtk_string_set_list (&theme->icons, icons);
-      l = l->next;
-    }
-
-  g_hash_table_foreach (self->unthemed_icons,
-                        add_key_to_hash,
-                        icons);
+  g_hash_table_foreach (self->unthemed_icons, add_key_to_hash, icons);
 
   names = g_new (char *, g_hash_table_size (icons) + 1);
 
@@ -2809,7 +2829,6 @@ theme_new (const char *theme_name,
   theme->name = g_strdup (theme_name);
   theme->dir_sizes = g_array_new (FALSE, FALSE, sizeof (IconThemeDirSize));
   theme->dirs = g_array_new (FALSE, FALSE, sizeof (IconThemeDir));
-  gtk_string_set_init (&theme->icons);
 
   theme->display_name =
     g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
@@ -2840,8 +2859,6 @@ theme_destroy (IconTheme *theme)
     theme_dir_destroy (&g_array_index (theme->dirs, IconThemeDir, i));
   g_array_free (theme->dirs, TRUE);
 
-  gtk_string_set_destroy (&theme->icons);
-
   g_free (theme);
 }
 
@@ -3040,13 +3057,6 @@ theme_lookup_icon (IconTheme   *theme,
   IconCacheFlag min_suffix;
   int i;
 
-  /* Its not uncommon with misses, so we do an early check which allows us do
-   * do a lot less work.
-   * We also intern the name so later hash lookups are faster. */
-  icon_name = gtk_string_set_lookup (&theme->icons, icon_name);
-  if (icon_name == NULL)
-    return FALSE;
-
   min_difference = G_MAXINT;
   min_dir_size = NULL;
 
@@ -3106,13 +3116,6 @@ theme_lookup_icon (IconTheme   *theme,
   return NULL;
 }
 
-static gboolean
-theme_has_icon (IconTheme   *theme,
-                const char *icon_name)
-{
-  return gtk_string_set_lookup (&theme->icons, icon_name) != NULL;
-}
-
 static GHashTable *
 scan_directory (GtkIconTheme  *self,
                 char          *full_dir,