changeset 32155:5ffd5582f5fe

Pluck the performance improvement code needed for large IRC channels.
author Paul Aurich <paul@darkrain42.org>
date Wed, 22 Jun 2011 02:48:46 +0000
parents 54ec596b599f
children 1693114a2655
files ChangeLog ChangeLog.API libpurple/conversation.c libpurple/conversation.h libpurple/value.h pidgin/gtkconv.c
diffstat 6 files changed, 135 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jun 21 04:46:11 2011 +0000
+++ b/ChangeLog	Wed Jun 22 02:48:46 2011 +0000
@@ -2,6 +2,8 @@
 
 version 2.9.0 (06/23/2011):
 	Pidgin:
+	* Significantly improved performance of larger IRC channels (regression
+	  introduced in 2.8.0)
 	* Fix Conversation->Add on AIM and MSN.
 	* Entries in the chat user list are sorted properly again.  This was
 	  inadvertenly broken in 2.8.0.
--- a/ChangeLog.API	Tue Jun 21 04:46:11 2011 +0000
+++ b/ChangeLog.API	Wed Jun 22 02:48:46 2011 +0000
@@ -1,7 +1,16 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.9.0:
-	* No changes
+	libpurple:
+		Added:
+		* Hash table to PurpleConvChat struct, used to make
+		  purple_conv_chat_cb_find O(1).
+		* ui_data pointer to PurpleConvChatBuddy struct.
+		* deleting-chat-buddy signal (conversation signals)
+
+		Deprecated:
+		* purple_conv_chat_set_users
+		* PurpleConvChat in_room list
 
 version 2.8.0 (06/07/2011):
 	libpurple:
--- a/libpurple/conversation.c	Tue Jun 21 04:46:11 2011 +0000
+++ b/libpurple/conversation.c	Wed Jun 22 02:48:46 2011 +0000
@@ -70,6 +70,23 @@
 	g_free(hc);
 }
 
+static guint _purple_conversation_user_hash(gconstpointer data)
+{
+	const gchar *name = data;
+	gchar *collated;
+	guint hash;
+
+	collated = g_utf8_collate_key(name, -1);
+	hash     = g_str_hash(collated);
+	g_free(collated);
+	return hash;
+}
+
+static gboolean _purple_conversation_user_equal(gconstpointer a, gconstpointer b)
+{
+	return !g_utf8_collate(a, b);
+}
+
 void
 purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
 {
@@ -393,6 +410,8 @@
 
 		conv->u.chat = g_new0(PurpleConvChat, 1);
 		conv->u.chat->conv = conv;
+		conv->u.chat->users = g_hash_table_new_full(_purple_conversation_user_hash,
+				_purple_conversation_user_equal, g_free, NULL);
 		PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat);
 
 		chats = g_list_prepend(chats, conv);
