Replace uses of Gee.TreeSet where used for sorting

Add new Geary.Iterable::to_sorted_list method that should be more
performant than adding elements to a sorted set and sorting one-by-one,
use that instead of a TreeSet.
This commit is contained in:
Michael Gratton 2019-11-20 12:46:36 +11:00
parent 37d2898b73
commit 89642c5283
5 changed files with 66 additions and 18 deletions

View file

@ -226,11 +226,11 @@ public class ConversationListStore : Gtk.ListStore {
// sort the conversations so the previews are fetched from the newest to the oldest, matching // sort the conversations so the previews are fetched from the newest to the oldest, matching
// the user experience // the user experience
Gee.TreeSet<Geary.App.Conversation> sorted_conversations = var sorted_conversations = Geary.traverse(
new Gee.TreeSet<Geary.App.Conversation>( this.conversations.read_only_view
Util.Email.compare_conversation_descending ).to_sorted_list(
); Util.Email.compare_conversation_descending
sorted_conversations.add_all(this.conversations.read_only_view); );
foreach (Geary.App.Conversation conversation in sorted_conversations) { foreach (Geary.App.Conversation conversation in sorted_conversations) {
// find oldest unread message for the preview // find oldest unread message for the preview
Geary.Email? need_preview = null; Geary.Email? need_preview = null;

View file

@ -328,8 +328,7 @@ public class Geary.Imap.MessageSet : BaseObject {
private static int64[] seq_array_to_int64(Gee.Collection<SequenceNumber> seq_nums) { private static int64[] seq_array_to_int64(Gee.Collection<SequenceNumber> seq_nums) {
// guarantee sorted (to maximum finding runs in build_sparse_range()) // guarantee sorted (to maximum finding runs in build_sparse_range())
Gee.TreeSet<SequenceNumber> sorted = new Gee.TreeSet<SequenceNumber>(); var sorted = traverse(seq_nums).to_sorted_list((a,b) => a.compare_to(b));
sorted.add_all(seq_nums);
// build sorted array // build sorted array
int64[] ret = new int64[sorted.size]; int64[] ret = new int64[sorted.size];
@ -342,8 +341,7 @@ public class Geary.Imap.MessageSet : BaseObject {
private static int64[] uid_array_to_int64(Gee.Collection<UID> msg_uids) { private static int64[] uid_array_to_int64(Gee.Collection<UID> msg_uids) {
// guarantee sorted (to maximize finding runs in build_sparse_range()) // guarantee sorted (to maximize finding runs in build_sparse_range())
Gee.TreeSet<UID> sorted = new Gee.TreeSet<UID>(); var sorted = traverse(msg_uids).to_sorted_list((a,b) => a.compare_to(b));
sorted.add_all(msg_uids);
// build sorted array // build sorted array
int64[] ret = new int64[sorted.size]; int64[] ret = new int64[sorted.size];

View file

@ -59,6 +59,9 @@ public class Geary.RFC822.MailboxAddresses :
private Gee.List<MailboxAddress> addrs = new Gee.ArrayList<MailboxAddress>(); private Gee.List<MailboxAddress> addrs = new Gee.ArrayList<MailboxAddress>();
private bool hash_cached = false;
private uint hash_value = 0;
public MailboxAddresses(Gee.Collection<MailboxAddress>? addrs = null) { public MailboxAddresses(Gee.Collection<MailboxAddress>? addrs = null) {
if (addrs != null) { if (addrs != null) {
@ -170,17 +173,22 @@ public class Geary.RFC822.MailboxAddresses :
} }
public uint hash() { public uint hash() {
// create sorted set to ensure ordering no matter the list's order if (!this.hash_cached) {
Gee.TreeSet<string> sorted_addresses = traverse<RFC822.MailboxAddress>(addrs) // Sort the addresses to ensure a stable hash
.map<string>(m => m.address) var sorted_addresses = traverse<RFC822.MailboxAddress>(addrs)
.to_tree_set(String.stri_cmp); .map<string>(m => m.address)
.to_sorted_list(String.stri_cmp);
// xor all strings in sorted order // xor all strings in sorted order
uint xor = 0; uint xor = 0;
foreach (string address in sorted_addresses) foreach (string address in sorted_addresses) {
xor ^= address.hash(); xor ^= address.hash();
}
this.hash_value = xor;
this.hash_cached = true;
}
return xor; return this.hash_value;
} }
public bool equal_to(MailboxAddresses other) { public bool equal_to(MailboxAddresses other) {

View file

@ -225,6 +225,19 @@ public class Geary.Iterable<G> : BaseObject {
return (Gee.ArrayList<G>) add_all_to(new Gee.ArrayList<G>((owned) equal_func)); return (Gee.ArrayList<G>) add_all_to(new Gee.ArrayList<G>((owned) equal_func));
} }
/**
* Returns a new list containing all elements, sorted.
*
* The ordering is applied after adding all elements to the list,
* so as to minimise computational overhead.
*/
public Gee.ArrayList<G> to_sorted_list(owned GLib.CompareDataFunc<G> comparator,
owned Gee.EqualDataFunc<G>? equal_func = null) {
var list = to_array_list((owned) equal_func);
list.sort((owned) comparator);
return list;
}
/** Returns a new linked list containing all elements. */ /** Returns a new linked list containing all elements. */
public Gee.LinkedList<G> to_linked_list(owned Gee.EqualDataFunc<G>? equal_func = null) { public Gee.LinkedList<G> to_linked_list(owned Gee.EqualDataFunc<G>? equal_func = null) {
return (Gee.LinkedList<G>) add_all_to(new Gee.LinkedList<G>((owned) equal_func)); return (Gee.LinkedList<G>) add_all_to(new Gee.LinkedList<G>((owned) equal_func));

View file

@ -13,6 +13,7 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
add_test("from_rfc822_string_quoted", from_rfc822_string_quoted); add_test("from_rfc822_string_quoted", from_rfc822_string_quoted);
add_test("to_rfc822_string", to_rfc822_string); add_test("to_rfc822_string", to_rfc822_string);
add_test("equal_to", equal_to); add_test("equal_to", equal_to);
add_test("hash", hash);
} }
public void from_rfc822_string_encoded() throws Error { public void from_rfc822_string_encoded() throws Error {
@ -82,6 +83,34 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
); );
} }
public void hash() throws Error {
var mailboxes_a = new_addreses({ "test1@example.com" });
var mailboxes_b = new_addreses({ "test1@example.com" });
var mailboxes_c = new_addreses({ "test2@example.com" });
assert_true(mailboxes_a.hash() == mailboxes_a.hash());
assert_true(mailboxes_a.hash() == mailboxes_b.hash());
assert_false(mailboxes_a.hash() == mailboxes_c.hash());
assert_true(
new_addreses({ "test1@example.com", "test2@example.com" }).hash() ==
new_addreses({ "test1@example.com", "test2@example.com" }).hash()
);
assert_true(
new_addreses({ "test1@example.com", "test2@example.com" }).hash() ==
new_addreses({ "test2@example.com", "test1@example.com" }).hash()
);
assert_false(
new_addreses({ "test1@example.com", "test2@example.com" }).hash() ==
new_addreses({ "test1@example.com" }).hash()
);
assert_false(
new_addreses({ "test1@example.com", "test2@example.com" }).hash() ==
new_addreses({ "test1@example.com", "test3@example.com" }).hash()
);
}
private MailboxAddresses new_addreses(string[] address_strings) { private MailboxAddresses new_addreses(string[] address_strings) {
Gee.List<MailboxAddress> addresses = new Gee.LinkedList<MailboxAddress>(); Gee.List<MailboxAddress> addresses = new Gee.LinkedList<MailboxAddress>();
foreach (string address in address_strings) { foreach (string address in address_strings) {