diff libpurple/blist.c @ 26764:0e99b80b54c6

Make purple_find_buddies(account, NULL) O(# buddies in the account) and rewrite purple_ssi_parselist to take advantage of said efficiency. Applied HanzZ's patch with a few tweaks, mostly making the hash table local to blist.c and using the signals from the previous commit. committer: Paul Aurich <paul@darkrain42.org>
author hanzz@soc.pidgin.im
date Fri, 01 May 2009 08:07:12 +0000
parents 3e274d6fcfaa
children 0924698e2362
line wrap: on
line diff
--- a/libpurple/blist.c	Fri May 01 06:59:36 2009 +0000
+++ b/libpurple/blist.c	Fri May 01 08:07:12 2009 +0000
@@ -40,6 +40,14 @@
 static PurpleBlistUiOps *blist_ui_ops = NULL;
 
 static PurpleBuddyList *purplebuddylist = NULL;
+
+/**
+ * A hash table used for efficient lookups of buddies by name.
+ * PurpleAccount* => GHashTable*, with the inner hash table being
+ * struct _purple_hbuddy => PurpleBuddy*
+ */
+static GHashTable *buddies_cache = NULL;
+
 static guint          save_timer = 0;
 static gboolean       blist_loaded = FALSE;
 
@@ -91,6 +99,21 @@
 	g_free(hb);
 }
 
