[folks] folks, backends: use "small sets" instead of hash sets most of the time



commit ab22ced5b97ef63ad206bdd6e3a9270969fb6157
Author: Simon McVittie <simon mcvittie collabora co uk>
Date:   Thu Apr 4 13:00:53 2013 +0100

    folks, backends: use "small sets" instead of hash sets most of the time
    
    Notable exceptions are:
    
    * sets of personas that are, or might be, the entire set from a backend
      (possible future refinement: use a SmallSet if there aren't many)
    
    * sets of potentially many Individuals (likewise)
    
    * the object cache (potentially pretty large)
    
    * debug stuff (not relevant for performance)
    
    * the set of IM addresses being matched (we want to keep this O(1),
      but it should be a GHashTable)
    
    This speeds up tests/eds/perf by around 5%.
    
    Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687161
    Reviewed-by: Philip Withnall <philip tecnocode co uk>
    [squashed in responses to review -smcv]
    Signed-off-by: Simon McVittie <simon mcvittie collabora co uk>

 backends/eds/lib/Makefile.am                       |    1 +
 backends/eds/lib/edsf-persona-store.vala           |    6 +-
 backends/eds/lib/edsf-persona.vala                 |   76 ++++++++++----------
 backends/key-file/Makefile.am                      |    1 +
 backends/key-file/kf-persona-store.vala            |    4 +-
 backends/key-file/kf-persona.vala                  |    6 +-
 backends/libsocialweb/lib/Makefile.am              |    1 +
 backends/libsocialweb/lib/swf-persona.vala         |    4 +-
 backends/ofono/Makefile.am                         |    1 +
 backends/ofono/ofono-persona.vala                  |    8 +-
 backends/telepathy/lib/Makefile.am                 |    1 +
 .../telepathy/lib/tpf-persona-store-cache.vala     |    8 +-
 backends/telepathy/lib/tpf-persona-store.vala      |   22 +++---
 backends/telepathy/lib/tpf-persona.vala            |   26 ++++----
 backends/tracker/lib/Makefile.am                   |    1 +
 backends/tracker/lib/trf-persona-store.vala        |   35 +++++----
 backends/tracker/lib/trf-persona.vala              |   34 +++++-----
 folks/Makefile.am                                  |    1 +
 folks/anti-linkable.vala                           |    4 +-
 folks/backend-store.vala                           |    8 +-
 folks/individual-aggregator.vala                   |   32 +++++----
 folks/individual.vala                              |   63 ++++++++--------
 folks/potential-match.vala                         |    3 +-
 23 files changed, 178 insertions(+), 168 deletions(-)
---
diff --git a/backends/eds/lib/Makefile.am b/backends/eds/lib/Makefile.am
index c5a969c..8f00006 100644
--- a/backends/eds/lib/Makefile.am
+++ b/backends/eds/lib/Makefile.am
@@ -47,6 +47,7 @@ libfolks_eds_la_VALAFLAGS = \
        --vapidir=. \
        --vapidir=$(top_srcdir)/folks \
        --pkg folks \
+       --pkg folks-generics \
        --pkg folks-internal \
        --pkg gee-0.8 \
        --pkg gio-2.0 \
diff --git a/backends/eds/lib/edsf-persona-store.vala b/backends/eds/lib/edsf-persona-store.vala
index a2d84d9..3ff352e 100644
--- a/backends/eds/lib/edsf-persona-store.vala
+++ b/backends/eds/lib/edsf-persona-store.vala
@@ -417,7 +417,7 @@ public class Edsf.PersonaStore : Folks.PersonaStore
       unowned string k;
       unowned Value? _v;
       bool is_fav = false; // Remember this for _set_contact_groups.
-      Set<string> groups = new HashSet<string> (); // For _set_is_favourite
+      Set<string> groups = new SmallSet<string> (); // For _set_is_favourite
 
       while (iter.next (out k, out _v) == true)
         {
@@ -585,7 +585,7 @@ public class Edsf.PersonaStore : Folks.PersonaStore
                       contact.set (E.Contact.field_id ("id"), added_uid);
                       persona = new Persona (this, contact);
                       this._personas.set (persona.iid, persona);
-                      var added_personas = new HashSet<Persona> ();
+                      var added_personas = new SmallSet<Persona> ();
                       added_personas.add (persona);
                       this._emit_personas_changed (added_personas, null);
 
@@ -853,7 +853,7 @@ public class Edsf.PersonaStore : Folks.PersonaStore
               Internal.profiling_point ("got supported fields in " +
                   "Edsf.PersonaStore (ID: %s)", this.id);
 
-              var prop_set = new HashSet<string> ();
+              var prop_set = new SmallSet<string> ();
 
               /* We get a comma-separated list of fields back. */
               if (supported_fields != null)
diff --git a/backends/eds/lib/edsf-persona.vala b/backends/eds/lib/edsf-persona.vala
index 294c23a..eb10f79 100644
--- a/backends/eds/lib/edsf-persona.vala
+++ b/backends/eds/lib/edsf-persona.vala
@@ -195,7 +195,7 @@ public class Edsf.Persona : Folks.Persona,
 
   /* NOTE: Other properties support lazy initialisation, but local-ids doesn't
    * as it's a linkable property, so always has to be loaded anyway. */
-  private HashSet<string> _local_ids = new HashSet<string> ();
+  private SmallSet<string> _local_ids = new SmallSet<string> ();
   private Set<string> _local_ids_ro;
 
   /**
@@ -249,7 +249,7 @@ public class Edsf.Persona : Folks.Persona,
       yield ((Edsf.PersonaStore) this.store)._set_location (this, location);
     }
 
-  private HashSet<PostalAddressFieldDetails>? _postal_addresses = null;
+  private SmallSet<PostalAddressFieldDetails>? _postal_addresses = null;
   private Set<PostalAddressFieldDetails>? _postal_addresses_ro = null;
 
   /**
@@ -282,7 +282,7 @@ public class Edsf.Persona : Folks.Persona,
           postal_addresses);
     }
 
-  private HashSet<PhoneFieldDetails>? _phone_numbers = null;
+  private SmallSet<PhoneFieldDetails>? _phone_numbers = null;
   private Set<PhoneFieldDetails>? _phone_numbers_ro = null;
 
   /**
@@ -312,8 +312,8 @@ public class Edsf.Persona : Folks.Persona,
       yield ((Edsf.PersonaStore) this.store)._set_phones (this, phone_numbers);
     }
 
-  private HashSet<EmailFieldDetails>? _email_addresses =
-          new HashSet<EmailFieldDetails> (
+  private SmallSet<EmailFieldDetails>? _email_addresses =
+          new SmallSet<EmailFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
   private Set<EmailFieldDetails> _email_addresses_ro;
@@ -342,7 +342,7 @@ public class Edsf.Persona : Folks.Persona,
           email_addresses);
     }
 
-  private HashSet<NoteFieldDetails>? _notes = null;
+  private SmallSet<NoteFieldDetails>? _notes = null;
   private Set<NoteFieldDetails>? _notes_ro = null;
 
   /**
@@ -518,7 +518,7 @@ public class Edsf.Persona : Folks.Persona,
       yield ((Edsf.PersonaStore) this.store)._set_gender (this, gender);
     }
 
-  private HashSet<UrlFieldDetails>? _urls = null;
+  private SmallSet<UrlFieldDetails>? _urls = null;
   private Set<UrlFieldDetails>? _urls_ro = null;
   /**
    * { inheritDoc}
@@ -576,7 +576,7 @@ public class Edsf.Persona : Folks.Persona,
       yield ((Edsf.PersonaStore) this.store)._set_im_fds (this, im_addresses);
     }
 
-  private HashSet<string>? _groups = null;
+  private SmallSet<string>? _groups = null;
   private Set<string>? _groups_ro = null;
 
   /**
@@ -614,7 +614,7 @@ public class Edsf.Persona : Folks.Persona,
         }
 
       /* Replace the current set of groups with a modified one. */
-      var new_groups = new HashSet<string> ();
+      var new_groups = new SmallSet<string> ();
       foreach (var category_name in this.groups)
         {
           new_groups.add (category_name);
@@ -697,7 +697,7 @@ public class Edsf.Persona : Folks.Persona,
           return;
         }
 
-      HashSet<string> new_system_groups = new HashSet<string> ();
+      var new_system_groups = new SmallSet<string> ();
       foreach (var sg in this._system_groups)
         {
           if (sg == GOOGLE_PERSONAL_GROUP_NAME && !in_personal)
@@ -759,7 +759,7 @@ public class Edsf.Persona : Folks.Persona,
           bday);
     }
 
-  private HashSet<RoleFieldDetails>? _roles = null;
+  private SmallSet<RoleFieldDetails>? _roles = null;
   private Set<RoleFieldDetails>? _roles_ro = null;
 
   /**
@@ -823,7 +823,7 @@ public class Edsf.Persona : Folks.Persona,
           is_favourite);
     }
 
-  private HashSet<string> _anti_links;
+  private SmallSet<string> _anti_links;
   private Set<string> _anti_links_ro;
 
   /**
@@ -849,7 +849,7 @@ public class Edsf.Persona : Folks.Persona,
       yield ((Edsf.PersonaStore) this.store)._set_anti_links (this, anti_links);
     }
 
-  private HashSet<string>? _system_groups = null;
+  private SmallSet<string>? _system_groups = null;
   private Set<string>? _system_groups_ro = null;
   private bool _in_google_personal_group;
 
@@ -960,27 +960,27 @@ public class Edsf.Persona : Folks.Persona,
       debug ("Creating new Edsf.Persona with IID '%s'", this.iid);
 
       this._gender = Gender.UNSPECIFIED;
-      this._phone_numbers = new HashSet<PhoneFieldDetails> (
+      this._phone_numbers = new SmallSet<PhoneFieldDetails> (
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
       this._phone_numbers_ro = this._phone_numbers.read_only_view;
-      this._email_addresses = new HashSet<EmailFieldDetails> (
+      this._email_addresses = new SmallSet<EmailFieldDetails> (
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
       this._email_addresses_ro = this._email_addresses.read_only_view;
-      this._notes = new HashSet<NoteFieldDetails> (
+      this._notes = new SmallSet<NoteFieldDetails> (
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
       this._notes_ro = this._notes.read_only_view;
-      this._urls = new HashSet<UrlFieldDetails> (
+      this._urls = new SmallSet<UrlFieldDetails> (
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
       this._urls_ro = this._urls.read_only_view;
-      this._postal_addresses = new HashSet<PostalAddressFieldDetails> (
+      this._postal_addresses = new SmallSet<PostalAddressFieldDetails> (
            AbstractFieldDetails<PostalAddress>.hash_static,
            AbstractFieldDetails<PostalAddress>.equal_static);
       this._postal_addresses_ro = this._postal_addresses.read_only_view;
-      this._local_ids = new HashSet<string> ();
+      this._local_ids = new SmallSet<string> ();
       this._local_ids_ro = this._local_ids.read_only_view;
       this._location = null;
       this._web_service_addresses =
@@ -988,13 +988,13 @@ public class Edsf.Persona : Folks.Persona,
           null, null, AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       this._email_addresses_ro = this._email_addresses.read_only_view;
-      this._groups = new HashSet<string> ();
+      this._groups = new SmallSet<string> ();
       this._groups_ro = this._groups.read_only_view;
-      this._roles = new HashSet<RoleFieldDetails> (
+      this._roles = new SmallSet<RoleFieldDetails> (
            AbstractFieldDetails<Role>.hash_static,
            AbstractFieldDetails<Role>.equal_static);
       this._roles_ro = this._roles.read_only_view;
-      this._anti_links = new HashSet<string> ();
+      this._anti_links = new SmallSet<string> ();
       this._anti_links_ro = this._anti_links.read_only_view;
 
       this._update (this._contact);
@@ -1189,13 +1189,13 @@ public class Edsf.Persona : Folks.Persona,
         }
       else if (this._roles == null)
         {
-          this._roles = new HashSet<RoleFieldDetails> (
+          this._roles = new SmallSet<RoleFieldDetails> (
               AbstractFieldDetails<Role>.hash_static,
               AbstractFieldDetails<Role>.equal_static);
           this._roles_ro = this._roles.read_only_view;
         }
 
-      var new_roles = new HashSet<RoleFieldDetails> (
+      var new_roles = new SmallSet<RoleFieldDetails> (
           AbstractFieldDetails<Role>.hash_static,
           AbstractFieldDetails<Role>.equal_static);
 
@@ -1351,7 +1351,7 @@ public class Edsf.Persona : Folks.Persona,
 
   private void _update_emails (bool emit_notification = true)
     {
-      var new_email_addresses = new HashSet<EmailFieldDetails> (
+      var new_email_addresses = new SmallSet<EmailFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
 
@@ -1395,13 +1395,13 @@ public class Edsf.Persona : Folks.Persona,
         }
       else if (this._notes == null)
         {
-          this._notes = new HashSet<NoteFieldDetails> (
+          this._notes = new SmallSet<NoteFieldDetails> (
               AbstractFieldDetails<string>.hash_static,
               AbstractFieldDetails<string>.equal_static);
           this._notes_ro = this._notes.read_only_view;
         }
 
-      var new_notes = new HashSet<NoteFieldDetails> (
+      var new_notes = new SmallSet<NoteFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
 
@@ -1578,13 +1578,13 @@ public class Edsf.Persona : Folks.Persona,
         }
       else if (this._urls == null)
         {
-          this._urls = new HashSet<UrlFieldDetails> (
+          this._urls = new SmallSet<UrlFieldDetails> (
               AbstractFieldDetails<string>.hash_static,
               AbstractFieldDetails<string>.equal_static);
           this._urls_ro = this._urls.read_only_view;
         }
 
-      var new_urls = new HashSet<UrlFieldDetails> (
+      var new_urls = new SmallSet<UrlFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
 
@@ -1747,7 +1747,7 @@ public class Edsf.Persona : Folks.Persona,
 
       var category_names =
           this._contact.get<GLib.List<string>> (E.ContactField.CATEGORY_LIST);
-      var new_categories = new HashSet<string> ();
+      var new_categories = new SmallSet<string> ();
       bool any_added_categories = false;
 
       foreach (var category_name in category_names)
@@ -1807,7 +1807,7 @@ public class Edsf.Persona : Folks.Persona,
           if (attr != null)
             {
               unowned GLib.List<string> system_group_ids = attr.get_values();
-              var new_sysgroups = new HashSet<string> ();
+              var new_sysgroups = new SmallSet<string> ();
               bool any_added_sysgroups = false;
 
               foreach (var system_group_id in system_group_ids)
@@ -1851,7 +1851,7 @@ public class Edsf.Persona : Folks.Persona,
             }
           else
             {
-              this._system_groups = new HashSet<string>();
+              this._system_groups = new SmallSet<string>();
               this._system_groups_ro = this._system_groups.read_only_view;
             }
         }
@@ -1947,13 +1947,13 @@ public class Edsf.Persona : Folks.Persona,
         }
       else if (this._phone_numbers == null)
         {
-          this._phone_numbers = new HashSet<PhoneFieldDetails> (
+          this._phone_numbers = new SmallSet<PhoneFieldDetails> (
               AbstractFieldDetails<string>.hash_static,
               AbstractFieldDetails<string>.equal_static);
           this._phone_numbers_ro = this._phone_numbers.read_only_view;
         }
 
-      var new_phone_numbers = new HashSet<PhoneFieldDetails> (
+      var new_phone_numbers = new SmallSet<PhoneFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
 
@@ -2057,13 +2057,13 @@ public class Edsf.Persona : Folks.Persona,
         }
       else if (this._postal_addresses == null)
         {
-          this._postal_addresses = new HashSet<PostalAddressFieldDetails> (
+          this._postal_addresses = new SmallSet<PostalAddressFieldDetails> (
               AbstractFieldDetails<PostalAddress>.hash_static,
               AbstractFieldDetails<PostalAddress>.equal_static);
           this._postal_addresses_ro = this._postal_addresses.read_only_view;
         }
 
-      var new_postal_addresses = new HashSet<PostalAddressFieldDetails> (
+      var new_postal_addresses = new SmallSet<PostalAddressFieldDetails> (
           AbstractFieldDetails<PostalAddress>.hash_static,
           AbstractFieldDetails<PostalAddress>.equal_static);
 
@@ -2096,7 +2096,7 @@ public class Edsf.Persona : Folks.Persona,
 
   private void _update_local_ids ()
     {
-      var new_local_ids = new HashSet<string> ();
+      var new_local_ids = new SmallSet<string> ();
 
       var ids = this.contact.get_attribute ("X-FOLKS-CONTACTS-IDS");
       if (ids != null)
@@ -2181,7 +2181,7 @@ public class Edsf.Persona : Folks.Persona,
 
   private void _update_anti_links ()
     {
-      var new_anti_links = new HashSet<string> ();
+      var new_anti_links = new SmallSet<string> ();
 
       var vcard = (E.VCard) this.contact;
       foreach (unowned E.VCardAttribute attr in vcard.get_attributes ())
diff --git a/backends/key-file/Makefile.am b/backends/key-file/Makefile.am
index a50110f..c3a372f 100644
--- a/backends/key-file/Makefile.am
+++ b/backends/key-file/Makefile.am
@@ -16,6 +16,7 @@ key_file_la_VALAFLAGS = \
        --vapidir=. \
        --vapidir=$(top_srcdir)/folks \
        --pkg folks \
+       --pkg folks-generics \
        --pkg folks-internal \
        --pkg gee-0.8 \
        --pkg gio-2.0 \
diff --git a/backends/key-file/kf-persona-store.vala b/backends/key-file/kf-persona-store.vala
index cfe1061..752343a 100644
--- a/backends/key-file/kf-persona-store.vala
+++ b/backends/key-file/kf-persona-store.vala
@@ -346,7 +346,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
           yield this.save_key_file ();
 
           /* Signal the removal of the Persona */
-          var personas = new HashSet<Folks.Persona> ();
+          var personas = new SmallSet<Folks.Persona> ();
           personas.add (persona);
 
           this._emit_personas_changed (null, personas);
@@ -423,7 +423,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
         }
 
       /* FIXME: GroupDetails.ChangeReason is not the right enum to use here */
