optimize hb_set_del_range()

fix issue #2193
diff --git a/src/hb-set.hh b/src/hb-set.hh
index 048f099..822111d 100644
--- a/src/hb-set.hh
+++ b/src/hb-set.hh
@@ -89,6 +89,23 @@
       }
     }
 
+    void del_range (hb_codepoint_t a, hb_codepoint_t b)
+    {
+      elt_t *la = &elt (a);
+      elt_t *lb = &elt (b);
+      if (la == lb)
+	*la &= ~((mask (b) << 1) - mask(a));
+      else
+      {
+	*la &= mask (a) - 1;
+	la++;
+
+	memset (la, 0, (char *) lb - (char *) la);
+
+	*lb &= ~((mask (b) << 1) - 1);
+      }
+    }
+
     bool is_equal (const page_t *other) const
     {
       return 0 == hb_memcmp (&v, &other->v, sizeof (v));
@@ -366,14 +383,47 @@
     dirty ();
     page->del (g);
   }
+
   void del_range (hb_codepoint_t a, hb_codepoint_t b)
   {
     /* TODO perform op even if !successful. */
-    /* TODO Optimize, like add_range(). */
     if (unlikely (!successful)) return;
-    for (unsigned int i = a; i < b + 1; i++)
-      del (i);
+    if (unlikely (a > b || a == INVALID || b == INVALID)) return;
+    dirty ();
+    unsigned int ma = get_major (a);
+    unsigned int mb = get_major (b);
+    unsigned int mds = (a == major_start (ma))? ma: (ma + 1);
+    int          mde = (b + 1 == major_start (mb + 1))? (int)mb: ((int)mb - 1);
+    if (ma < mds)
+    {
+      page_t *page = page_for (a);
+      if (page)
+      {
+	if (ma == mb)
+	  page->del_range (a, b);
+	else
+	  page->del_range (a, major_start (ma + 1) - 1);
+      }
+    }
+    if (mde < (int)mb && ma != mb)
+    {
+      page_t *page = page_for (b);
+      if (page)
+	page->del_range (major_start (mb), b);
+    }
+    if ((int)mds <= mde)
+    {
+      unsigned int write_index = 0;
+      for (unsigned int i = 0; i < page_map.length; i++)
+      {
+	unsigned int m = page_map[i].major;
+	if (m < mds || mde < (int)m)
+	  page_map[write_index++] = page_map[i];
+      }
+      compact (write_index);
+    }
   }
+
   bool get (hb_codepoint_t g) const
   {
     const page_t *page = page_for (g);