[subset] Don't assume FeatureList is sorted

Though the spec said FeatureRecords are sorted alphabetically by feature
tag, there're font files with unsorted FeatureList. And harfbuzz is not
able to subset these files correctly because we use binary search in
finding featureRecords when collecting lookups. Also
find_duplicate_features needs to be updated to handle this.
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index fde273e..59abdf7 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -3658,24 +3658,33 @@
                                 const hb_set_t *feature_indices,
                                 hb_map_t *duplicate_feature_map /* OUT */) const
   {
-    hb_set_t unique_features;
-    hb_tag_t prev_t = get_feature_tag (feature_indices->get_min ());
+    if (feature_indices->is_empty ()) return;
+    hb_hashmap_t<hb_tag_t, hb_set_t *, (unsigned)-1, nullptr> unique_features;
     //find out duplicate features after subset
     for (unsigned i : feature_indices->iter ())
     {
       hb_tag_t t = get_feature_tag (i);
-      if (t != prev_t)
+      if (!unique_features.has (t))
       {
-        prev_t = t;
-        unique_features.clear ();
-        unique_features.add (i);
+        hb_set_t* indices = hb_set_create ();
+        if (unlikely (indices == hb_set_get_empty () ||
+                      !unique_features.set (t, indices)))
+        {
+          hb_set_destroy (indices);
+          for (auto _ : unique_features.iter ())
+            hb_set_destroy (_.second);
+          return;
+        }
+        if (unique_features.get (t))
+          unique_features.get (t)->add (i);
         duplicate_feature_map->set (i, i);
         continue;
       }
 
       bool found = false;
 
-      for (unsigned other_f_index : unique_features.iter ())
+      hb_set_t* same_tag_features = unique_features.get (t);
+      for (unsigned other_f_index : same_tag_features->iter ())
       {
         const Feature& f = get_feature (i);
         const Feature& other_f = get_feature (other_f_index);
@@ -3707,10 +3716,13 @@
       
       if (found == false)
       {
-        unique_features.add (i);
+        same_tag_features->add (i);
         duplicate_feature_map->set (i, i);
       }
     }
+
+    for (auto _ : unique_features.iter ())
+      hb_set_destroy (_.second);
   }
 
   void prune_features (const hb_map_t *lookup_indices, /* IN */
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index d02a3c0..1e446c6 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1013,24 +1013,15 @@
     }
 
     has_feature_filter = true;
+    hb_set_t features_set;
     for (; *features; features++)
+      features_set.add (*features);
+    
+    for (unsigned i = 0; i < g.get_feature_count (); i++)
     {
-      hb_tag_t tag = *features;
-      unsigned index;
-      g.find_feature_index (tag, &index);
-      if (index == OT::Index::NOT_FOUND_INDEX) continue;
-
-      feature_indices_filter.add(index);
-      for (int i = (int) index - 1; i >= 0; i--)
-      {
-        if (g.get_feature_tag (i) != tag) break;
+      hb_tag_t tag = g.get_feature_tag (i);
+      if (features_set.has (tag))
         feature_indices_filter.add(i);
-      }
-      for (unsigned i = index + 1; i < g.get_feature_count (); i++)
-      {
-        if (g.get_feature_tag (i) != tag) break;
-        feature_indices_filter.add(i);
-      }
     }
   }
 
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index f530fad..33ffb9e 100644
--- a/test/subset/data/Makefile.am
+++ b/test/subset/data/Makefile.am
@@ -36,6 +36,7 @@
 	expected/layout.notonastaliqurdu \
 	expected/layout.tinos \
 	expected/layout.duplicate_features \
+	expected/layout.unsorted_featurelist \
 	expected/cmap \
 	expected/cmap14 \
 	expected/sbix \
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index 2ed1056..34f2f07 100644
--- a/test/subset/data/Makefile.sources
+++ b/test/subset/data/Makefile.sources
@@ -34,6 +34,7 @@
 	tests/layout.tests \
 	tests/layout.tinos.tests \
 	tests/layout.duplicate_features.tests \
+	tests/layout.unsorted_featurelist.tests \
 	tests/sbix.tests \
 	tests/variable.tests \
 	tests/glyph_names.tests \
diff --git a/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.default.392,3a7,3b2,3c7.ttf b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.default.392,3a7,3b2,3c7.ttf
new file mode 100644
index 0000000..3fb42c6
--- /dev/null
+++ b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.default.392,3a7,3b2,3c7.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.default.retain-all-codepoint.ttf b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.default.retain-all-codepoint.ttf
new file mode 100644
index 0000000..decba00
--- /dev/null
+++ b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.default.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.layout-test.392,3a7,3b2,3c7.ttf b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.layout-test.392,3a7,3b2,3c7.ttf
new file mode 100644
index 0000000..a0b6145
--- /dev/null
+++ b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.layout-test.392,3a7,3b2,3c7.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.layout-test.retain-all-codepoint.ttf b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.layout-test.retain-all-codepoint.ttf
new file mode 100644
index 0000000..2d76b5a
--- /dev/null
+++ b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.layout-test.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.retain-gids.392,3a7,3b2,3c7.ttf b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.retain-gids.392,3a7,3b2,3c7.ttf
new file mode 100644
index 0000000..012bb16
--- /dev/null
+++ b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.retain-gids.392,3a7,3b2,3c7.ttf
Binary files differ
diff --git a/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.retain-gids.retain-all-codepoint.ttf b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.retain-gids.retain-all-codepoint.ttf
new file mode 100644
index 0000000..7ddb09a
--- /dev/null
+++ b/test/subset/data/expected/layout.unsorted_featurelist/NotoIKEAHebrewLatin-Regular.retain-gids.retain-all-codepoint.ttf
Binary files differ
diff --git a/test/subset/data/fonts/NotoIKEAHebrewLatin-Regular.ttf b/test/subset/data/fonts/NotoIKEAHebrewLatin-Regular.ttf
new file mode 100644
index 0000000..7566808
--- /dev/null
+++ b/test/subset/data/fonts/NotoIKEAHebrewLatin-Regular.ttf
Binary files differ
diff --git a/test/subset/data/tests/layout.unsorted_featurelist.tests b/test/subset/data/tests/layout.unsorted_featurelist.tests
new file mode 100644
index 0000000..9c77a7a
--- /dev/null
+++ b/test/subset/data/tests/layout.unsorted_featurelist.tests
@@ -0,0 +1,11 @@
+FONTS:
+NotoIKEAHebrewLatin-Regular.ttf
+
+PROFILES:
+layout-test.txt
+default.txt
+retain-gids.txt
+
+SUBSETS:
+U+392,U+3a7,U+3b2,U+3c7
+*
diff --git a/test/subset/meson.build b/test/subset/meson.build
index 9a5377c..a287136 100644
--- a/test/subset/meson.build
+++ b/test/subset/meson.build
@@ -28,6 +28,7 @@
   'layout.notonastaliqurdu',
   'layout.tinos',
   'layout.duplicate_features',
+  'layout.unsorted_featurelist',
   'cmap',
   'cmap14',
   'sbix',