-      var personas = new HashSet<Persona> ();
+      var personas = new SmallSet<Persona> ();
       personas.add (persona);
 
       this._emit_personas_changed (personas, null);
diff --git a/backends/key-file/kf-persona.vala b/backends/key-file/kf-persona.vala
index 950bb91..3a0b98a 100644
--- a/backends/key-file/kf-persona.vala
+++ b/backends/key-file/kf-persona.vala
@@ -151,7 +151,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
       foreach (var protocol2 in im_addresses.get_keys ())
         {
           var addresses = im_addresses.get (protocol2);
-          var normalised_addresses = new HashSet<string> ();
+          var normalised_addresses = new SmallSet<string> ();
 
           foreach (var im_fd in addresses)
             {
@@ -256,7 +256,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
       this.notify_property ("web-service-addresses");
     }
 
-  private HashSet<string> _anti_links;
+  private SmallSet<string> _anti_links;
   private Set<string> _anti_links_ro;
 
   /**
@@ -331,7 +331,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
         new HashMultiMap<string, WebServiceFieldDetails> (
           null, null, AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
-      this._anti_links = new HashSet<string> ();
+      this._anti_links = new SmallSet<string> ();
       this._anti_links_ro = this._anti_links.read_only_view;
 
       /* Load the IM addresses from the key file */
diff --git a/backends/libsocialweb/lib/Makefile.am b/backends/libsocialweb/lib/Makefile.am
index 618c8cb..0255895 100644
--- a/backends/libsocialweb/lib/Makefile.am
+++ b/backends/libsocialweb/lib/Makefile.am
@@ -42,6 +42,7 @@ libfolks_libsocialweb_la_VALAFLAGS = \
         --vapidir=$(top_srcdir)/folks \
         --pkg folks \
         --pkg folks-internal \
+        --pkg folks-generics \
         --pkg gobject-2.0 \
         --pkg gio-2.0 \
         --pkg gee-0.8 \
diff --git a/backends/libsocialweb/lib/swf-persona.vala b/backends/libsocialweb/lib/swf-persona.vala
index 3abf357..1c22608 100644
--- a/backends/libsocialweb/lib/swf-persona.vala
+++ b/backends/libsocialweb/lib/swf-persona.vala
@@ -136,7 +136,7 @@ public class Swf.Persona : Folks.Persona,
       set { this.change_gender.begin (value); } /* not writeable */
     }
 
