changeset 28033:ce3bc26aa8cd

More efficient purple_find_conversation_with_account. Closes #9703. Patch mostly from Aman "tmm1" Gupta; I added the g_list_prepend calls. The functionality is going to get whacky when dealing with chats that share the same name ("MSN Chat"), but purple_find_conversation_with_account doesn't work for those in any sense of the word 'work', so... committer: Paul Aurich <paul@darkrain42.org>
author aman@tmm1.net
date Mon, 27 Jul 2009 04:12:06 +0000
parents ab0dace4c688
children 2cb2da84663d
files ChangeLog ChangeLog.API libpurple/conversation.c
diffstat 3 files changed, 78 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jul 27 03:33:32 2009 +0000
+++ b/ChangeLog	Mon Jul 27 04:12:06 2009 +0000
@@ -36,7 +36,7 @@
 	  (Aman Gupta)
 	* Don't fork a DNS resolver process to resolve IP addresses.  (Aman Gupta)
 	* Better handling of corrupt certificates in the TLS Peers cache.
-	* More efficient purple_find_buddies() and purple_find_group() functions.
+	* More efficient buddy list and conversation search functions.
 	  (Jan Kaluza and Aman Gupta)
 	* Internationalized Domain Names are supported when libpurple is compiled
 	  against the GNU IDN library.
--- a/ChangeLog.API	Mon Jul 27 03:33:32 2009 +0000
+++ b/ChangeLog.API	Mon Jul 27 04:12:06 2009 +0000
@@ -85,6 +85,8 @@
 		* purple_find_buddies is now more efficient in the case where
 		  it is enumerating all the buddies for an account.
 		* purple_find_group is now more efficient for large numbers of groups.
+		* purple_find_conversation_with_account is more efficient for large
+		  numbers of concurrent conversations.
 		* All DNS routines support internationalized domain names (IDNs) when
 		  libpurple is compiled with GNU libidn.
 		* status is set before emitting signals in purple_xfer_set_status.
--- a/libpurple/conversation.c	Mon Jul 27 03:33:32 2009 +0000
+++ b/libpurple/conversation.c	Mon Jul 27 04:12:06 2009 +0000
@@ -40,6 +40,36 @@
 static GList *chats = NULL;
 static PurpleConversationUiOps *default_ops = NULL;
 
+/**
+ * A hash table used for efficient lookups of conversations by name.
+ * struct _purple_hconv => PurpleConversation*
+ */
+static GHashTable *conversation_cache = NULL;
+
+struct _purple_hconv {
+	PurpleConversationType type;
+	char *name;
+	const PurpleAccount *account;
+};
+
+static guint _purple_conversations_hconv_hash(struct _purple_hconv *hc)
+{
+	return g_str_hash(hc->name) ^ hc->type ^ g_direct_hash(hc->account);
+}
+
+static guint _purple_conversations_hconv_equal(struct _purple_hconv *hc1, struct _purple_hconv *hc2)
+{
+	return (hc1->type == hc2->type &&
+	        hc1->account == hc2->account &&
+	        g_str_equal(hc1->name, hc2->name));
+}
+
+static void _purple_conversations_hconv_free_key(struct _purple_hconv *hc)
+{
+	g_free(hc->name);
+	g_free(hc);
+}
+
 void
 purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
 {
@@ -287,6 +317,7 @@
 	PurpleConversation *conv;
 	PurpleConnection *gc;
 	PurpleConversationUiOps *ops;
+	struct _purple_hconv *hc;
 
 	g_return_val_if_fail(type    != PURPLE_CONV_TYPE_UNKNOWN, NULL);
 	g_return_val_if_fail(account != NULL, NULL);
@@ -342,7 +373,7 @@
 		conv->u.im->conv = conv;
 		PURPLE_DBUS_REGISTER_POINTER(conv->u.im, PurpleConvIm);
 
-		ims = g_list_append(ims, conv);
+		ims = g_list_prepend(ims, conv);
 		if ((icon = purple_buddy_icons_find(account, name)))
 		{
 			purple_conv_im_set_icon(conv->u.im, icon);
@@ -364,7 +395,7 @@
 		conv->u.chat->conv = conv;
 		PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat);
 
-		chats = g_list_append(chats, conv);
+		chats = g_list_prepend(chats, conv);
 
 		if ((disp = purple_connection_get_display_name(account->gc)))
 			purple_conv_chat_set_nick(conv->u.chat, disp);
@@ -379,7 +410,14 @@
 		}
 	}
 
