[layout] Memoize collect_features

Fixes https://github.com/harfbuzz/harfbuzz/pull/1317
Fixes https://oss-fuzz.com/v2/testcase-detail/6543700493598720
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index aa1a357..c2803f3 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -664,8 +664,46 @@
     : g (get_gsubgpos_table (face, table_tag)),
       feature_indexes (feature_indexes_) {}
 
+  bool inline visited (const OT::Script &s)
+  {
+    /* We might have Null() object here.  Don't want to involve
+     * that in the memoize.  So, detect empty objects and return. */
+    if (unlikely (!s.has_default_lang_sys () &&
+		  !s.get_lang_sys_count ()))
+      return true;
+
+    return visited (s, visited_script);
+  }
+  bool inline visited (const OT::LangSys &l)
+  {
+    /* We might have Null() object here.  Don't want to involve
+     * that in the memoize.  So, detect empty objects and return. */
+    if (unlikely (!l.has_required_feature () &&
+		  !l.get_feature_count ()))
+      return true;
+
+    return visited (l, visited_langsys);
+  }
+
+  private:
+  template <typename T>
+  bool inline visited (const T &p, hb_set_t &visited_set)
+  {
+    hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g);
+     if (visited_set.has (delta))
+      return true;
+
+    visited_set.add (delta);
+    return false;
+  }
+
+  public:
   const OT::GSUBGPOS &g;
   hb_set_t           *feature_indexes;
+
+  private:
+  hb_auto_t<hb_set_t> visited_script;
+  hb_auto_t<hb_set_t> visited_langsys;
 };
 
 static void
@@ -673,12 +711,13 @@
 			  const OT::LangSys  &l,
 			  const hb_tag_t     *features)
 {
+  if (c->visited (l)) return;
+
   if (!features)
   {
     /* All features. */
-    unsigned int index = l.get_required_feature_index ();
-    if (index != HB_OT_LAYOUT_NO_FEATURE_INDEX)
-      c->feature_indexes->add (index);
+    if (l.has_required_feature ())
+      c->feature_indexes->add (l.get_required_feature_index ());
 
     l.add_feature_indexes_to (c->feature_indexes);
   }
@@ -688,7 +727,6 @@
     for (; *features; features++)
     {
       hb_tag_t feature_tag = *features;
-      unsigned int feature_index;
       unsigned int num_features = l.get_feature_count ();
       for (unsigned int i = 0; i < num_features; i++)
       {
@@ -710,12 +748,15 @@
 			 const hb_tag_t *languages,
 			 const hb_tag_t *features)
 {
+  if (c->visited (s)) return;
+
   if (!languages)
   {
     /* All languages. */
-    langsys_collect_features (c,
-			      s.get_default_lang_sys (),
-			      features);
+    if (s.has_default_lang_sys ())
+      langsys_collect_features (c,
+				s.get_default_lang_sys (),
+				features);
 
     unsigned int count = s.get_lang_sys_count ();
     for (unsigned int language_index = 0; language_index < count; language_index++)