-  private HashSet<UrlFieldDetails> _urls;
+  private SmallSet<UrlFieldDetails> _urls;
   private Set<UrlFieldDetails> _urls_ro;
 
   /**
@@ -384,7 +384,7 @@ public class Swf.Persona : Folks.Persona,
           this.notify_property ("full-name");
         }
 
-      var urls = new HashSet<UrlFieldDetails> (
+      var urls = new SmallSet<UrlFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
 
diff --git a/backends/ofono/Makefile.am b/backends/ofono/Makefile.am
index 0877de5..695afca 100644
--- a/backends/ofono/Makefile.am
+++ b/backends/ofono/Makefile.am
@@ -17,6 +17,7 @@ ofono_la_VALAFLAGS = \
        --vapidir=. \
        --vapidir=$(top_srcdir)/folks \
        --pkg folks \
+       --pkg folks-generics \
        --pkg folks-internal \
        --pkg gee-0.8 \
        --pkg gio-2.0 \
diff --git a/backends/ofono/ofono-persona.vala b/backends/ofono/ofono-persona.vala
index 2ef2957..f81230d 100644
--- a/backends/ofono/ofono-persona.vala
+++ b/backends/ofono/ofono-persona.vala
@@ -39,9 +39,9 @@ public class Folks.Backends.Ofono.Persona : Folks.Persona,
   private StructuredName? _structured_name = null;
   private string _full_name = "";
   private string _nickname = "";
-  private HashSet<PhoneFieldDetails> _phone_numbers;
+  private SmallSet<PhoneFieldDetails> _phone_numbers;
   private Set<PhoneFieldDetails> _phone_numbers_ro;
-  private HashSet<EmailFieldDetails> _email_addresses;
+  private SmallSet<EmailFieldDetails> _email_addresses;
   private Set<EmailFieldDetails> _email_addresses_ro;
   
   private const string[] _linkable_properties =
@@ -145,10 +145,10 @@ public class Folks.Backends.Ofono.Persona : Folks.Persona,
       debug ("Adding Ofono Persona '%s' (IID '%s', group '%s')", this.uid,
           this.iid, this.display_id);
       
-      this._phone_numbers = new HashSet<PhoneFieldDetails> ();
+      this._phone_numbers = new SmallSet<PhoneFieldDetails> ();
       this._phone_numbers_ro = this._phone_numbers.read_only_view;
 
-      this._email_addresses = new HashSet<EmailFieldDetails> ();
+      this._email_addresses = new SmallSet<EmailFieldDetails> ();
       this._email_addresses_ro = this._email_addresses.read_only_view;
     }
 
diff --git a/backends/telepathy/lib/Makefile.am b/backends/telepathy/lib/Makefile.am
index 4ed3533..094916b 100644
--- a/backends/telepathy/lib/Makefile.am
+++ b/backends/telepathy/lib/Makefile.am
@@ -127,6 +127,7 @@ libfolks_telepathy_la_VALAFLAGS = \
        --vapidir=$(abs_top_srcdir)/folks \
        --vapidir=$(abs_top_builddir)/folks \
        --pkg folks \
+       --pkg folks-generics \
        --pkg folks-internal \
        --pkg tp-lowlevel \
        --pkg gobject-2.0 \
diff --git a/backends/telepathy/lib/tpf-persona-store-cache.vala 
b/backends/telepathy/lib/tpf-persona-store-cache.vala
index 720214e..d8d0a63 100644
--- a/backends/telepathy/lib/tpf-persona-store-cache.vala
+++ b/backends/telepathy/lib/tpf-persona-store-cache.vala
@@ -287,7 +287,7 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
       var avatar_variant = variant.get_child_value (9).get_maybe ();
 
       // Deserialise the groups
-      var group_set = new HashSet<string> ();
+      var group_set = new SmallSet<string> ();
       for (uint i = 0; i < groups.n_children (); i++)
         {
           group_set.add (groups.get_child_value (i).get_string ());
@@ -316,13 +316,13 @@ internal class Tpf.PersonaStoreCache : Folks.ObjectCache<Tpf.Persona>
           full_name = variant.get_child_value (11).get_string();
         }
 
-      var email_address_set = new HashSet<EmailFieldDetails> (
+      var email_address_set = new SmallSet<EmailFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
-      var phone_number_set = new HashSet<PhoneFieldDetails> (
+      var phone_number_set = new SmallSet<PhoneFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
-      var url_set = new HashSet<UrlFieldDetails> (
+      var url_set = new SmallSet<UrlFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
 
diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala
index ff5897d..4c60caa 100644
--- a/backends/telepathy/lib/tpf-persona-store.vala
+++ b/backends/telepathy/lib/tpf-persona-store.vala
@@ -58,7 +58,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
 
   /* TpContact IDs. Note that this should *not* be cleared in _reset().
    * See bgo#630822. */