@@ -547,6 +566,8 @@
 		conv->u.im = NULL;
 	}
 	else if (conv->type == PURPLE_CONV_TYPE_CHAT) {
+		g_hash_table_destroy(conv->u.chat->users);
+		conv->u.chat->users = NULL;
 
 		g_list_foreach(conv->u.chat->in_room, (GFunc)purple_conv_chat_cb_destroy, NULL);
 		g_list_free(conv->u.chat->in_room);
@@ -1677,9 +1698,9 @@
 
 		cbuddy = purple_conv_chat_cb_new(user, alias, flag);
 		cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
-		/* This seems dumb. Why should we set users thousands of times? */
-		purple_conv_chat_set_users(chat,
-				g_list_prepend(chat->in_room, cbuddy));
+
+		chat->in_room = g_list_prepend(chat->in_room, cbuddy);
+		g_hash_table_replace(chat->users, g_strdup(cbuddy->name), cbuddy);
 
 		cbuddies = g_list_prepend(cbuddies, cbuddy);
 
@@ -1771,8 +1792,9 @@
 	flags = purple_conv_chat_user_get_flags(chat, old_user);
 	cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
 	cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
-	purple_conv_chat_set_users(chat,
-		g_list_prepend(chat->in_room, cb));
+
+	chat->in_room = g_list_prepend(chat->in_room, cb);
+	g_hash_table_replace(chat->users, g_strdup(cb->name), cb);
 
 	if (ops != NULL && ops->chat_rename_user != NULL)
 		ops->chat_rename_user(conv, old_user, new_user, new_alias);
@@ -1780,8 +1802,8 @@
 	cb = purple_conv_chat_cb_find(chat, old_user);
 
 	if (cb) {
-		purple_conv_chat_set_users(chat,
-				g_list_remove(chat->in_room, cb));
+		chat->in_room = g_list_remove(chat->in_room, cb);
+		g_hash_table_remove(chat->users, cb->name);
 		purple_conv_chat_cb_destroy(cb);
 	}
 
@@ -1874,8 +1896,8 @@
 		cb = purple_conv_chat_cb_find(chat, user);
 
 		if (cb) {
-			purple_conv_chat_set_users(chat,
-					g_list_remove(chat->in_room, cb));
+			chat->in_room = g_list_remove(chat->in_room, cb);
+			g_hash_table_remove(chat->users, cb->name);
 			purple_conv_chat_cb_destroy(cb);
 		}
 
@@ -1955,8 +1977,11 @@
 		purple_conv_chat_cb_destroy(cb);
 	}
 
+	g_hash_table_remove_all(chat->users);
+	chat->users = NULL;
+
 	g_list_free(users);
-	purple_conv_chat_set_users(chat, NULL);
+	chat->in_room = NULL;
 }
 
 
@@ -2146,19 +2171,10 @@
 PurpleConvChatBuddy *
 purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name)
 {
-	GList *l;
-	PurpleConvChatBuddy *cb = NULL;
-
 	g_return_val_if_fail(chat != NULL, NULL);
 	g_return_val_if_fail(name != NULL, NULL);
 
-	for (l = purple_conv_chat_get_users(chat); l; l = l->next) {
-		cb = l->data;
-		if (!g_utf8_collate(cb->name, name))
-			return cb;
-	}
-
-	return NULL;
+	return g_hash_table_lookup(chat->users, name);
 }
 
 void
@@ -2167,6 +2183,9 @@
 	if (cb == NULL)
 		return;
 
+	purple_signal_emit(purple_conversations_get_handle(),
+			"deleting-chat-buddy", cb);
+
 	g_free(cb->alias);
 	g_free(cb->alias_key);
 	g_free(cb->name);
@@ -2573,6 +2592,11 @@
 						 purple_value_new(PURPLE_TYPE_STRING),
 						 purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "deleting-chat-buddy",
+						 purple_marshal_VOID__POINTER, NULL, 1,
+						 purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_CHATBUDDY));
+
 	purple_signal_register(handle, "chat-inviting-user",
 						 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/conversation.h	Tue Jun 21 04:46:11 2011 +0000
+++ b/libpurple/conversation.h	Wed Jun 22 02:48:46 2011 +0000
@@ -271,7 +271,9 @@
 {
 	PurpleConversation *conv;          /**< The parent conversation.      */
 
-	GList *in_room;                  /**< The users in the room.        */
+	GList *in_room;                  /**< The users in the room.
+	                                  *   @deprecated Will be removed in 3.0.0
+									  */
 	GList *ignored;                  /**< Ignored users.                */
 	char  *who;                      /**< The person who set the topic. */
 	char  *topic;                    /**< The topic.                    */
@@ -279,6 +281,9 @@
 	char *nick;                      /**< Your nick in this chat.       */
 
 	gboolean left;                   /**< We left the chat and kept the window open */
+	GHashTable *users;               /**< Hash table of the users in the room.
+	                                  *   @since 2.9.0
+	                                  */
 };
 
 /**
@@ -304,6 +309,7 @@
 	GHashTable *attributes;          /**< A hash table of attributes about the user, such as
                                     *   real name, user@host, etc.
                                     */
+	gpointer ui_data;                /** < The UI can put whatever it wants here. */
 };
 
 /**
@@ -1065,6 +1071,8 @@
  * @param users The list of users.
  *
  * @return The list passed.
+ *
+ * @deprecated This function will be removed in 3.0.0.  You shouldn't be using it anyway.
  */
 GList *purple_conv_chat_set_users(PurpleConvChat *chat, GList *users);
 
