# HG changeset patch # User hanzz@soc.pidgin.im # Date 1241165232 0 # Node ID 0e99b80b54c6baa06ecc6b73cb8af89b0871c9e3 # Parent 76a1598ecf1c85d3c52e03674dece0801d10f840 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 diff -r 76a1598ecf1c -r 0e99b80b54c6 COPYRIGHT --- a/COPYRIGHT Fri May 01 06:59:36 2009 +0000 +++ b/COPYRIGHT Fri May 01 08:07:12 2009 +0000 @@ -215,6 +215,7 @@ David Jedelsky Henry Jen Benjamin Kahn +Jan Kaluza Anders Kaseorg Praveen Karadakal Jaromír Karmazín diff -r 76a1598ecf1c -r 0e99b80b54c6 libpurple/blist.c --- 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()); } diff -r 76a1598ecf1c -r 0e99b80b54c6 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Fri May 01 06:59:36 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Fri May 01 08:07:12 2009 +0000 @@ -5122,58 +5122,43 @@ aim_ssi_cleanlist(od); { /* If not in server list then prune from local list */ - PurpleBlistNode *gnode, *cnode, *bnode; - PurpleBuddyList *blist; GSList *cur, *next; - + GSList *buddies = purple_find_buddies(account, NULL); + /* Buddies */ cur = NULL; - if ((blist = purple_get_blist()) != NULL) { - for (gnode = purple_blist_get_root(); gnode; - gnode = purple_blist_node_get_sibling_next(gnode)) { - const char *gname; - if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) - continue; - g = (PurpleGroup *)gnode; - gname = purple_group_get_name(g); - for (cnode = purple_blist_node_get_first_child(gnode); - cnode; - cnode = purple_blist_node_get_sibling_next(cnode)) { - if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) - continue; - for (bnode = purple_blist_node_get_first_child(cnode); - bnode; - bnode = purple_blist_node_get_sibling_next(bnode)) { - const char *bname; - if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) - continue; - b = (PurpleBuddy *)bnode; - bname = purple_buddy_get_name(b); - if (purple_buddy_get_account(b) == account) { - if (aim_ssi_itemlist_exists(od->ssi.local, bname)) { - /* If the buddy is an ICQ user then load his nickname */ - const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick"); - char *alias; - const char *balias; - if (servernick) - serv_got_alias(gc, bname, servernick); - - /* Store local alias on server */ - alias = aim_ssi_getalias(od->ssi.local, gname, bname); - balias = purple_buddy_get_local_buddy_alias(b); - if (!alias && balias && *balias) - aim_ssi_aliasbuddy(od, gname, bname, balias); - g_free(alias); - } else { - purple_debug_info("oscar", - "ssi: removing buddy %s from local list\n", bname); - /* We can't actually remove now because it will screw up our looping */ - cur = g_slist_prepend(cur, b); - } - } - } - } + + while(buddies) { + PurpleGroup *g; + const char *gname; + const char *bname; + + b = buddies->data; + g = purple_buddy_get_group(b); + gname = purple_group_get_name(g); + bname = purple_buddy_get_name(b); + + if (aim_ssi_itemlist_exists(od->ssi.local, bname)) { + /* If the buddy is an ICQ user then load his nickname */ + const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick"); + char *alias; + const char *balias; + if (servernick) + serv_got_alias(gc, bname, servernick); + + /* Store local alias on server */ + alias = aim_ssi_getalias(od->ssi.local, gname, bname); + balias = purple_buddy_get_local_buddy_alias(b); + if (!alias && balias && *balias) + aim_ssi_aliasbuddy(od, gname, bname, balias); + g_free(alias); + } else { + purple_debug_info("oscar", + "ssi: removing buddy %s from local list\n", bname); + /* We can't actually remove now because it will screw up our looping */ + cur = g_slist_prepend(cur, b); } + buddies = g_slist_delete_link(buddies, buddies); } while (cur != NULL) {