[set] Cache population

Part of https://github.com/harfbuzz/harfbuzz/issues/1017
diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
index 3615c50..5ccefc8 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set-private.hh
@@ -189,12 +189,14 @@
   hb_object_header_t header;
   ASSERT_POD ();
   bool in_error;
+  mutable unsigned int population;
   hb_prealloced_array_t<page_map_t, 8> page_map;
   hb_prealloced_array_t<page_t, 1> pages;
 
   inline void init (void)
   {
     in_error = false;
+    population = 0;
     page_map.init ();
     pages.init ();
   }
@@ -220,6 +222,7 @@
     if (unlikely (hb_object_is_inert (this)))
       return;
     in_error = false;
+    population = 0;
     page_map.resize (0);
     pages.resize (0);
   }
@@ -231,10 +234,13 @@
     return true;
   }
 
+  inline void dirty (void) { population = (unsigned int) -1; }
+
   inline void add (hb_codepoint_t g)
   {
     if (unlikely (in_error)) return;
     if (unlikely (g == INVALID)) return;
+    dirty ();
     page_t *page = page_for_insert (g); if (unlikely (!page)) return;
     page->add (g);
   }
@@ -242,6 +248,7 @@
   {
     if (unlikely (in_error)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
     if (unlikely (a > b || a == INVALID || b == INVALID)) return false;
+    dirty ();
     unsigned int ma = get_major (a);
     unsigned int mb = get_major (b);
     if (ma == mb)
@@ -271,6 +278,7 @@
   {
     if (unlikely (in_error)) return;
     if (!count) return;
+    dirty ();
     hb_codepoint_t g = *array;
     while (count)
     {
@@ -296,6 +304,7 @@
   {
     if (unlikely (in_error)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
     if (!count) return true;
+    dirty ();
     hb_codepoint_t g = *array;
     hb_codepoint_t last_g = g;
     while (count)
@@ -325,6 +334,7 @@
     page_t *p = page_for (g);
     if (!p)
       return;
+    dirty ();
     p->del (g);
   }
   inline void del_range (hb_codepoint_t a, hb_codepoint_t b)
@@ -353,13 +363,18 @@
     unsigned int count = other->pages.len;
     if (!resize (count))
       return;
-
+    population = other->population;
     memcpy (pages.array, other->pages.array, count * sizeof (pages.array[0]));
     memcpy (page_map.array, other->page_map.array, count * sizeof (page_map.array[0]));
   }
 
   inline bool is_equal (const hb_set_t *other) const
   {
+    if (population != (unsigned int) -1 &&
+	other->population != (unsigned int) -1 &&
+	population != other->population)
+      return false;
+
     unsigned int na = pages.len;
     unsigned int nb = other->pages.len;
 
@@ -387,6 +402,8 @@
   {
     if (unlikely (in_error)) return;
 
+    dirty ();
+
     unsigned int na = pages.len;
     unsigned int nb = other->pages.len;
     unsigned int next_page = na;
@@ -592,10 +609,15 @@
 
   inline unsigned int get_population (void) const
   {
+    if (population != (unsigned int) -1)
+      return population;
+
     unsigned int pop = 0;
     unsigned int count = pages.len;
     for (unsigned int i = 0; i < count; i++)
       pop += pages[i].get_population ();
+
+    population = pop;
     return pop;
   }
   inline hb_codepoint_t get_min (void) const
diff --git a/src/hb-set.cc b/src/hb-set.cc
index af590ef..deb8d29 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -53,6 +53,7 @@
 static const hb_set_t _hb_set_nil = {
   HB_OBJECT_HEADER_STATIC,
   true, /* in_error */
+  0, /* population */
 
   {0} /* elts */
 };