--- a/libpurple/value.h	Tue Jun 21 04:46:11 2011 +0000
+++ b/libpurple/value.h	Wed Jun 22 02:48:46 2011 +0000
@@ -79,7 +79,8 @@
 	PURPLE_SUBTYPE_XMLNODE,
 	PURPLE_SUBTYPE_USERINFO,
 	PURPLE_SUBTYPE_STORED_IMAGE,
-	PURPLE_SUBTYPE_CERTIFICATEPOOL
+	PURPLE_SUBTYPE_CERTIFICATEPOOL,
+	PURPLE_SUBTYPE_CHATBUDDY
 } PurpleSubType;
 
 /**
--- a/pidgin/gtkconv.c	Tue Jun 21 04:46:11 2011 +0000
+++ b/pidgin/gtkconv.c	Wed Jun 22 02:48:46 2011 +0000
@@ -3978,6 +3978,16 @@
 }
 
 static void
+deleting_chat_buddy_cb(PurpleConvChatBuddy *cb)
+{
+	if (cb->ui_data) {
+		GtkTreeRowReference *ref = cb->ui_data;
+		gtk_tree_row_reference_free(ref);
+		cb->ui_data = NULL;
+	}
+}
+
+static void
 add_chat_buddy_common(PurpleConversation *conv, PurpleConvChatBuddy *cb, const char *old_name)
 {
 	PidginConversation *gtkconv;
@@ -3985,7 +3995,9 @@
 	PurpleConvChat *chat;
 	PurpleConnection *gc;
 	PurplePluginProtocolInfo *prpl_info;
+	GtkTreeModel *tm;
 	GtkListStore *ls;
+	GtkTreePath *newpath;
 	const char *stock;
 	GtkTreeIter iter;
 	gboolean is_me = FALSE;
@@ -4006,7 +4018,8 @@
 	if (!gc || !(prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)))
 		return;
 
-	ls = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)));
+	tm = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
+	ls = GTK_LIST_STORE(tm);
 
 	stock = get_chat_buddy_status_icon(chat, name, flags);
 
@@ -4051,6 +4064,15 @@
 			CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
 			-1);
 
+	if (cb->ui_data) {
+		GtkTreeRowReference *ref = cb->ui_data;
+		gtk_tree_row_reference_free(ref);
+	}
+
+	newpath = gtk_tree_model_get_path(tm, &iter);
+	cb->ui_data = gtk_tree_row_reference_new(tm, newpath);
+	gtk_tree_path_free(newpath);
+
 	if (is_me && color)
 		gdk_color_free(color);
 	g_free(alias_key);
@@ -6108,6 +6130,28 @@
 	update_typing_message(gtkconv, NULL);
 }
 
+static gboolean get_iter_from_chatbuddy(PurpleConvChatBuddy *cb, GtkTreeIter *iter)
+{
+	GtkTreeRowReference *ref = cb->ui_data;
+	GtkTreePath *path;
+	GtkTreeModel *model;
+
+	if (!ref)
+		return FALSE;
+
+	if ((path = gtk_tree_row_reference_get_path(ref)) == NULL)
+		return FALSE;
+
+	model = gtk_tree_row_reference_get_model(ref);
+	if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), iter, path)) {
+		gtk_tree_path_free(path);
+		return FALSE;
+	}
+
+	gtk_tree_path_free(path);
+	return TRUE;
+}
+
 static void
 pidgin_conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
 {
@@ -6158,11 +6202,10 @@
 	PurpleConvChat *chat;
 	PidginConversation *gtkconv;
 	PidginChatPane *gtkchat;
-	PurpleConvChatBuddy *cbuddy;
+	PurpleConvChatBuddy *old_cbuddy, *new_cbuddy;
 	GtkTreeIter iter;
 	GtkTreeModel *model;
 	GtkTextTag *tag;
-	int f = 1;
 
 	chat    = PURPLE_CONV_CHAT(conv);
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -6173,20 +6216,13 @@
 	if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
 		return;
 
-	while (f != 0) {
-		char *val;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &val, -1);
-
-		if (!purple_utf8_strcasecmp(old_name, val)) {
-			gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
-			g_free(val);
-			break;
-		}
-
-		f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
-
-		g_free(val);
+	old_cbuddy = purple_conv_chat_cb_find(chat, old_name);
+	if (get_iter_from_chatbuddy(old_cbuddy, &iter)) {
+		GtkTreeRowReference *ref = old_cbuddy->ui_data;
+
+		gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+		gtk_tree_row_reference_free(ref);
+		old_cbuddy->ui_data = NULL;
 	}
 
 	if ((tag = get_buddy_tag(conv, old_name, 0, FALSE)))
@@ -6194,14 +6230,14 @@
 	if ((tag = get_buddy_tag(conv, old_name, PURPLE_MESSAGE_NICK, FALSE)))
 		g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL);
 
-	if (!purple_conv_chat_find_user(chat, old_name))
+	if (!old_cbuddy)
 		return;
 
 	g_return_if_fail(new_alias != NULL);
 
-	cbuddy = purple_conv_chat_cb_find(chat, new_name);
-
-	add_chat_buddy_common(conv, cbuddy, old_name);
+	new_cbuddy = purple_conv_chat_cb_find(chat, new_name);
+
+	add_chat_buddy_common(conv, new_cbuddy, old_name);
 }
 
 static void
@@ -6228,6 +6264,7 @@
 		model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list));
 
 		if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
+			/* XXX: Break? */
 			continue;
 
 		do {
@@ -6267,8 +6304,6 @@
 	PidginChatPane *gtkchat;
 	GtkTreeIter iter;
 	GtkTreeModel *model;
-	int f = 1;
-	char *alias = NULL;
 
 	chat    = PURPLE_CONV_CHAT(conv);
 	gtkconv = PIDGIN_CONVERSATION(conv);
@@ -6279,35 +6314,16 @@
 	if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
 		return;
 
-	while (f != 0) {
-		char *val;
-
-		gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &val, -1);
-
-		if (!purple_utf8_strcasecmp(user, val)) {
-			gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_ALIAS_COLUMN, &alias, -1);
-			gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
-			g_free(val);
-			break;
-		}
-
-		f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
-
-		g_free(val);
-	}
-
-	if (!purple_conv_chat_find_user(chat, user))
-	{
-		g_free(alias);
-		return;
-	}
-
-	g_return_if_fail(alias != NULL);
-
 	cbuddy = purple_conv_chat_cb_find(chat, user);
+	if (get_iter_from_chatbuddy(cbuddy, &iter)) {
+		GtkTreeRowReference *ref = cbuddy->ui_data;
+		gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+		gtk_tree_row_reference_free(ref);
+		cbuddy->ui_data = NULL;
+	}
+
 	if (cbuddy)
 		add_chat_buddy_common(conv, cbuddy, NULL);
-	g_free(alias);
 }
 
 gboolean
@@ -8088,6 +8104,9 @@
 	purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history",
 	                      handle, G_CALLBACK(clear_conversation_scrollback_cb), NULL);
 
+	purple_signal_connect(purple_conversations_get_handle(), "deleting-chat-buddy",
+	                      handle, G_CALLBACK(deleting_chat_buddy_cb), NULL);
+
 	purple_conversations_set_ui_ops(&conversation_ui_ops);
 
 	hidden_convwin = pidgin_conv_window_new();