+static void
+purple_blist_buddies_cache_add_account(PurpleAccount *account)
+{
+	GHashTable *account_buddies = g_hash_table_new_full((GHashFunc)_purple_blist_hbuddy_hash,
+						(GEqualFunc)_purple_blist_hbuddy_equal,
+						(GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
+	g_hash_table_insert(buddies_cache, account, account_buddies);
+}
+
+static void
+purple_blist_buddies_cache_remove_account(const PurpleAccount *account)
+{
+	g_hash_table_remove(buddies_cache, account);
+}
+
 
 /*********************************************************************
  * Writing to disk                                                   *
@@ -666,6 +689,7 @@
 PurpleBuddyList *purple_blist_new()
 {
 	PurpleBlistUiOps *ui_ops;
+	GList *account;
 	PurpleBuddyList *gbl = g_new0(PurpleBuddyList, 1);
 	PURPLE_DBUS_REGISTER_POINTER(gbl, PurpleBuddyList);
 
@@ -675,6 +699,14 @@
 					 (GEqualFunc)_purple_blist_hbuddy_equal,
 					 (GDestroyNotify)_purple_blist_hbuddy_free_key, NULL);
 
+	buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+					 NULL, (GDestroyNotify)g_hash_table_destroy);
+
+	for (account = purple_accounts_get_all(); account != NULL; account = account->next)
+	{
+		purple_blist_buddies_cache_add_account(account->data);
+	}
+
 	if (ui_ops != NULL && ui_ops->new_list != NULL)
 		ui_ops->new_list(gbl);
 
@@ -898,7 +930,8 @@
 void purple_blist_rename_buddy(PurpleBuddy *buddy, const char *name)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	struct _purple_hbuddy *hb;
+	struct _purple_hbuddy *hb, *hb2;
+	GHashTable *account_buddies;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -907,11 +940,21 @@
 	hb->account = buddy->account;
 	hb->group = ((PurpleBlistNode *)buddy)->parent->parent;
 	g_hash_table_remove(purplebuddylist->buddies, hb);
+	
+	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+	g_hash_table_remove(account_buddies, hb);
 
 	g_free(hb->name);
 	hb->name = g_strdup(purple_normalize(buddy->account, name));
 	g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
 
+	hb2 = g_new(struct _purple_hbuddy, 1);
+	hb2->name = g_strdup(hb->name);
+	hb2->account = buddy->account;
+	hb2->group = ((PurpleBlistNode *)buddy)->parent->parent;
+
+	g_hash_table_replace(account_buddies, hb2, buddy);
+
 	g_free(buddy->name);
 	buddy->name = g_strdup(name);
 
@@ -1433,7 +1476,8 @@
 	PurpleGroup *g;
 	PurpleContact *c;
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
-	struct _purple_hbuddy *hb;
+	struct _purple_hbuddy *hb, *hb2;
+	GHashTable *account_buddies;
 
 	g_return_if_fail(buddy != NULL);
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY((PurpleBlistNode*)buddy));
@@ -1503,6 +1547,10 @@
 			hb->account = buddy->account;
 			hb->group = bnode->parent->parent;
 			g_hash_table_remove(purplebuddylist->buddies, hb);
+
+			account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+			g_hash_table_remove(account_buddies, hb);
+
 			g_free(hb->name);
 			g_free(hb);
 		}
@@ -1549,6 +1597,15 @@
 
 	g_hash_table_replace(purplebuddylist->buddies, hb, buddy);
 
+	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+
+	hb2 = g_new(struct _purple_hbuddy, 1);
+	hb2->name = g_strdup(hb->name);
+	hb2->account = buddy->account;
+	hb2->group = ((PurpleBlistNode*)buddy)->parent->parent;
+
+	g_hash_table_replace(account_buddies, hb2, buddy);
+
 	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
 
 	purple_blist_schedule_save();
@@ -1707,18 +1764,31 @@
 			while (bnode) {
 				PurpleBlistNode *next_bnode = bnode->next;
 				PurpleBuddy *b = (PurpleBuddy*)bnode;
-
-				struct _purple_hbuddy *hb = g_new(struct _purple_hbuddy, 1);
+				GHashTable *account_buddies;
+
+				struct _purple_hbuddy *hb, *hb2;
+				
+				hb = g_new(struct _purple_hbuddy, 1);
 				hb->name = g_strdup(purple_normalize(b->account, b->name));
 				hb->account = b->account;
 				hb->group = cnode->parent;
 
 				g_hash_table_remove(purplebuddylist->buddies, hb);
 
+				account_buddies = g_hash_table_lookup(buddies_cache, b->account);
+				g_hash_table_remove(account_buddies, hb);
+
 				if (!purple_find_buddy_in_group(b->account, b->name, g)) {
 					hb->group = gnode;
 					g_hash_table_replace(purplebuddylist->buddies, hb, b);
 
+					hb2 = g_new(struct _purple_hbuddy, 1);
+					hb2->name = g_strdup(hb->name);
+					hb2->account = b->account;
+					hb2->group = gnode;
+
+					g_hash_table_replace(account_buddies, hb2, b);
+
 					if (purple_account_get_connection(b->account))
 						serv_move_buddy(b, (PurpleGroup *)cnode->parent, g);
 				} else {
@@ -1936,6 +2006,7 @@
 	PurpleContact *contact;
 	PurpleGroup *group;
 	struct _purple_hbuddy hb;
+	GHashTable *account_buddies;
 
 	g_return_if_fail(buddy != NULL);
 
@@ -1982,6 +2053,10 @@
 	hb.account = buddy->account;
 	hb.group = gnode;
 	g_hash_table_remove(purplebuddylist->buddies, &hb);
+
+	account_buddies = g_hash_table_lookup(buddies_cache, buddy->account);
+	g_hash_table_remove(account_buddies, &hb);
+
 	g_free(hb.name);
 
 	/* Update the UI */
@@ -2256,13 +2331,10 @@
 
 static void find_acct_buddies(gpointer key, gpointer value, gpointer data)
 {
-	struct _purple_hbuddy *hb = key;
 	PurpleBuddy *buddy = value;
-	struct _list_account_buddies *ab = data;
-
-	if (hb->account == ab->account) {
-		ab->list = g_slist_prepend(ab->list, buddy);
-	}
+	GSList **list = data;
+
+	*list = g_slist_prepend(*list, buddy);
 }
 
 GSList *purple_find_buddies(PurpleAccount *account, const char *name)
@@ -2274,7 +2346,6 @@
 	g_return_val_if_fail(purplebuddylist != NULL, NULL);
 	g_return_val_if_fail(account != NULL, NULL);
 
-
 	if ((name != NULL) && (*name != '\0')) {
 		struct _purple_hbuddy hb;
 
@@ -2288,11 +2359,10 @@
 		}
 		g_free(hb.name);
 	} else {
-		struct _list_account_buddies *ab = g_new0(struct _list_account_buddies, 1);
-		ab->account = account;
-		g_hash_table_foreach(purplebuddylist->buddies, find_acct_buddies, ab);
-		ret = ab->list;
-		g_free(ab);
+		GSList *list = NULL;
+		GHashTable *buddies = g_hash_table_lookup(buddies_cache, account);
+		g_hash_table_foreach(buddies, find_acct_buddies, &list);
+		ret = list;
 	}
 
 	return ret;
@@ -2931,6 +3001,16 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 										PURPLE_SUBTYPE_BLIST_NODE),
 						 purple_value_new(PURPLE_TYPE_STRING));
+
+	purple_signal_connect(purple_accounts_get_handle(), "account-created",
+			handle,
+			PURPLE_CALLBACK(purple_blist_buddies_cache_add_account),
+			NULL);
+
+	purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
+			handle,
+			PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account),
+			NULL);
 }
 
 void
@@ -2951,6 +3031,10 @@
 		node = next_node;
 	}
 	purplebuddylist->root = NULL;
-
+	
+	g_hash_table_destroy(purplebuddylist->buddies);
+	g_hash_table_destroy(buddies_cache);
+
+	purple_signals_disconnect_by_handle(purple_blist_get_handle());
 	purple_signals_unregister_by_instance(purple_blist_get_handle());
 }