-	conversations = g_list_append(conversations, conv);
+	conversations = g_list_prepend(conversations, conv);
+
+	hc = g_new(struct _purple_hconv, 1);
+	hc->name = g_strdup(purple_normalize(account, conv->name));
+	hc->account = account;
+	hc->type = type;
+
+	g_hash_table_insert(conversation_cache, hc, conv);
 
 	/* Auto-set the title. */
 	purple_conversation_autoset_title(conv);
@@ -405,6 +443,7 @@
 	PurpleConversationUiOps *ops;
 	PurpleConnection *gc;
 	const char *name;
+	struct _purple_hconv hc;
 
 	g_return_if_fail(conv != NULL);
 
@@ -481,6 +520,12 @@
 	else if(conv->type==PURPLE_CONV_TYPE_CHAT)
 		chats = g_list_remove(chats, conv);
 
+	hc.name = (gchar *)purple_normalize(conv->account, conv->name);
+	hc.account = conv->account;
+	hc.type = conv->type;
+
+	g_hash_table_remove(conversation_cache, &hc);
+
 	purple_signal_emit(purple_conversations_get_handle(),
 					 "deleting-conversation", conv);
 
@@ -707,10 +752,20 @@
 void
 purple_conversation_set_name(PurpleConversation *conv, const char *name)
 {
+	struct _purple_hconv *hc;
 	g_return_if_fail(conv != NULL);
 
+	hc = g_new(struct _purple_hconv, 1);
+	hc->type = conv->type;
+	hc->account = conv->account;
+	hc->name = (gchar *)purple_normalize(conv->account, conv->name);
+
+	g_hash_table_remove(conversation_cache, hc);
 	g_free(conv->name);
+
 	conv->name = g_strdup(name);
+	hc->name = g_strdup(purple_normalize(conv->account, conv->name));
+	g_hash_table_insert(conversation_cache, hc, conv);
 
 	purple_conversation_autoset_title(conv);
 }
@@ -819,43 +874,31 @@
 									const PurpleAccount *account)
 {
 	PurpleConversation *c = NULL;
-	gchar *name1;
-	const gchar *name2;
-	GList *cnv;
+	struct _purple_hconv hc;
 
 	g_return_val_if_fail(name != NULL, NULL);
 
+	hc.name = (gchar *)purple_normalize(account, name);
+	hc.account = account;
+	hc.type = type;
+
 	switch (type) {
 		case PURPLE_CONV_TYPE_IM:
-			cnv = purple_get_ims();
-			break;
 		case PURPLE_CONV_TYPE_CHAT:
-			cnv = purple_get_chats();
+			c = g_hash_table_lookup(conversation_cache, &hc);
 			break;
 		case PURPLE_CONV_TYPE_ANY:
-			cnv = purple_get_conversations();
+			hc.type = PURPLE_CONV_TYPE_IM;
+			c = g_hash_table_lookup(conversation_cache, &hc);
+			if (!c) {
+				hc.type = PURPLE_CONV_TYPE_CHAT;
+				c = g_hash_table_lookup(conversation_cache, &hc);
+			}
 			break;
 		default:
 			g_return_val_if_reached(NULL);
 	}
 
-	name1 = g_strdup(purple_normalize(account, name));
-
-	for (; cnv != NULL; cnv = cnv->next) {
-		c = (PurpleConversation *)cnv->data;
-		name2 = purple_normalize(account, purple_conversation_get_name(c));
-
-		if ((account == purple_conversation_get_account(c)) &&
-				!purple_utf8_strcasecmp(name1, name2)) {
-
-			break;
-		}
-
-		c = NULL;
-	}
-
-	g_free(name1);
-
 	return c;
 }
 
@@ -2215,6 +2258,10 @@
 {
 	void *handle = purple_conversations_get_handle();
 
+	conversation_cache = g_hash_table_new_full((GHashFunc)_purple_conversations_hconv_hash,
+						(GEqualFunc)_purple_conversations_hconv_equal,
+						(GDestroyNotify)_purple_conversations_hconv_free_key, NULL);
+
 	/**********************************************************************
 	 * Register preferences
 	 **********************************************************************/
@@ -2504,6 +2551,7 @@
 {
 	while (conversations)
 		purple_conversation_destroy((PurpleConversation*)conversations->data);
+	g_hash_table_destroy(conversation_cache);
 	purple_signals_unregister_by_instance(purple_conversations_get_handle());
 }