[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++)