-  private HashSet<string> _favourite_ids = new HashSet<string> ();
+  private SmallSet<string> _favourite_ids = new SmallSet<string> ();
 
   /* Mapping from Persona IIDs to their avatars. This allows avatars to persist
    * between the cached (offline) personas and the online personas. Note that
@@ -95,7 +95,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
   private bool _cache_needs_update = false;
 
   /* marshalled from ContactInfo.SupportedFields */
-  internal HashSet<string> _supported_fields;
+  internal SmallSet<string> _supported_fields;
   internal Set<string> _supported_fields_ro;
 
   private Account _account;
@@ -433,7 +433,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
 
       this._contact_persona_map = new HashMap<unowned Contact, Persona> ();
 
-      this._supported_fields = new HashSet<string> ();
+      this._supported_fields = new SmallSet<string> ();
       this._supported_fields_ro = this._supported_fields.read_only_view;
       this._self_persona = null;
     }
@@ -1113,7 +1113,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
            * self and roster TpContacts, so they should have been removed
            * already. But deal with it just in case... */
           warning ("A TpContact part of the ContactList is disposed");
-          var personas = new HashSet<Persona> ();
+          var personas = new SmallSet<Persona> ();
           personas.add (persona);
           this._emit_personas_changed (null, personas);
         }
@@ -1146,8 +1146,8 @@ public class Tpf.PersonaStore : Folks.PersonaStore
     {
       var contact = this._conn.self_contact;
 
-      var personas_added = new HashSet<Persona> ();
-      var personas_removed = new HashSet<Persona> ();
+      var personas_added = new SmallSet<Persona> ();
+      var personas_removed = new SmallSet<Persona> ();
 
       /* Remove old self persona if not also part of roster. Keep a reference
        * to the persona so _remove_persona() doesn't unset it early. */
@@ -1444,7 +1444,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
       else
         birthday_str = birthday.to_string ();
 
-      var info_set = new HashSet<ContactInfoField> ();
+      var info_set = new SmallSet<ContactInfoField> ();
       string[] values = { birthday_str };
       string[] parameters = { null };
 
@@ -1463,7 +1463,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
           full_name = "";
         }
 
-      var info_set = new HashSet<ContactInfoField> ();
+      var info_set = new SmallSet<ContactInfoField> ();
       string[] values = { full_name };
       string[] parameters = { null };
 
@@ -1478,7 +1478,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
       string field_name)
         throws PersonaStoreError
     {
-      var info_set = new HashSet<ContactInfoField> ();
+      var info_set = new SmallSet<ContactInfoField> ();
 
       foreach (var afd in details)
         {
@@ -1506,7 +1506,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
     }
 
   private async void _change_user_contact_info (Tpf.Persona persona,
-      HashSet<ContactInfoField> info_set) throws PersonaStoreError
+      SmallSet<ContactInfoField> info_set) throws PersonaStoreError
     {
       if (!persona.is_user)
         {
@@ -1545,7 +1545,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
     }
 
   private static GLib.List<ContactInfoField> _contact_info_set_to_list (
-      HashSet<ContactInfoField> info_set)
+      SmallSet<ContactInfoField> info_set)
     {
       var info_list = new GLib.List<ContactInfoField> ();
       foreach (var info_field in info_set)
diff --git a/backends/telepathy/lib/tpf-persona.vala b/backends/telepathy/lib/tpf-persona.vala
index d7fc067..3b5d78a 100644
--- a/backends/telepathy/lib/tpf-persona.vala
+++ b/backends/telepathy/lib/tpf-persona.vala
@@ -354,7 +354,7 @@ public class Tpf.Persona : Folks.Persona,
       ((Tpf.PersonaStore) this.store)._set_cache_needs_update ();
     }
 
-  private HashSet<EmailFieldDetails>? _email_addresses = null;
+  private SmallSet<EmailFieldDetails>? _email_addresses = null;
   private Set<EmailFieldDetails>? _email_addresses_ro = null;
 
   /**
@@ -462,7 +462,7 @@ public class Tpf.Persona : Folks.Persona,
       get { return this._last_call_interaction_datetime; }
     }
 
-  private HashSet<string> _groups = new HashSet<string> ();
+  private SmallSet<string> _groups = new SmallSet<string> ();
   private Set<string> _groups_ro;
 
   /**
@@ -650,7 +650,7 @@ public class Tpf.Persona : Folks.Persona,
         }
     }
 
-  private HashSet<PhoneFieldDetails>? _phone_numbers = null;
+  private SmallSet<PhoneFieldDetails>? _phone_numbers = null;
   private Set<PhoneFieldDetails>? _phone_numbers_ro = null;
 
   /**
@@ -681,7 +681,7 @@ public class Tpf.Persona : Folks.Persona,
           this._phone_numbers, "tel");
     }
 
-  private HashSet<UrlFieldDetails>? _urls = null;
+  private SmallSet<UrlFieldDetails>? _urls = null;
   private Set<UrlFieldDetails>? _urls_ro = null;
 
   /**
@@ -938,17 +938,17 @@ public class Tpf.Persona : Folks.Persona,
         }
       else if (this._urls == null)
         {
-          this._urls = new HashSet<UrlFieldDetails> (
+          this._urls = new SmallSet<UrlFieldDetails> (
               AbstractFieldDetails<string>.hash_static,
               AbstractFieldDetails<string>.equal_static);
           this._urls_ro = this._urls.read_only_view;
 
-          this._email_addresses = new HashSet<EmailFieldDetails> (
+          this._email_addresses = new SmallSet<EmailFieldDetails> (
               AbstractFieldDetails<string>.hash_static,
               AbstractFieldDetails<string>.equal_static);
           this._email_addresses_ro = this._email_addresses.read_only_view;
 
-          this._phone_numbers = new HashSet<PhoneFieldDetails> (
+          this._phone_numbers = new SmallSet<PhoneFieldDetails> (
               AbstractFieldDetails<string>.hash_static,
               AbstractFieldDetails<string>.equal_static);
           this._phone_numbers_ro = this._phone_numbers.read_only_view;
@@ -964,13 +964,13 @@ public class Tpf.Persona : Folks.Persona,
       var changed = false;
       var new_birthday_str = "";
       var new_full_name = "";
-      var new_email_addresses = new HashSet<EmailFieldDetails> (
+      var new_email_addresses = new SmallSet<EmailFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
-      var new_phone_numbers = new HashSet<PhoneFieldDetails> (
+      var new_phone_numbers = new SmallSet<PhoneFieldDetails> (
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
-      var new_urls = new HashSet<UrlFieldDetails> (
+      var new_urls = new SmallSet<UrlFieldDetails> (
            AbstractFieldDetails<string>.hash_static,
            AbstractFieldDetails<string>.equal_static);
 
@@ -1163,11 +1163,11 @@ public class Tpf.Persona : Folks.Persona,
    * @since 0.6.0
    */
   internal Persona.from_cache (PersonaStore store, string uid, string iid,
-      string im_address, string protocol, HashSet<string> groups,
+      string im_address, string protocol, SmallSet<string> groups,
       bool is_favourite, string alias, bool is_in_contact_list, bool is_user,
       LoadableIcon? avatar, DateTime? birthday, string full_name,
-      HashSet<EmailFieldDetails> email_addresses,
-      HashSet<PhoneFieldDetails> phone_numbers, HashSet<UrlFieldDetails> urls)
+      SmallSet<EmailFieldDetails> email_addresses,
+      SmallSet<PhoneFieldDetails> phone_numbers, SmallSet<UrlFieldDetails> urls)
     {
       Object (contact: null,
               display_id: im_address,
diff --git a/backends/tracker/lib/Makefile.am b/backends/tracker/lib/Makefile.am
index c44fe00..bc8cc0f 100644
--- a/backends/tracker/lib/Makefile.am
+++ b/backends/tracker/lib/Makefile.am
@@ -25,6 +25,7 @@ libfolks_tracker_la_VALAFLAGS = \
        --vapidir=. \
        --vapidir=$(top_srcdir)/folks \
        --pkg folks \
+       --pkg folks-generics \
        --pkg folks-internal \
        --pkg gobject-2.0 \
        --pkg gio-2.0 \
diff --git a/backends/tracker/lib/trf-persona-store.vala b/backends/tracker/lib/trf-persona-store.vala
index 25fb8e1..f18b5be 100644
--- a/backends/tracker/lib/trf-persona-store.vala
+++ b/backends/tracker/lib/trf-persona-store.vala
@@ -842,7 +842,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
  /**
-   * Transform HashSet<string> to "id1,id2,.."
+   * Transform Set<string> to "id1,id2,.."
    *
    * @since 0.5.1
    */
@@ -869,6 +869,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
    */
   public static Set<string> unserialize_local_ids (string local_ids)
     {
+      /* The documentation explicitly says this is a HashSet, so we shouldn't
+       * switch it to SmallSet. Add a parallel API and update callers if
+       * this turns out to be a hot path. */
       var ids = new HashSet<string> ();
 
       if (local_ids != "")
@@ -992,7 +995,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
    */
   private async void _remove_attributes (string urn, char remove_flag)
     {
-      Gee.HashSet<string> affiliations =
+      SmallSet<string> affiliations =
        yield this._affiliations_from_persona (urn);
 
      foreach (var affl in affiliations)
@@ -1003,7 +1006,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
              _REMOVE_ALL_ATTRIBS ||
              (remove_flag & _REMOVE_PHONES) == _REMOVE_PHONES)
            {
-             Gee.HashSet<string> phones =
+             SmallSet<string> phones =
                yield this._phones_from_affiliation (affl);
 
              foreach (var phone in phones)
@@ -1017,7 +1020,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
              _REMOVE_ALL_ATTRIBS ||
              (remove_flag & _REMOVE_POSTALS) == _REMOVE_POSTALS)
            {
-             Gee.HashSet<string> postals =
+             SmallSet<string> postals =
                yield this._postals_from_affiliation (affl);
              foreach (var postal in postals)
                {
@@ -1030,7 +1033,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
              _REMOVE_ALL_ATTRIBS ||
              (remove_flag & _REMOVE_IM_ADDRS) == _REMOVE_IM_ADDRS)
            {
-             Gee.HashSet<string> im_addrs =
+             SmallSet<string> im_addrs =
                yield this._imaddrs_from_affiliation (affl);
              foreach (var im_addr in im_addrs)
                {
@@ -1043,7 +1046,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
              _REMOVE_ALL_ATTRIBS ||
              (remove_flag & _REMOVE_EMAILS) == _REMOVE_EMAILS)
            {
-             Gee.HashSet<string> emails =
+             SmallSet<string> emails =
                yield this._emails_from_affiliation (affl);
                foreach (var email in emails)
                  {
@@ -1914,7 +1917,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
   /* This method is safe to call multiple times concurrently. */
   private async string _single_value_query (string query)
     {
-      Gee.HashSet<string> rows = yield this._multi_value_query (query);
+      SmallSet<string> rows = yield this._multi_value_query (query);
       foreach (var r in rows)
         {
           return r;
@@ -1923,9 +1926,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
   /* This method is safe to call multiple times concurrently. */
-  private async Gee.HashSet<string> _multi_value_query (string query)
+  private async SmallSet<string> _multi_value_query (string query)
     {
-      Gee.HashSet<string> ret = new Gee.HashSet<string> ();
+      SmallSet<string> ret = new SmallSet<string> ();
 
       debug ("[_multi_value_query] %s", query);
 
@@ -2131,7 +2134,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
   internal async void _set_im_addresses (Folks.Persona persona,
       MultiMap<string, ImFieldDetails> im_addresses)
     {
-      var ims = new HashSet<ImFieldDetails> ();
+      var ims = new SmallSet<ImFieldDetails> ();
       foreach (var proto in im_addresses.get_keys ())
         {
           var addrs = im_addresses.get (proto);
@@ -2618,14 +2621,14 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
   /* This method is safe to call multiple times concurrently. */
-  private async Gee.HashSet<string> _affiliations_from_persona (string urn)
+  private async SmallSet<string> _affiliations_from_persona (string urn)
     {
       return yield this._linked_resources (urn, Trf.OntologyDefs.NCO_PERSON,
           Trf.OntologyDefs.NCO_HAS_AFFILIATION);
     }
 
   /* This method is safe to call multiple times concurrently. */
-  private async Gee.HashSet<string> _phones_from_affiliation (string affl)
+  private async SmallSet<string> _phones_from_affiliation (string affl)
     {
       return yield this._linked_resources (affl,
           Trf.OntologyDefs.NCO_AFFILIATION,
@@ -2633,7 +2636,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
   /* This method is safe to call multiple times concurrently. */
-  private async Gee.HashSet<string>  _postals_from_affiliation (string affl)
+  private async SmallSet<string>  _postals_from_affiliation (string affl)
     {
       return yield this._linked_resources (affl,
           Trf.OntologyDefs.NCO_AFFILIATION,
@@ -2641,7 +2644,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
   /* This method is safe to call multiple times concurrently. */
-  private async Gee.HashSet<string> _imaddrs_from_affiliation  (string affl)
+  private async SmallSet<string> _imaddrs_from_affiliation  (string affl)
     {
       return yield this._linked_resources (affl,
           Trf.OntologyDefs.NCO_AFFILIATION,
@@ -2649,7 +2652,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
   /* This method is safe to call multiple times concurrently. */
-  private async Gee.HashSet<string> _emails_from_affiliation (string affl)
+  private async SmallSet<string> _emails_from_affiliation (string affl)
     {
       return yield this._linked_resources (affl,
           Trf.OntologyDefs.NCO_AFFILIATION,
@@ -2729,7 +2732,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
    * @param linking_predicate i.e.: nco:hasAffiliation
    * @return a list of linked resources (in <urn> format)
    */
-  private async Gee.HashSet<string> _linked_resources (string urn,
+  private async SmallSet<string> _linked_resources (string urn,
       string subject_type, string linking_predicate)
     {
       string query_t = "SELECT " +
diff --git a/backends/tracker/lib/trf-persona.vala b/backends/tracker/lib/trf-persona.vala
index 1683396..dcb37fa 100644
--- a/backends/tracker/lib/trf-persona.vala
+++ b/backends/tracker/lib/trf-persona.vala
@@ -49,9 +49,9 @@ public class Trf.Persona : Folks.Persona,
   private bool _is_favourite;
   private const string[] _linkable_properties =
       {"im-addresses", "local-ids", "web-service-addresses"};
-  private HashSet<PhoneFieldDetails> _phone_numbers;
+  private SmallSet<PhoneFieldDetails> _phone_numbers;
   private Set<PhoneFieldDetails> _phone_numbers_ro;
-  private HashSet<EmailFieldDetails> _email_addresses;
+  private SmallSet<EmailFieldDetails> _email_addresses;
   private Set<EmailFieldDetails> _email_addresses_ro;
   private weak Sparql.Cursor _cursor;
   private string _tracker_id;
@@ -277,7 +277,7 @@ public class Trf.Persona : Folks.Persona,
       set { this.change_calendar_event_id.begin (value); } /* not writeable */
     }
 
-  private HashSet<RoleFieldDetails> _roles;
+  private SmallSet<RoleFieldDetails> _roles;
   private Set<RoleFieldDetails> _roles_ro;
 
   /**
@@ -301,7 +301,7 @@ public class Trf.Persona : Folks.Persona,
       yield ((Trf.PersonaStore) this.store)._set_roles (this, roles);
     }
 
-  private HashSet<NoteFieldDetails> _notes;
+  private SmallSet<NoteFieldDetails> _notes;
   private Set<NoteFieldDetails> _notes_ro;
 
   /**
@@ -325,7 +325,7 @@ public class Trf.Persona : Folks.Persona,
       yield ((Trf.PersonaStore) this.store)._set_notes (this, notes);
     }
 
-  private HashSet<UrlFieldDetails> _urls;
+  private SmallSet<UrlFieldDetails> _urls;
   private Set<UrlFieldDetails> _urls_ro;
 
   /**
@@ -348,7 +348,7 @@ public class Trf.Persona : Folks.Persona,
       yield ((Trf.PersonaStore) this.store)._set_urls (this, urls);
     }
 
-  private HashSet<PostalAddressFieldDetails> _postal_addresses;
+  private SmallSet<PostalAddressFieldDetails> _postal_addresses;
   private Set<PostalAddressFieldDetails> _postal_addresses_ro;
 
   /**
@@ -556,27 +556,27 @@ public class Trf.Persona : Folks.Persona,
       this._gender = Gender.UNSPECIFIED;
       this._full_name = "";
       this._structured_name = null;
-      this._phone_numbers = new HashSet<PhoneFieldDetails> (
+      this._phone_numbers = new SmallSet<PhoneFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       this._phone_numbers_ro = this._phone_numbers.read_only_view;
-      this._email_addresses = new HashSet<EmailFieldDetails> (
+      this._email_addresses = new SmallSet<EmailFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       this._email_addresses_ro = this._email_addresses.read_only_view;
-      this._roles = new HashSet<RoleFieldDetails> (
+      this._roles = new SmallSet<RoleFieldDetails> (
           AbstractFieldDetails<Role>.hash_static,
           AbstractFieldDetails<Role>.equal_static);
       this._roles_ro = this._roles.read_only_view;
-      this._notes = new HashSet<NoteFieldDetails> (
+      this._notes = new SmallSet<NoteFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       this._notes_ro = this._notes.read_only_view;
-      this._urls = new HashSet<UrlFieldDetails> (
+      this._urls = new SmallSet<UrlFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       this._urls_ro = this._urls.read_only_view;
-      this._postal_addresses = new HashSet<PostalAddressFieldDetails> (
+      this._postal_addresses = new SmallSet<PostalAddressFieldDetails> (
           AbstractFieldDetails<PostalAddress>.hash_static,
           AbstractFieldDetails<PostalAddress>.equal_static);
       this._postal_addresses_ro = this._postal_addresses.read_only_view;
@@ -802,7 +802,7 @@ public class Trf.Persona : Folks.Persona,
           return;
         }
 
-      var postal_addresses = new HashSet<PostalAddressFieldDetails> (
+      var postal_addresses = new SmallSet<PostalAddressFieldDetails> (
           AbstractFieldDetails<PostalAddress>.hash_static,
           AbstractFieldDetails<PostalAddress>.equal_static);
 
@@ -982,7 +982,7 @@ public class Trf.Persona : Folks.Persona,
           return;
         }
 
-      HashSet<RoleFieldDetails> role_fds = new HashSet<RoleFieldDetails> (
+      SmallSet<RoleFieldDetails> role_fds = new SmallSet<RoleFieldDetails> (
           AbstractFieldDetails<Role>.hash_static,
           AbstractFieldDetails<Role>.equal_static);
 
@@ -1219,7 +1219,7 @@ public class Trf.Persona : Folks.Persona,
           return;
         }
 
-      var phones = new HashSet<PhoneFieldDetails> (
+      var phones = new SmallSet<PhoneFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       string[] phones_a = phones_field.split ("\n");
@@ -1345,7 +1345,7 @@ public class Trf.Persona : Folks.Persona,
           return;
         }
 
-      var email_addresses = new HashSet<EmailFieldDetails> (
+      var email_addresses = new SmallSet<EmailFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       string[] emails_a = emails_field.split (",");
@@ -1370,7 +1370,7 @@ public class Trf.Persona : Folks.Persona,
 
   private void _update_urls ()
     {
-      var url_fds = new HashSet<UrlFieldDetails> (
+      var url_fds = new SmallSet<UrlFieldDetails> (
           AbstractFieldDetails<string>.hash_static,
           AbstractFieldDetails<string>.equal_static);
       var _urls_field = this._cursor.get_string (Trf.Fields.URLS).dup ();
diff --git a/folks/Makefile.am b/folks/Makefile.am
index 57c10f3..4d539ab 100644
--- a/folks/Makefile.am
+++ b/folks/Makefile.am
@@ -111,6 +111,7 @@ libfolks_la_VALAFLAGS = \
        --vapidir=$(abs_builddir) \
        --pkg build-conf \
        --pkg folks-internal \
+       --pkg folks-generics \
        --pkg gobject-2.0 \
        --pkg gmodule-2.0 \
        --pkg gio-2.0 \
diff --git a/folks/anti-linkable.vala b/folks/anti-linkable.vala
index 39b4e63..a53af5f 100644
--- a/folks/anti-linkable.vala
+++ b/folks/anti-linkable.vala
@@ -115,7 +115,7 @@ public interface Folks.AntiLinkable : Folks.Persona
   public async void add_anti_links (Set<Persona> other_personas)
       throws PropertyError
     {
-      var new_anti_links = new HashSet<string> ();
+      var new_anti_links = new SmallSet<string> ();
       new_anti_links.add_all (this.anti_links);
 
       foreach (var p in other_personas)
@@ -148,7 +148,7 @@ public interface Folks.AntiLinkable : Folks.Persona
   public async void remove_anti_links (Set<Persona> other_personas)
       throws PropertyError
     {
-      var new_anti_links = new HashSet<string> ();
+      var new_anti_links = new SmallSet<string> ();
       new_anti_links.add_all (this.anti_links);
 
       foreach (var p in other_personas)
diff --git a/folks/backend-store.vala b/folks/backend-store.vala
index e7b5d69..0f47236 100644
--- a/folks/backend-store.vala
+++ b/folks/backend-store.vala
@@ -44,9 +44,9 @@ public class Folks.BackendStore : Object {
   /* this contains all backends, regardless of enabled or prepared state */
   private HashMap<string,Backend> _backend_hash;
   /* if null, all backends are allowed */
-  private HashSet<string>? _backends_allowed;
+  private SmallSet<string>? _backends_allowed;
   /* if null, no backends are disabled */
-  private HashSet<string>? _backends_disabled;
+  private SmallSet<string>? _backends_disabled;
   private HashMap<string, Backend> _prepared_backends;
   private Map<string, Backend> _prepared_backends_ro;
   private File _config_file;
@@ -794,7 +794,7 @@ public class Folks.BackendStore : Object {
            * g_parse_debug_string(). */
           var tokens = envvar.split_set (" ,:");
 
-          this._backends_allowed = new HashSet<string> ();
+          this._backends_allowed = new SmallSet<string> ();
 
           foreach (unowned string s in tokens)
             {
@@ -827,7 +827,7 @@ public class Folks.BackendStore : Object {
         {
           var tokens = envvar.split_set (" ,:");
 
-          this._backends_disabled = new HashSet<string> ();
+          this._backends_disabled = new SmallSet<string> ();
 
           foreach (unowned string s in tokens)
             {
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index 705b34c..84bb9e7 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -119,7 +119,7 @@ public class Folks.IndividualAggregator : Object
   private BackendStore _backend_store;
   private HashMap<string, PersonaStore> _stores;
   private unowned PersonaStore? _primary_store = null;
-  private HashSet<Backend> _backends;
+  private SmallSet<Backend> _backends;
 
   /* This is conceptually a MultiMap<string, Individual> but it's sufficiently
    * heavily-used that avoiding GObject overhead in favour of inlinable
@@ -360,7 +360,7 @@ public class Folks.IndividualAggregator : Object
       this._link_map = new HashTable<string, GenericArray<Individual>> (
           str_hash, str_equal);
 
-      this._backends = new HashSet<Backend> ();
+      this._backends = new SmallSet<Backend> ();
       this._debug = Debug.dup ();
       this._debug.print_status.connect (this._debug_print_status);
 
@@ -919,7 +919,7 @@ public class Folks.IndividualAggregator : Object
             }
 
           this._personas_changed_cb (store, persona_set,
-              new HashSet<Persona> (), null, null,
+              SmallSet.empty<Persona> (), null, null,
               GroupDetails.ChangeReason.NONE);
         }
 
@@ -976,8 +976,8 @@ public class Folks.IndividualAggregator : Object
         {
           removed_personas.add (iter.get_value ());
         }
-      this._personas_changed_cb (store, new HashSet<Persona> (), removed_personas,
-          null, null, GroupDetails.ChangeReason.NONE);
+      this._personas_changed_cb (store, SmallSet.empty<Persona> (),
+          removed_personas, null, null, GroupDetails.ChangeReason.NONE);
 
       if (this._primary_store == store)
         {
@@ -1019,8 +1019,8 @@ public class Folks.IndividualAggregator : Object
       Internal.profiling_point ("emitting " +
           "IndividualAggregator::individuals-changed");
 
-      _added = (added != null) ? (!) added : new HashSet<Individual> ();
-      _removed = (removed != null) ? (!) removed : new HashSet<Individual> ();
+      _added = (added != null) ? (!) added : SmallSet.empty<Individual> ();
+      _removed = (removed != null) ? (!) removed : SmallSet.empty<Individual> ();
 
       if (changes != null)
         {
@@ -1304,7 +1304,7 @@ public class Folks.IndividualAggregator : Object
           "(is user: %s, IID: %s).", pspec.name, persona.uid,
           persona.is_user ? "yes" : "no", persona.iid);
 
-      var persona_set = new HashSet<Persona> ();
+      var persona_set = new SmallSet<Persona> ();
       persona_set.add (persona);
 
       this._personas_changed_cb (persona.store, persona_set, persona_set,
@@ -1322,7 +1322,7 @@ public class Folks.IndividualAggregator : Object
       debug ("Anti-links changed for persona '%s' (is user: %s, IID: %s).",
           persona.uid, persona.is_user ? "yes" : "no", persona.iid);
 
-      var persona_set = new HashSet<Persona> ();
+      var persona_set = new SmallSet<Persona> ();
       persona_set.add (persona);
 
       this._personas_changed_cb (persona.store, persona_set, persona_set,
@@ -1833,7 +1833,7 @@ public class Folks.IndividualAggregator : Object
       if (i.personas.size > 0)
         {
           var changes = new HashMultiMap<Individual?, Individual?> ();
-          var individuals = new HashSet<Individual> ();
+          var individuals = new SmallSet<Individual> ();
 
           individuals.add (i);
           changes.set (i, replacement);
@@ -1943,7 +1943,9 @@ public class Folks.IndividualAggregator : Object
     {
       /* Removing personas changes the persona set so we need to make a copy
        * first */
-      var personas = new HashSet<Persona> ();
+      var personas = new SmallSet<Persona> ();
+      /* FIXME: this is O(n**2) but if we know that individual.personas
+       * is a SmallSet, we can do it in O(n) */
       foreach (var p in individual.personas)
         {
           personas.add (p);
@@ -2065,7 +2067,7 @@ public class Folks.IndividualAggregator : Object
             AbstractFieldDetails<string>.equal_static);
 
       /* List of local_ids */
-      var local_ids = new Gee.HashSet<string> ();
+      var local_ids = new SmallSet<string> ();
 
       foreach (var persona in personas)
         {
@@ -2178,7 +2180,7 @@ public class Folks.IndividualAggregator : Object
        * In the worst case, this will double the number of personas, since if
        * none of the personas have anti-links writeable, each will have to be
        * linked with a new writeable persona. */
-      var individual_personas = new HashSet<Persona> (); /* as we modify it */
+      var individual_personas = new SmallSet<Persona> (); /* as we modify it */
       individual_personas.add_all (individual.personas);
 
       debug ("    Inserting anti-links:");
@@ -2186,7 +2188,7 @@ public class Folks.IndividualAggregator : Object
         {
           try
             {
-              var personas = new HashSet<Persona> ();
+              var personas = new SmallSet<Persona> ();
               personas.add (pers);
               debug ("        Anti-linking persona '%s' (%p)", pers.uid, pers);
 
@@ -2197,7 +2199,7 @@ public class Folks.IndividualAggregator : Object
                   writeable_persona.uid, writeable_persona);
 
               /* Make sure not to anti-link the new persona to pers. */
-              var anti_link_personas = new HashSet<Persona> ();
+              var anti_link_personas = new SmallSet<Persona> ();
               anti_link_personas.add_all (individual_personas);
               anti_link_personas.remove (pers);
 
diff --git a/folks/individual.vala b/folks/individual.vala
index ac373e0..1362b45 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -103,8 +103,7 @@ public class Folks.Individual : Object,
     WebServiceDetails
 {
   /* Stores the Personas contained in this Individual. */
-  private HashSet<Persona> _persona_set =
-      new HashSet<Persona> ();
+  private SmallSet<Persona> _persona_set = new SmallSet<Persona> ();
   /* Read-only view of the above set */
   private Set<Persona> _persona_set_ro;
   /* Mapping from PersonaStore -> number of Personas from that store contained
@@ -495,7 +494,7 @@ public class Folks.Individual : Object,
       set { this.change_gender.begin (value); } /* not writeable */
     }
 
-  private HashSet<UrlFieldDetails>? _urls = null;
+  private SmallSet<UrlFieldDetails>? _urls = null;
   private Set<UrlFieldDetails>? _urls_ro = null;
 
   /**
@@ -512,7 +511,7 @@ public class Folks.Individual : Object,
       set { this.change_urls.begin (value); } /* not writeable */
     }
 
-  private HashSet<PhoneFieldDetails>? _phone_numbers = null;
+  private SmallSet<PhoneFieldDetails>? _phone_numbers = null;
   private Set<PhoneFieldDetails>? _phone_numbers_ro = null;
 
   /**
@@ -529,7 +528,7 @@ public class Folks.Individual : Object,
       set { this.change_phone_numbers.begin (value); } /* not writeable */
     }
 
-  private HashSet<EmailFieldDetails>? _email_addresses = null;
+  private SmallSet<EmailFieldDetails>? _email_addresses = null;
   private Set<EmailFieldDetails>? _email_addresses_ro = null;
 
   /**
@@ -546,7 +545,7 @@ public class Folks.Individual : Object,
       set { this.change_email_addresses.begin (value); } /* not writeable */
     }
 
-  private HashSet<RoleFieldDetails>? _roles = null;
+  private SmallSet<RoleFieldDetails>? _roles = null;
   private Set<RoleFieldDetails>? _roles_ro = null;
 
   /**
@@ -563,7 +562,7 @@ public class Folks.Individual : Object,
       set { this.change_roles.begin (value); } /* not writeable */
     }
 
-  private HashSet<string>? _local_ids = null;
+  private SmallSet<string>? _local_ids = null;
   private Set<string>? _local_ids_ro = null;
 
   /**
@@ -615,7 +614,7 @@ public class Folks.Individual : Object,
       set { this.change_calendar_event_id.begin (value); } /* not writeable */
     }
 
-  private HashSet<NoteFieldDetails>? _notes = null;
+  private SmallSet<NoteFieldDetails>? _notes = null;
   private Set<NoteFieldDetails>? _notes_ro = null;
 
   /**
@@ -632,7 +631,7 @@ public class Folks.Individual : Object,
       set { this.change_notes.begin (value); } /* not writeable */
     }
 
-  private HashSet<PostalAddressFieldDetails>? _postal_addresses = null;
+  private SmallSet<PostalAddressFieldDetails>? _postal_addresses = null;
   private Set<PostalAddressFieldDetails>? _postal_addresses_ro = null;
 
   /**
@@ -735,7 +734,7 @@ public class Folks.Individual : Object,
         }
     }
 
-  private HashSet<string>? _groups = null;
+  private SmallSet<string>? _groups = null;
   private Set<string>? _groups_ro = null;
 
   /**
@@ -1201,11 +1200,11 @@ public class Folks.Individual : Object,
         }
       else if (added == null)
         {
-          _added = new HashSet<Persona> ();
+          _added = SmallSet.empty<Persona> ();
         }
       else if (removed == null)
         {
-          _removed = new HashSet<Persona> ();
+          _removed = SmallSet.empty<Persona> ();
         }
 
       // We've now guaranteed that both _added and _removed are non-null.
@@ -1215,7 +1214,7 @@ public class Folks.Individual : Object,
 
   private void _store_removed_cb (PersonaStore store)
     {
-      var remaining_personas = new HashSet<Persona> ();
+      var remaining_personas = new SmallSet<Persona> ();
 
       /* Build a set of the remaining personas (those which weren't in the
        * removed store. */
@@ -1237,7 +1236,7 @@ public class Folks.Individual : Object,
       Persona? actor,
       GroupDetails.ChangeReason reason)
     {
-      var remaining_personas = new HashSet<Persona> ();
+      var remaining_personas = new SmallSet<Persona> ();
 
       /* Build a set of the remaining personas (those which aren't in the
        * set of removed personas). */
@@ -1512,7 +1511,7 @@ public class Folks.Individual : Object,
       /* Lazily instantiate the set of groups. */
       else if (this._groups == null)
         {
-          this._groups = new HashSet<string> ();
+          this._groups = new SmallSet<string> ();
           this._groups_ro = this._groups.read_only_view;
           created = true;
         }
@@ -1521,7 +1520,7 @@ public class Folks.Individual : Object,
       if (!created && !force_update)
          return;
 
-      var new_groups = new HashSet<string> ();
+      var new_groups = new SmallSet<string> ();
 
       /* FIXME: this should partition the personas by store (maybe we should
        * keep that mapping in general in this class), and execute
@@ -2063,14 +2062,14 @@ public class Folks.Individual : Object,
           () => { return this._urls == null; },
           () =>
             {
-              this._urls = new HashSet<UrlFieldDetails> (
+              this._urls = new SmallSet<UrlFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               this._urls_ro = this._urls.read_only_view;
             },
           () =>
             {
-              var new_urls = new HashSet<UrlFieldDetails> (
+              var new_urls = new SmallSet<UrlFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               var urls_set = new HashMap<unowned string,
@@ -2121,14 +2120,14 @@ public class Folks.Individual : Object,
           () => { return this._phone_numbers == null; },
           () =>
             {
-              this._phone_numbers = new HashSet<PhoneFieldDetails> (
+              this._phone_numbers = new SmallSet<PhoneFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               this._phone_numbers_ro = this._phone_numbers.read_only_view;
             },
           () =>
             {
-              var new_phone_numbers = new HashSet<PhoneFieldDetails> (
+              var new_phone_numbers = new SmallSet<PhoneFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               var phone_numbers_set = new HashMap<string, PhoneFieldDetails> (
@@ -2178,14 +2177,14 @@ public class Folks.Individual : Object,
           create_if_not_exist, () => { return this._email_addresses == null; },
           () =>
             {
-              this._email_addresses = new HashSet<EmailFieldDetails> (
+              this._email_addresses = new SmallSet<EmailFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               this._email_addresses_ro = this._email_addresses.read_only_view;
             },
           () =>
             {
-              var new_email_addresses = new HashSet<EmailFieldDetails> (
+              var new_email_addresses = new SmallSet<EmailFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               var emails_set = new HashMap<string, EmailFieldDetails> (
@@ -2236,14 +2235,14 @@ public class Folks.Individual : Object,
           () => { return this._roles == null; },
           () =>
             {
-              this._roles = new HashSet<RoleFieldDetails> (
+              this._roles = new SmallSet<RoleFieldDetails> (
                   AbstractFieldDetails<Role>.hash_static,
                   AbstractFieldDetails<Role>.equal_static);
               this._roles_ro = this._roles.read_only_view;
             },
           () =>
             {
-              var new_roles = new HashSet<RoleFieldDetails> (
+              var new_roles = new SmallSet<RoleFieldDetails> (
                   AbstractFieldDetails<Role>.hash_static,
                   AbstractFieldDetails<Role>.equal_static);
 
@@ -2276,12 +2275,12 @@ public class Folks.Individual : Object,
           () => { return this._local_ids == null; },
           () =>
             {
-              this._local_ids = new HashSet<string> ();
+              this._local_ids = new SmallSet<string> ();
               this._local_ids_ro = this._local_ids.read_only_view;
             },
           () =>
             {
-              var new_local_ids = new HashSet<string> ();
+              var new_local_ids = new SmallSet<string> ();
 
               foreach (var persona in this._persona_set)
                 {
@@ -2341,7 +2340,7 @@ public class Folks.Individual : Object,
           create_if_not_exist, () => { return this._postal_addresses == null; },
           () =>
             {
-              this._postal_addresses = new HashSet<PostalAddressFieldDetails> (
+              this._postal_addresses = new SmallSet<PostalAddressFieldDetails> (
                   AbstractFieldDetails<PostalAddress>.hash_static,
                   AbstractFieldDetails<PostalAddress>.equal_static);
               this._postal_addresses_ro = this._postal_addresses.read_only_view;
@@ -2349,7 +2348,7 @@ public class Folks.Individual : Object,
           () =>
             {
               var new_postal_addresses =
-                  new HashSet<PostalAddressFieldDetails> (
+                  new SmallSet<PostalAddressFieldDetails> (
                       AbstractFieldDetails<PostalAddress>.hash_static,
                       AbstractFieldDetails<PostalAddress>.equal_static);
 
@@ -2437,14 +2436,14 @@ public class Folks.Individual : Object,
           () => { return this._notes == null; },
           () =>
             {
-              this._notes = new HashSet<NoteFieldDetails> (
+              this._notes = new SmallSet<NoteFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
               this._notes_ro = this._notes.read_only_view;
             },
           () =>
             {
-              var new_notes = new HashSet<NoteFieldDetails> (
+              var new_notes = new SmallSet<NoteFieldDetails> (
                   AbstractFieldDetails<string>.hash_static,
                   AbstractFieldDetails<string>.equal_static);
 
@@ -2476,8 +2475,8 @@ public class Folks.Individual : Object,
     {
       assert (replacement_individual == null || replacement_individual != this);
 
-      var added = new HashSet<Persona> ();
-      var removed = new HashSet<Persona> ();
+      var added = new SmallSet<Persona> ();
+      var removed = new SmallSet<Persona> ();
 
       /* Determine which Personas have been added. If personas == null, we
        * assume it's an empty set. */
diff --git a/folks/potential-match.vala b/folks/potential-match.vala
index 6e7bb40..2148269 100644
--- a/folks/potential-match.vala
+++ b/folks/potential-match.vala
@@ -101,8 +101,7 @@ public class Folks.PotentialMatch : Object
    *
    * @since 0.5.1
    */
-  public static Set<string> known_email_aliases =
-      new Gee.HashSet<string> ();
+  public static Set<string> known_email_aliases = new SmallSet<string> ();
 
   private static double _DIST_THRESHOLD = 0.70;
   private const string _SEPARATORS = "._-+";


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]