Imported Upstream version 3.4.0
[platform/upstream/harfbuzz.git] / src / hb-ot-layout-common.hh
index fdd1f31..60a1906 100644 (file)
@@ -68,8 +68,8 @@
 #define HB_MAX_FEATURE_INDICES 1500
 #endif
 
-#ifndef HB_MAX_LOOKUP_INDICES
-#define HB_MAX_LOOKUP_INDICES  20000
+#ifndef HB_MAX_LOOKUP_VISIT_COUNT
+#define HB_MAX_LOOKUP_VISIT_COUNT      35000
 #endif
 
 
@@ -88,12 +88,66 @@ static inline void ClassDef_serialize (hb_serialize_context_t *c,
                                       Iterator it);
 
 static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
-                                         const hb_set_t &glyphset,
                                          const hb_map_t &gid_klass_map,
-                                         hb_sorted_vector_t<HBGlyphID> &glyphs,
+                                         hb_sorted_vector_t<HBGlyphID16> &glyphs,
                                          const hb_set_t &klasses,
+                                         bool use_class_zero,
                                          hb_map_t *klass_map /*INOUT*/);
 
+
+struct hb_prune_langsys_context_t
+{
+  hb_prune_langsys_context_t (const void         *table_,
+                              hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map_,
+                              const hb_map_t     *duplicate_feature_map_,
+                              hb_set_t           *new_collected_feature_indexes_)
+      :table (table_),
+      script_langsys_map (script_langsys_map_),
+      duplicate_feature_map (duplicate_feature_map_),
+      new_feature_indexes (new_collected_feature_indexes_),
+      script_count (0),langsys_count (0) {}
+
+  bool visitedScript (const void *s)
+  {
+    if (script_count++ > HB_MAX_SCRIPTS)
+      return true;
+
+    return visited (s, visited_script);
+  }
+
+  bool visitedLangsys (const void *l)
+  {
+    if (langsys_count++ > HB_MAX_LANGSYS)
+      return true;
+
+    return visited (l, visited_langsys);
+  }
+
+  private:
+  template <typename T>
+  bool visited (const T *p, hb_set_t &visited_set)
+  {
+    hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) p - (uintptr_t) table);
+    if (visited_set.in_error () || visited_set.has (delta))
+      return true;
+
+    visited_set.add (delta);
+    return false;
+  }
+
+  public:
+  const void *table;
+  hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map;
+  const hb_map_t     *duplicate_feature_map;
+  hb_set_t           *new_feature_indexes;
+
+  private:
+  hb_set_t visited_script;
+  hb_set_t visited_langsys;
+  unsigned script_count;
+  unsigned langsys_count;
+};
+
 struct hb_subset_layout_context_t :
   hb_dispatch_context_t<hb_subset_layout_context_t, hb_empty_t, HB_DEBUG_SUBSET>
 {
@@ -119,24 +173,27 @@ struct hb_subset_layout_context_t :
   bool visitLookupIndex()
   {
     lookup_index_count++;
-    return lookup_index_count < HB_MAX_LOOKUP_INDICES;
+    return lookup_index_count < HB_MAX_LOOKUP_VISIT_COUNT;
   }
 
   hb_subset_context_t *subset_context;
   const hb_tag_t table_tag;
   const hb_map_t *lookup_index_map;
+  const hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map;
   const hb_map_t *feature_index_map;
-  unsigned debug_depth;
+  unsigned cur_script_index;
 
   hb_subset_layout_context_t (hb_subset_context_t *c_,
                              hb_tag_t tag_,
                              hb_map_t *lookup_map_,
-                             hb_map_t *feature_map_) :
+                             hb_hashmap_t<unsigned, hb_set_t *> *script_langsys_map_,
+                             hb_map_t *feature_index_map_) :
                                subset_context (c_),
                                table_tag (tag_),
                                lookup_index_map (lookup_map_),
-                               feature_index_map (feature_map_),
-                               debug_depth (0),
+                               script_langsys_map (script_langsys_map_),
+                               feature_index_map (feature_index_map_),
+                               cur_script_index (0xFFFFu),
                                script_count (0),
                                langsys_count (0),
                                feature_index_count (0),
@@ -151,9 +208,8 @@ struct hb_subset_layout_context_t :
 };
 
 struct hb_collect_variation_indices_context_t :
-       hb_dispatch_context_t<hb_collect_variation_indices_context_t, hb_empty_t, 0>
+       hb_dispatch_context_t<hb_collect_variation_indices_context_t>
 {
-  const char *get_name () { return "CLOSURE_LAYOUT_VARIATION_IDXES"; }
   template <typename T>
   return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); }
   static return_t default_return_value () { return hb_empty_t (); }
@@ -161,15 +217,13 @@ struct hb_collect_variation_indices_context_t :
   hb_set_t *layout_variation_indices;
   const hb_set_t *glyph_set;
   const hb_map_t *gpos_lookups;
-  unsigned int debug_depth;
 
   hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_,
                                          const hb_set_t *glyph_set_,
                                          const hb_map_t *gpos_lookups_) :
                                        layout_variation_indices (layout_variation_indices_),
                                        glyph_set (glyph_set_),
-                                       gpos_lookups (gpos_lookups_),
-                                       debug_depth (0) {}
+                                       gpos_lookups (gpos_lookups_) {}
 };
 
 template<typename OutputArray>
@@ -183,9 +237,9 @@ struct subset_offset_array_t
   template <typename T>
   bool operator () (T&& offset)
   {
+    auto snap = subset_context->serializer->snapshot ();
     auto *o = out.serialize_append (subset_context->serializer);
     if (unlikely (!o)) return false;
-    auto snap = subset_context->serializer->snapshot ();
     bool ret = o->serialize_subset (subset_context, offset, base);
     if (!ret)
     {
@@ -214,9 +268,9 @@ struct subset_offset_array_arg_t
   template <typename T>
   bool operator () (T&& offset)
   {
+    auto snap = subset_context->serializer->snapshot ();
     auto *o = out.serialize_append (subset_context->serializer);
     if (unlikely (!o)) return false;
-    auto snap = subset_context->serializer->snapshot ();
     bool ret = o->serialize_subset (subset_context, offset, base, arg);
     if (!ret)
     {
@@ -292,6 +346,43 @@ struct
 }
 HB_FUNCOBJ (subset_record_array);
 
+
+template<typename OutputArray>
+struct serialize_math_record_array_t
+{
+  serialize_math_record_array_t (hb_serialize_context_t *serialize_context_,
+                         OutputArray& out_,
+                         const void *base_) : serialize_context (serialize_context_),
+                                              out (out_), base (base_) {}
+
+  template <typename T>
+  bool operator () (T&& record)
+  {
+    if (!serialize_context->copy (record, base)) return false;
+    out.len++;
+    return true;
+  }
+
+  private:
+  hb_serialize_context_t *serialize_context;
+  OutputArray &out;
+  const void *base;
+};
+
+/*
+ * Helper to serialize an array of MATH records.
+ */
+struct
+{
+  template<typename OutputArray>
+  serialize_math_record_array_t<OutputArray>
+  operator () (hb_serialize_context_t *serialize_context, OutputArray& out,
+               const void *base) const
+  { return serialize_math_record_array_t<OutputArray> (serialize_context, out, base); }
+
+}
+HB_FUNCOBJ (serialize_math_record_array);
+
 /*
  *
  * OpenType Layout Common Table Formats
@@ -330,7 +421,7 @@ struct Record
   }
 
   Tag          tag;            /* 4-byte Tag identifier */
-  OffsetTo<Type>
+  Offset16To<Type>
                offset;         /* Offset from beginning of object holding
                                 * the Record */
   public:
@@ -338,11 +429,11 @@ struct Record
 };
 
 template <typename Type>
-struct RecordArrayOf : SortedArrayOf<Record<Type>>
+struct RecordArrayOf : SortedArray16Of<Record<Type>>
 {
-  const OffsetTo<Type>& get_offset (unsigned int i) const
+  const Offset16To<Type>& get_offset (unsigned int i) const
   { return (*this)[i].offset; }
-  OffsetTo<Type>& get_offset (unsigned int i)
+  Offset16To<Type>& get_offset (unsigned int i)
   { return (*this)[i].offset; }
   const Tag& get_tag (unsigned int i) const
   { return (*this)[i].tag; }
@@ -350,17 +441,18 @@ struct RecordArrayOf : SortedArrayOf<Record<Type>>
                         unsigned int *record_count /* IN/OUT */,
                         hb_tag_t     *record_tags /* OUT */) const
   {
-    if (record_count) {
-      const Record<Type> *arr = this->sub_array (start_offset, record_count);
-      unsigned int count = *record_count;
-      for (unsigned int i = 0; i < count; i++)
-       record_tags[i] = arr[i].tag;
+    if (record_count)
+    {
+      + this->sub_array (start_offset, record_count)
+      | hb_map (&Record<Type>::tag)
+      | hb_sink (hb_array (record_tags, *record_count))
+      ;
     }
     return this->len;
   }
   bool find_index (hb_tag_t tag, unsigned int *index) const
   {
-    return this->bfind (tag, index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
+    return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
   }
 };
 
@@ -411,6 +503,30 @@ struct RecordListOfFeature : RecordListOf<Feature>
   }
 };
 
+struct Script;
+struct RecordListOfScript : RecordListOf<Script>
+{
+  bool subset (hb_subset_context_t *c,
+               hb_subset_layout_context_t *l) const
+  {
+    TRACE_SUBSET (this);
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+
+    unsigned count = this->len;
+    for (auto _ : + hb_zip (*this, hb_range (count)))
+    {
+      auto snap = c->serializer->snapshot ();
+      l->cur_script_index = _.second;
+      bool ret = _.first.subset (l, this);
+      if (!ret) c->serializer->revert (snap);
+      else out->len++;
+    }
+
+    return_trace (true);
+  }
+};
+
 struct RangeRecord
 {
   int cmp (hb_codepoint_t g) const
@@ -429,8 +545,8 @@ struct RangeRecord
   bool collect_coverage (set_t *glyphs) const
   { return glyphs->add_range (first, last); }
 
-  HBGlyphID    first;          /* First GlyphID in the range */
-  HBGlyphID    last;           /* Last GlyphID in the range */
+  HBGlyphID16  first;          /* First GlyphID in the range */
+  HBGlyphID16  last;           /* Last GlyphID in the range */
   HBUINT16     value;          /* Value */
   public:
   DEFINE_SIZE_STATIC (6);
@@ -438,7 +554,7 @@ struct RangeRecord
 DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord);
 
 
-struct IndexArray : ArrayOf<Index>
+struct IndexArray : Array16Of<Index>
 {
   bool intersects (const hb_map_t *indexes) const
   { return hb_any (*this, indexes); }
@@ -467,18 +583,18 @@ struct IndexArray : ArrayOf<Index>
                            unsigned int *_count /* IN/OUT */,
                            unsigned int *_indexes /* OUT */) const
   {
-    if (_count) {
-      const HBUINT16 *arr = this->sub_array (start_offset, _count);
-      unsigned int count = *_count;
-      for (unsigned int i = 0; i < count; i++)
-       _indexes[i] = arr[i];
+    if (_count)
+    {
+      + this->sub_array (start_offset, _count)
+      | hb_sink (hb_array (_indexes, *_count))
+      ;
     }
     return this->len;
   }
 
   void add_indexes_to (hb_set_t* output /* OUT */) const
   {
-    output->add_array (arrayZ, len);
+    output->add_array (as_array ());
   }
 };
 
@@ -510,18 +626,45 @@ struct LangSys
     return_trace (c->embed (*this));
   }
 
-  bool operator == (const LangSys& o) const
+  bool compare (const LangSys& o, const hb_map_t *feature_index_map) const
   {
-    if (featureIndex.len != o.featureIndex.len ||
-       reqFeatureIndex != o.reqFeatureIndex)
+    if (reqFeatureIndex != o.reqFeatureIndex)
       return false;
 
-    for (const auto _ : + hb_zip (featureIndex, o.featureIndex))
+    auto iter =
+    + hb_iter (featureIndex)
+    | hb_filter (feature_index_map)
+    | hb_map (feature_index_map)
+    ;
+
+    auto o_iter =
+    + hb_iter (o.featureIndex)
+    | hb_filter (feature_index_map)
+    | hb_map (feature_index_map)
+    ;
+
+    if (iter.len () != o_iter.len ())
+      return false;
+
+    for (const auto _ : + hb_zip (iter, o_iter))
       if (_.first != _.second) return false;
 
     return true;
   }
 
+  void collect_features (hb_prune_langsys_context_t *c) const
+  {
+    if (!has_required_feature () && !get_feature_count ()) return;
+    if (has_required_feature () &&
+        c->duplicate_feature_map->has (reqFeatureIndex))
+      c->new_feature_indexes->add (get_required_feature_index ());
+
+    + hb_iter (featureIndex)
+    | hb_filter (c->duplicate_feature_map)
+    | hb_sink (c->new_feature_indexes)
+    ;
+  }
+
   bool subset (hb_subset_context_t        *c,
               hb_subset_layout_context_t *l,
               const Tag                  *tag = nullptr) const
@@ -585,6 +728,54 @@ struct Script
   bool has_default_lang_sys () const           { return defaultLangSys != 0; }
   const LangSys& get_default_lang_sys () const { return this+defaultLangSys; }
 
+  void prune_langsys (hb_prune_langsys_context_t *c,
+                      unsigned script_index) const
+  {
+    if (!has_default_lang_sys () && !get_lang_sys_count ()) return;
+    if (c->visitedScript (this)) return;
+
+    if (!c->script_langsys_map->has (script_index))
+    {
+      hb_set_t* empty_set = hb_set_create ();
+      if (unlikely (!c->script_langsys_map->set (script_index, empty_set)))
+      {
+       hb_set_destroy (empty_set);
+       return;
+      }
+    }
+
+    unsigned langsys_count = get_lang_sys_count ();
+    if (has_default_lang_sys ())
+    {
+      //only collect features from non-redundant langsys
+      const LangSys& d = get_default_lang_sys ();
+      if (!c->visitedLangsys (&d)) {
+        d.collect_features (c);
+      }
+
+      for (auto _ : + hb_zip (langSys, hb_range (langsys_count)))
+      {
+
+        const LangSys& l = this+_.first.offset;
+        if (c->visitedLangsys (&l)) continue;
+        if (l.compare (d, c->duplicate_feature_map)) continue;
+
+        l.collect_features (c);
+        c->script_langsys_map->get (script_index)->add (_.second);
+      }
+    }
+    else
+    {
+      for (auto _ : + hb_zip (langSys, hb_range (langsys_count)))
+      {
+        const LangSys& l = this+_.first.offset;
+        if (c->visitedLangsys (&l)) continue;
+        l.collect_features (c);
+        c->script_langsys_map->get (script_index)->add (_.second);
+      }
+    }
+  }
+
   bool subset (hb_subset_context_t         *c,
               hb_subset_layout_context_t  *l,
               const Tag                   *tag) const
@@ -613,16 +804,17 @@ struct Script
       }
     }
 
-    + langSys.iter ()
-    | hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); })
-    | hb_filter ([&] (const Record<LangSys>& record)
-                {
-                  const LangSys& d = this+defaultLangSys;
-                  const LangSys& l = this+record.offset;
-                  return !(l == d);
-                })
-    | hb_apply (subset_record_array (l, &(out->langSys), this))
-    ;
+    const hb_set_t *active_langsys = l->script_langsys_map->get (l->cur_script_index);
+    if (active_langsys)
+    {
+      unsigned count = langSys.len;
+      + hb_zip (langSys, hb_range (count))
+      | hb_filter (active_langsys, hb_second)
+      | hb_map (hb_first)
+      | hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); })
+      | hb_apply (subset_record_array (l, &(out->langSys), this))
+      ;
+    }
 
     return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB);
   }
@@ -635,7 +827,7 @@ struct Script
   }
 
   protected:
-  OffsetTo<LangSys>
+  Offset16To<LangSys>
                defaultLangSys; /* Offset to DefaultLangSys table--from
                                 * beginning of Script table--may be Null */
   RecordArrayOf<LangSys>
@@ -645,7 +837,7 @@ struct Script
   DEFINE_SIZE_ARRAY_SIZED (4, langSys);
 };
 
-typedef RecordListOf<Script> ScriptList;
+typedef RecordListOfScript ScriptList;
 
 
 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */
@@ -657,7 +849,7 @@ struct FeatureParamsSize
     if (unlikely (!c->check_struct (this))) return_trace (false);
 
     /* This subtable has some "history", if you will.  Some earlier versions of
-     * Adobe tools calculated the offset of the FeatureParams sutable from the
+     * Adobe tools calculated the offset of the FeatureParams subtable from the
      * beginning of the FeatureList table!  Now, that is dealt with in the
      * Feature implementation.  But we still need to be able to tell junk from
      * real data.  Note: We don't check that the nameID actually exists.
@@ -809,11 +1001,16 @@ struct FeatureParamsStylisticSet
 /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */
 struct FeatureParamsCharacterVariants
 {
-  bool sanitize (hb_sanitize_context_t *c) const
+  unsigned
+  get_characters (unsigned start_offset, unsigned *char_count, hb_codepoint_t *chars) const
   {
-    TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-                 characters.sanitize (c));
+    if (char_count)
+    {
+      + characters.sub_array (start_offset, char_count)
+      | hb_sink (hb_array (chars, *char_count))
+      ;
+    }
+    return characters.len;
   }
 
   unsigned get_size () const
@@ -825,6 +1022,13 @@ struct FeatureParamsCharacterVariants
     return_trace ((bool) c->serializer->embed (*this));
   }
 
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                 characters.sanitize (c));
+  }
+
   HBUINT16     format;                 /* Format number is set to 0. */
   NameID       featUILableNameID;      /* The â€˜name’ table name ID that
                                         * specifies a string (or strings,
@@ -848,7 +1052,7 @@ struct FeatureParamsCharacterVariants
                                         * user-interface labels for the
                                         * feature parameters. (Must be zero
                                         * if numParameters is zero.) */
-  ArrayOf<HBUINT24>
+  Array16Of<HBUINT24>
                characters;             /* Array of the Unicode Scalar Value
                                         * of the characters for which this
                                         * feature provides glyph variants.
@@ -945,7 +1149,7 @@ struct Feature
     auto *out = c->serializer->start_embed (*this);
     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
 
-    bool subset_featureParams = out->featureParams.serialize_subset (c, featureParams, this, tag);
+    out->featureParams.serialize_subset (c, featureParams, this, tag);
 
     auto it =
     + hb_iter (lookupIndex)
@@ -954,8 +1158,9 @@ struct Feature
     ;
 
     out->lookupIndex.serialize (c->serializer, l, it);
-    return_trace (bool (it) || subset_featureParams
-                  || (tag && *tag == HB_TAG ('p', 'r', 'e', 'f')));
+    // The decision to keep or drop this feature is already made before we get here
+    // so always retain it.
+    return_trace (true);
   }
 
   bool sanitize (hb_sanitize_context_t *c,
@@ -990,7 +1195,7 @@ struct Feature
       unsigned int new_offset_int = orig_offset -
                                    (((char *) this) - ((char *) closure->list_base));
 
-      OffsetTo<FeatureParams> new_offset;
+      Offset16To<FeatureParams> new_offset;
       /* Check that it would not overflow. */
       new_offset = new_offset_int;
       if (new_offset == new_offset_int &&
@@ -1002,7 +1207,7 @@ struct Feature
     return_trace (true);
   }
 
-  OffsetTo<FeatureParams>
+  Offset16To<FeatureParams>
                 featureParams; /* Offset to Feature Parameters table (if one
                                 * has been defined for the feature), relative
                                 * to the beginning of the Feature Table; = Null
@@ -1041,11 +1246,11 @@ struct Lookup
   unsigned int get_subtable_count () const { return subTable.len; }
 
   template <typename TSubTable>
-  const OffsetArrayOf<TSubTable>& get_subtables () const
-  { return reinterpret_cast<const OffsetArrayOf<TSubTable> &> (subTable); }
+  const Array16OfOffset16To<TSubTable>& get_subtables () const
+  { return reinterpret_cast<const Array16OfOffset16To<TSubTable> &> (subTable); }
   template <typename TSubTable>
-  OffsetArrayOf<TSubTable>& get_subtables ()
-  { return reinterpret_cast<OffsetArrayOf<TSubTable> &> (subTable); }
+  Array16OfOffset16To<TSubTable>& get_subtables ()
+  { return reinterpret_cast<Array16OfOffset16To<TSubTable> &> (subTable); }
 
   template <typename TSubTable>
   const TSubTable& get_subtable (unsigned int i) const
@@ -1085,7 +1290,7 @@ struct Lookup
     TRACE_DISPATCH (this, lookup_type);
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      typename context_t::return_t r = get_subtable<TSubTable> (i).dispatch (c, lookup_type, hb_forward<Ts> (ds)...);
+      typename context_t::return_t r = get_subtable<TSubTable> (i).dispatch (c, lookup_type, std::forward<Ts> (ds)...);
       if (c->stop_sublookup_iteration (r))
        return_trace (r);
     }
@@ -1098,13 +1303,13 @@ struct Lookup
                  unsigned int num_subtables)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
     lookupType = lookup_type;
     lookupFlag = lookup_props & 0xFFFFu;
     if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false);
     if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
-      if (unlikely (!c->extend (*this))) return_trace (false);
+      if (unlikely (!c->extend (this))) return_trace (false);
       HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
       markFilteringSet = lookup_props >> 16;
     }
@@ -1120,14 +1325,22 @@ struct Lookup
     out->lookupType = lookupType;
     out->lookupFlag = lookupFlag;
 
-    const hb_set_t *glyphset = c->plan->glyphset ();
+    const hb_set_t *glyphset = c->plan->glyphset_gsub ();
     unsigned int lookup_type = get_type ();
     + hb_iter (get_subtables <TSubTable> ())
-    | hb_filter ([this, glyphset, lookup_type] (const OffsetTo<TSubTable> &_) { return (this+_).intersects (glyphset, lookup_type); })
+    | hb_filter ([this, glyphset, lookup_type] (const Offset16To<TSubTable> &_) { return (this+_).intersects (glyphset, lookup_type); })
     | hb_apply (subset_offset_array (c, out->get_subtables<TSubTable> (), this, lookup_type))
     ;
 
-    return_trace (true);
+    if (lookupFlag & LookupFlag::UseMarkFilteringSet)
+    {
+      if (unlikely (!c->serializer->extend (out))) return_trace (false);
+      const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable);
+      HBUINT16 &outMarkFilteringSet = StructAfter<HBUINT16> (out->subTable);
+      outMarkFilteringSet = markFilteringSet;
+    }
+
+    return_trace (out->subTable.len);
   }
 
   template <typename TSubTable>
@@ -1148,7 +1361,7 @@ struct Lookup
     if (unlikely (!get_subtables<TSubTable> ().sanitize (c, this, get_type ())))
       return_trace (false);
 
-    if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ()))
+    if (unlikely (get_type () == TSubTable::Extension && subtables && !c->get_edit_count ()))
     {
       /* The spec says all subtables of an Extension lookup should
        * have the same type, which shall not be the Extension type
@@ -1171,7 +1384,7 @@ struct Lookup
   private:
   HBUINT16     lookupType;             /* Different enumerations for GSUB and GPOS */
   HBUINT16     lookupFlag;             /* Lookup qualifiers */
-  ArrayOf<Offset16>
+  Array16Of<Offset16>
                subTable;               /* Array of SubTables */
 /*HBUINT16     markFilteringSetX[HB_VAR_ARRAY];*//* Index (base 0) into GDEF mark glyph sets
                                         * structure. This field is only present if bit
@@ -1180,10 +1393,10 @@ struct Lookup
   DEFINE_SIZE_ARRAY (6, subTable);
 };
 
-typedef OffsetListOf<Lookup> LookupList;
+typedef List16OfOffset16To<Lookup> LookupList;
 
 template <typename TLookup>
-struct LookupOffsetList : OffsetListOf<TLookup>
+struct LookupOffsetList : List16OfOffset16To<TLookup>
 {
   bool subset (hb_subset_context_t        *c,
               hb_subset_layout_context_t *l) const
@@ -1204,7 +1417,7 @@ struct LookupOffsetList : OffsetListOf<TLookup>
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (OffsetListOf<TLookup>::sanitize (c, this));
+    return_trace (List16OfOffset16To<TLookup>::sanitize (c, this));
   }
 };
 
@@ -1221,7 +1434,7 @@ struct CoverageFormat1
   unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     unsigned int i;
-    glyphArray.bfind (glyph_id, &i, HB_BFIND_NOT_FOUND_STORE, NOT_COVERED);
+    glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED);
     return i;
   }
 
@@ -1242,18 +1455,25 @@ struct CoverageFormat1
   bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
-    unsigned int count = glyphArray.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (glyphs->has (glyphArray[i]))
+    for (const auto& g : glyphArray.as_array ())
+      if (glyphs->has (g))
        return true;
     return false;
   }
   bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   { return glyphs->has (glyphArray[index]); }
 
+  void intersected_coverage_glyphs (const hb_set_t *glyphs, hb_set_t *intersect_glyphs) const
+  {
+    unsigned count = glyphArray.len;
+    for (unsigned i = 0; i < count; i++)
+      if (glyphs->has (glyphArray[i]))
+        intersect_glyphs->add (glyphArray[i]);
+  }
+
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
-  { return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len); }
+  { return glyphs->add_sorted_array (glyphArray.as_array ()); }
 
   public:
   /* Older compilers need this to be public. */
@@ -1275,7 +1495,7 @@ struct CoverageFormat1
 
   protected:
   HBUINT16     coverageFormat; /* Format identifier--format = 1 */
-  SortedArrayOf<HBGlyphID>
+  SortedArray16Of<HBGlyphID16>
                glyphArray;     /* Array of GlyphIDs--in numerical order */
   public:
   DEFINE_SIZE_ARRAY (4, glyphArray);
@@ -1289,9 +1509,9 @@ struct CoverageFormat2
   unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     const RangeRecord &range = rangeRecord.bsearch (glyph_id);
-    return likely (range.first <= range.last) ?
-          (unsigned int) range.value + (glyph_id - range.first) :
-          NOT_COVERED;
+    return likely (range.first <= range.last)
+        ? (unsigned int) range.value + (glyph_id - range.first)
+        : NOT_COVERED;
   }
 
   template <typename Iterator,
@@ -1299,7 +1519,7 @@ struct CoverageFormat2
   bool serialize (hb_serialize_context_t *c, Iterator glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
 
     if (unlikely (!glyphs))
     {
@@ -1348,18 +1568,17 @@ struct CoverageFormat2
   bool intersects (const hb_set_t *glyphs) const
   {
     /* TODO Speed up, using hb_set_next() and bsearch()? */
-    unsigned int count = rangeRecord.len;
-    for (unsigned int i = 0; i < count; i++)
-      if (rangeRecord[i].intersects (glyphs))
+    /* TODO(iter) Rewrite as dagger. */
+    for (const auto& range : rangeRecord.as_array ())
+      if (range.intersects (glyphs))
        return true;
     return false;
   }
   bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
   {
-    unsigned int i;
-    unsigned int count = rangeRecord.len;
-    for (i = 0; i < count; i++) {
-      const RangeRecord &range = rangeRecord[i];
+    /* TODO(iter) Rewrite as dagger. */
+    for (const auto& range : rangeRecord.as_array ())
+    {
       if (range.value <= index &&
          index < (unsigned int) range.value + (range.last - range.first) &&
          range.intersects (glyphs))
@@ -1370,6 +1589,16 @@ struct CoverageFormat2
     return false;
   }
 
+  void intersected_coverage_glyphs (const hb_set_t *glyphs, hb_set_t *intersect_glyphs) const
+  {
+    for (const auto& range : rangeRecord.as_array ())
+    {
+      if (!range.intersects (glyphs)) continue;
+      for (hb_codepoint_t g = range.first; g <= range.last; g++)
+        if (glyphs->has (g)) intersect_glyphs->add (g);
+    }
+  }
+
   template <typename set_t>
   bool collect_coverage (set_t *glyphs) const
   {
@@ -1436,7 +1665,7 @@ struct CoverageFormat2
 
   protected:
   HBUINT16     coverageFormat; /* Format identifier--format = 2 */
-  SortedArrayOf<RangeRecord>
+  SortedArray16Of<RangeRecord>
                rangeRecord;    /* Array of glyph ranges--ordered by
                                 * Start GlyphID. rangeCount entries
                                 * long */
@@ -1469,7 +1698,7 @@ struct Coverage
   bool serialize (hb_serialize_context_t *c, Iterator glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
 
     unsigned count = 0;
     unsigned num_ranges = 0;
@@ -1494,7 +1723,7 @@ struct Coverage
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
     auto it =
@@ -1552,6 +1781,16 @@ struct Coverage
     }
   }
 
+  void intersected_coverage_glyphs (const hb_set_t *glyphs, hb_set_t *intersect_glyphs) const
+  {
+    switch (u.format)
+    {
+    case 1: return u.format1.intersected_coverage_glyphs (glyphs, intersect_glyphs);
+    case 2: return u.format2.intersected_coverage_glyphs (glyphs, intersect_glyphs);
+    default:return ;
+    }
+  }
+
   struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
   {
     static constexpr bool is_sorted_iterator = true;
@@ -1633,10 +1872,10 @@ Coverage_serialize (hb_serialize_context_t *c,
 { c->start_embed<Coverage> ()->serialize (c, it); }
 
 static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
-                                         const hb_set_t &glyphset,
                                          const hb_map_t &gid_klass_map,
-                                         hb_sorted_vector_t<HBGlyphID> &glyphs,
+                                         hb_sorted_vector_t<HBGlyphID16> &glyphs,
                                          const hb_set_t &klasses,
+                                          bool use_class_zero,
                                          hb_map_t *klass_map /*INOUT*/)
 {
   if (!klass_map)
@@ -1648,7 +1887,7 @@ static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
 
   /* any glyph not assigned a class value falls into Class zero (0),
    * if any glyph assigned to class 0, remapping must start with 0->0*/
-  if (glyphset.get_population () > gid_klass_map.get_population ())
+  if (!use_class_zero)
     klass_map->set (0, 0);
 
   unsigned idx = klass_map->has (0) ? 1 : 0;
@@ -1661,7 +1900,7 @@ static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
 
   auto it =
   + glyphs.iter ()
-  | hb_map_retains_sorting ([&] (const HBGlyphID& gid) -> hb_pair_t<hb_codepoint_t, unsigned>
+  | hb_map_retains_sorting ([&] (const HBGlyphID16& gid) -> hb_pair_t<hb_codepoint_t, unsigned>
                            {
                              unsigned new_klass = klass_map->get (gid_klass_map[gid]);
                              return hb_pair ((hb_codepoint_t)gid, new_klass);
@@ -1692,10 +1931,11 @@ struct ClassDefFormat1
                  Iterator it)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
 
     if (unlikely (!it))
     {
+      classFormat = 1;
       startGlyph = 0;
       classValue.len = 0;
       return_trace (true);
@@ -1709,7 +1949,7 @@ struct ClassDefFormat1
 
     startGlyph = glyph_min;
     if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false);
-    for (const hb_pair_t<hb_codepoint_t, unsigned>& gid_klass_pair : + it)
+    for (const hb_pair_t<hb_codepoint_t, unsigned> gid_klass_pair : + it)
     {
       unsigned idx = gid_klass_pair.first - glyph_min;
       classValue[idx] = gid_klass_pair.second;
@@ -1718,21 +1958,27 @@ struct ClassDefFormat1
   }
 
   bool subset (hb_subset_context_t *c,
-              hb_map_t *klass_map = nullptr /*OUT*/) const
+              hb_map_t *klass_map = nullptr /*OUT*/,
+               bool keep_empty_table = true,
+               bool use_class_zero = true,
+               const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
-    hb_sorted_vector_t<HBGlyphID> glyphs;
+    hb_sorted_vector_t<HBGlyphID16> glyphs;
     hb_set_t orig_klasses;
     hb_map_t gid_org_klass_map;
 
     hb_codepoint_t start = startGlyph;
     hb_codepoint_t end   = start + classValue.len;
+
     for (const hb_codepoint_t gid : + hb_range (start, end)
-                                   | hb_filter (glyphset))
+                                    | hb_filter (glyphset))
     {
+      if (glyph_filter && !glyph_filter->has(gid)) continue;
+
       unsigned klass = classValue[gid - start];
       if (!klass) continue;
 
@@ -1741,9 +1987,13 @@ struct ClassDefFormat1
       orig_klasses.add (klass);
     }
 
-    ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map,
-                                 glyphs, orig_klasses, klass_map);
-    return_trace ((bool) glyphs);
+    unsigned glyph_count = glyph_filter
+                           ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter))
+                           : glyphset.get_population ();
+    use_class_zero = use_class_zero && glyph_count <= gid_org_klass_map.get_population ();
+    ClassDef_remap_and_serialize (c->serializer, gid_org_klass_map,
+                                 glyphs, orig_klasses, use_class_zero, klass_map);
+    return_trace (keep_empty_table || (bool) glyphs);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -1776,7 +2026,7 @@ struct ClassDefFormat1
   }
 
   template <typename set_t>
-  bool collect_class (set_t *glyphs, unsigned int klass) const
+  bool collect_class (set_t *glyphs, unsigned klass) const
   {
     unsigned int count = classValue.len;
     for (unsigned int i = 0; i < count; i++)
@@ -1794,7 +2044,7 @@ struct ClassDefFormat1
       if (classValue[iter - start]) return true;
     return false;
   }
-  bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+  bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const
   {
     unsigned int count = classValue.len;
     if (klass == 0)
@@ -1807,16 +2057,54 @@ struct ClassDefFormat1
       if (hb_set_next (glyphs, &g)) return true;
       /* Fall through. */
     }
+    /* TODO Speed up, using set overlap first? */
+    /* TODO(iter) Rewrite as dagger. */
+    HBUINT16 k {klass};
+    const HBUINT16 *arr = classValue.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (classValue[i] == klass && glyphs->has (startGlyph + i))
+      if (arr[i] == k && glyphs->has (startGlyph + i))
        return true;
     return false;
   }
 
+  void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const
+  {
+    unsigned count = classValue.len;
+    if (klass == 0)
+    {
+      hb_codepoint_t endGlyph = startGlyph + count -1;
+      for (hb_codepoint_t g : glyphs->iter ())
+        if (g < startGlyph || g > endGlyph)
+          intersect_glyphs->add (g);
+
+      return;
+    }
+
+    for (unsigned i = 0; i < count; i++)
+      if (classValue[i] == klass && glyphs->has (startGlyph + i))
+        intersect_glyphs->add (startGlyph + i);
+  }
+
+  void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const
+  {
+    if (glyphs->is_empty ()) return;
+    hb_codepoint_t end_glyph = startGlyph + classValue.len - 1;
+    if (glyphs->get_min () < startGlyph ||
+        glyphs->get_max () > end_glyph)
+      intersect_classes->add (0);
+
+    for (const auto& _ : + hb_enumerate (classValue))
+    {
+      hb_codepoint_t g = startGlyph + _.first;
+      if (glyphs->has (g))
+        intersect_classes->add (_.second);
+    }
+  }
+
   protected:
   HBUINT16     classFormat;    /* Format identifier--format = 1 */
-  HBGlyphID    startGlyph;     /* First GlyphID of the classValueArray */
-  ArrayOf<HBUINT16>
+  HBGlyphID16  startGlyph;     /* First GlyphID of the classValueArray */
+  Array16Of<HBUINT16>
                classValue;     /* Array of Class Values--one per GlyphID */
   public:
   DEFINE_SIZE_ARRAY (6, classValue);
@@ -1838,10 +2126,11 @@ struct ClassDefFormat2
                  Iterator it)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
 
     if (unlikely (!it))
     {
+      classFormat = 2;
       rangeRecord.len = 0;
       return_trace (true);
     }
@@ -1887,13 +2176,16 @@ struct ClassDefFormat2
   }
 
   bool subset (hb_subset_context_t *c,
-              hb_map_t *klass_map = nullptr /*OUT*/) const
+              hb_map_t *klass_map = nullptr /*OUT*/,
+               bool keep_empty_table = true,
+               bool use_class_zero = true,
+               const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
-    const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+    const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
     const hb_map_t &glyph_map = *c->plan->glyph_map;
 
-    hb_sorted_vector_t<HBGlyphID> glyphs;
+    hb_sorted_vector_t<HBGlyphID16> glyphs;
     hb_set_t orig_klasses;
     hb_map_t gid_org_klass_map;
 
@@ -1907,15 +2199,20 @@ struct ClassDefFormat2
       for (hb_codepoint_t g = start; g < end; g++)
       {
        if (!glyphset.has (g)) continue;
+        if (glyph_filter && !glyph_filter->has (g)) continue;
        glyphs.push (glyph_map[g]);
        gid_org_klass_map.set (glyph_map[g], klass);
        orig_klasses.add (klass);
       }
     }
 
-    ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map,
-                                 glyphs, orig_klasses, klass_map);
-    return_trace ((bool) glyphs);
+    unsigned glyph_count = glyph_filter
+                           ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter))
+                           : glyphset.get_population ();
+    use_class_zero = use_class_zero && glyph_count <= gid_org_klass_map.get_population ();
+    ClassDef_remap_and_serialize (c->serializer, gid_org_klass_map,
+                                 glyphs, orig_klasses, use_class_zero, klass_map);
+    return_trace (keep_empty_table || (bool) glyphs);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -1953,11 +2250,14 @@ struct ClassDefFormat2
     /* TODO Speed up, using hb_set_next() and bsearch()? */
     unsigned int count = rangeRecord.len;
     for (unsigned int i = 0; i < count; i++)
-      if (rangeRecord[i].intersects (glyphs))
+    {
+      const auto& range = rangeRecord[i];
+      if (range.intersects (glyphs) && range.value)
        return true;
+    }
     return false;
   }
-  bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+  bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const
   {
     unsigned int count = rangeRecord.len;
     if (klass == 0)
@@ -1976,15 +2276,92 @@ struct ClassDefFormat2
        return true;
       /* Fall through. */
     }
+    /* TODO Speed up, using set overlap first? */
+    /* TODO(iter) Rewrite as dagger. */
+    HBUINT16 k {klass};
+    const RangeRecord *arr = rangeRecord.arrayZ;
     for (unsigned int i = 0; i < count; i++)
-      if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
+      if (arr[i].value == k && arr[i].intersects (glyphs))
        return true;
     return false;
   }
 
+  void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const
+  {
+    unsigned count = rangeRecord.len;
+    if (klass == 0)
+    {
+      hb_codepoint_t g = HB_SET_VALUE_INVALID;
+      for (unsigned int i = 0; i < count; i++)
+      {
+        if (!hb_set_next (glyphs, &g))
+          break;
+        while (g != HB_SET_VALUE_INVALID && g < rangeRecord[i].first)
+        {
+          intersect_glyphs->add (g);
+          hb_set_next (glyphs, &g);
+        }
+        g = rangeRecord[i].last;
+      }
+      while (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
+        intersect_glyphs->add (g);
+
+      return;
+    }
+
+    hb_codepoint_t g = HB_SET_VALUE_INVALID;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (rangeRecord[i].value != klass) continue;
+
+      if (g != HB_SET_VALUE_INVALID)
+      {
+        if (g >= rangeRecord[i].first &&
+            g <= rangeRecord[i].last)
+          intersect_glyphs->add (g);
+        if (g > rangeRecord[i].last)
+          continue;
+      }
+
+      g = rangeRecord[i].first - 1;
+      while (hb_set_next (glyphs, &g))
+      {
+        if (g >= rangeRecord[i].first && g <= rangeRecord[i].last)
+          intersect_glyphs->add (g);
+        else if (g > rangeRecord[i].last)
+          break;
+      }
+    }
+  }
+
+  void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const
+  {
+    if (glyphs->is_empty ()) return;
+
+    unsigned count = rangeRecord.len;
+    hb_codepoint_t g = HB_SET_VALUE_INVALID;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (!hb_set_next (glyphs, &g))
+        break;
+      if (g < rangeRecord[i].first)
+      {
+        intersect_classes->add (0);
+        break;
+      }
+      g = rangeRecord[i].last;
+    }
+    if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
+      intersect_classes->add (0);
+
+    for (const RangeRecord& record : rangeRecord.iter ())
+      if (record.intersects (glyphs))
+        intersect_classes->add (record.value);
+  }
+
   protected:
   HBUINT16     classFormat;    /* Format identifier--format = 2 */
-  SortedArrayOf<RangeRecord>
+  SortedArray16Of<RangeRecord>
                rangeRecord;    /* Array of glyph ranges--ordered by
                                 * Start GlyphID */
   public:
@@ -2013,19 +2390,20 @@ struct ClassDef
 
   template<typename Iterator,
           hb_requires (hb_is_iterator (Iterator))>
-  bool serialize (hb_serialize_context_t *c, Iterator it)
+  bool serialize (hb_serialize_context_t *c, Iterator it_with_class_zero)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+
+    auto it = + it_with_class_zero | hb_filter (hb_second);
 
     unsigned format = 2;
     if (likely (it))
     {
       hb_codepoint_t glyph_min = (*it).first;
-      hb_codepoint_t glyph_max = + it
-                                | hb_map (hb_first)
-                                | hb_reduce (hb_max, 0u);
+      hb_codepoint_t glyph_max = glyph_min;
 
+      unsigned num_glyphs = 0;
       unsigned num_ranges = 1;
       hb_codepoint_t prev_gid = glyph_min;
       unsigned prev_klass = (*it).second;
@@ -2034,7 +2412,9 @@ struct ClassDef
       {
        hb_codepoint_t cur_gid = gid_klass_pair.first;
        unsigned cur_klass = gid_klass_pair.second;
-       if (cur_gid == glyph_min || !cur_klass) continue;
+        num_glyphs++;
+       if (cur_gid == glyph_min) continue;
+        if (cur_gid > glyph_max) glyph_max = cur_gid;
        if (cur_gid != prev_gid + 1 ||
            cur_klass != prev_klass)
          num_ranges++;
@@ -2043,7 +2423,7 @@ struct ClassDef
        prev_klass = cur_klass;
       }
 
-      if (1 + (glyph_max - glyph_min + 1) <= num_ranges * 3)
+      if (num_glyphs && 1 + (glyph_max - glyph_min + 1) <= num_ranges * 3)
        format = 1;
     }
     u.format = format;
@@ -2057,12 +2437,15 @@ struct ClassDef
   }
 
   bool subset (hb_subset_context_t *c,
-              hb_map_t *klass_map = nullptr /*OUT*/) const
+              hb_map_t *klass_map = nullptr /*OUT*/,
+               bool keep_empty_table = true,
+               bool use_class_zero = true,
+               const Coverage* glyph_filter = nullptr) const
   {
     TRACE_SUBSET (this);
     switch (u.format) {
-    case 1: return_trace (u.format1.subset (c, klass_map));
-    case 2: return_trace (u.format2.subset (c, klass_map));
+    case 1: return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter));
+    case 2: return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter));
     default:return_trace (false);
     }
   }
@@ -2119,6 +2502,25 @@ struct ClassDef
     }
   }
 
+  void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs);
+    case 2: return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs);
+    default:return;
+    }
+  }
+
+  void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const
+  {
+    switch (u.format) {
+    case 1: return u.format1.intersected_classes (glyphs, intersect_classes);
+    case 2: return u.format2.intersected_classes (glyphs, intersect_classes);
+    default:return;
+    }
+  }
+
+
   protected:
   union {
   HBUINT16             format;         /* Format identifier */
@@ -2183,7 +2585,7 @@ struct VarRegionAxis
 struct VarRegionList
 {
   float evaluate (unsigned int region_index,
-                        const int *coords, unsigned int coord_len) const
+                 const int *coords, unsigned int coord_len) const
   {
     if (unlikely (region_index >= regionCount))
       return 0.;
@@ -2206,19 +2608,19 @@ struct VarRegionList
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
-    return_trace (c->check_struct (this) &&
-                 axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount));
+    return_trace (c->check_struct (this) && axesZ.sanitize (c, axisCount * regionCount));
   }
 
   bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t &region_map)
   {
     TRACE_SERIALIZE (this);
-    VarRegionList *out = c->allocate_min<VarRegionList> ();
-    if (unlikely (!out)) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
     axisCount = src->axisCount;
     regionCount = region_map.get_population ();
-    if (unlikely (!c->allocate_size<VarRegionList> (get_size () - min_size))) return_trace (false);
-    unsigned int region_count = src->get_region_count ();
+    if (unlikely (hb_unsigned_mul_overflows (axisCount * regionCount,
+                                            VarRegionAxis::static_size))) return_trace (false);
+    if (unlikely (!c->extend (this))) return_trace (false);
+    unsigned int region_count = src->regionCount;
     for (unsigned int r = 0; r < regionCount; r++)
     {
       unsigned int backward = region_map.backward (r);
@@ -2230,11 +2632,11 @@ struct VarRegionList
   }
 
   unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; }
-  unsigned int get_region_count () const { return regionCount; }
 
-  protected:
+  public:
   HBUINT16     axisCount;
-  HBUINT16     regionCount;
+  HBUINT15     regionCount;
+  protected:
   UnsizedArrayOf<VarRegionAxis>
                axesZ;
   public:
@@ -2250,11 +2652,14 @@ struct VarData
   { return shortCount + regionIndices.len; }
 
   unsigned int get_size () const
-  { return itemCount * get_row_size (); }
+  { return min_size
+        - regionIndices.min_size + regionIndices.get_size ()
+        + itemCount * get_row_size ();
+  }
 
   float get_delta (unsigned int inner,
-                         const int *coords, unsigned int coord_count,
-                         const VarRegionList &regions) const
+                  const int *coords, unsigned int coord_count,
+                  const VarRegionList &regions) const
   {
     if (unlikely (inner >= itemCount))
       return 0.;
@@ -2284,10 +2689,10 @@ struct VarData
    return delta;
   }
 
-  void get_scalars (const int *coords, unsigned int coord_count,
-                   const VarRegionList &regions,
-                   float *scalars /*OUT */,
-                   unsigned int num_scalars) const
+  void get_region_scalars (const int *coords, unsigned int coord_count,
+                          const VarRegionList &regions,
+                          float *scalars /*OUT */,
+                          unsigned int num_scalars) const
   {
     unsigned count = hb_min (num_scalars, regionIndices.len);
     for (unsigned int i = 0; i < count; i++)
@@ -2313,7 +2718,7 @@ struct VarData
                  const hb_bimap_t &region_map)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
     itemCount = inner_map.get_next_value ();
 
     /* Optimize short count */
@@ -2355,9 +2760,7 @@ struct VarData
     shortCount = new_short_count;
     regionIndices.len = new_ri_count;
 
-    unsigned int size = regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (get_row_size () * itemCount);
-    if (unlikely (!c->allocate_size<HBUINT8> (size)))
-      return_trace (false);
+    if (unlikely (!c->extend (this))) return_trace (false);
 
     for (r = 0; r < ri_count; r++)
       if (delta_sz[r]) regionIndices[ri_map[r]] = region_map[src->regionIndices[r]];
@@ -2372,16 +2775,16 @@ struct VarData
     return_trace (true);
   }
 
-  void collect_region_refs (hb_inc_bimap_t &region_map, const hb_inc_bimap_t &inner_map) const
+  void collect_region_refs (hb_set_t &region_indices, const hb_inc_bimap_t &inner_map) const
   {
     for (unsigned int r = 0; r < regionIndices.len; r++)
     {
       unsigned int region = regionIndices[r];
-      if (region_map.has (region)) continue;
+      if (region_indices.has (region)) continue;
       for (unsigned int i = 0; i < inner_map.get_next_value (); i++)
        if (get_item_delta (inner_map.backward (i), r) != 0)
        {
-         region_map.add (region);
+         region_indices.add (region);
          break;
        }
     }
@@ -2416,7 +2819,7 @@ struct VarData
   protected:
   HBUINT16             itemCount;
   HBUINT16             shortCount;
-  ArrayOf<HBUINT16>    regionIndices;
+  Array16Of<HBUINT16>  regionIndices;
 /*UnsizedArrayOf<HBUINT8>bytesX;*/
   public:
   DEFINE_SIZE_ARRAY (6, regionIndices);
@@ -2424,6 +2827,7 @@ struct VarData
 
 struct VariationStore
 {
+  private:
   float get_delta (unsigned int outer, unsigned int inner,
                   const int *coords, unsigned int coord_count) const
   {
@@ -2439,6 +2843,7 @@ struct VariationStore
                                             this+regions);
   }
 
+  public:
   float get_delta (unsigned int index,
                   const int *coords, unsigned int coord_count) const
   {
@@ -2465,32 +2870,48 @@ struct VariationStore
                  const hb_array_t <hb_inc_bimap_t> &inner_maps)
   {
     TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (this))) return_trace (false);
+
     unsigned int set_count = 0;
     for (unsigned int i = 0; i < inner_maps.length; i++)
-      if (inner_maps[i].get_population () > 0) set_count++;
+      if (inner_maps[i].get_population ())
+       set_count++;
 
-    unsigned int size = min_size + HBUINT32::static_size * set_count;
-    if (unlikely (!c->allocate_size<HBUINT32> (size))) return_trace (false);
     format = 1;
 
-    hb_inc_bimap_t region_map;
+    const auto &src_regions = src+src->regions;
+
+    hb_set_t region_indices;
     for (unsigned int i = 0; i < inner_maps.length; i++)
-      (src+src->dataSets[i]).collect_region_refs (region_map, inner_maps[i]);
-    region_map.sort ();
+      (src+src->dataSets[i]).collect_region_refs (region_indices, inner_maps[i]);
 
-    if (unlikely (!regions.serialize (c, this)
-                 .serialize (c, &(src+src->regions), region_map))) return_trace (false);
+    if (region_indices.in_error ())
+      return_trace (false);
+
+    region_indices.del_range ((src_regions).regionCount, hb_set_t::INVALID);
+
+    /* TODO use constructor when our data-structures support that. */
+    hb_inc_bimap_t region_map;
+    + hb_iter (region_indices)
+    | hb_apply ([&region_map] (unsigned _) { region_map.add(_); })
+    ;
+    if (region_map.in_error())
+      return_trace (false);
+
+    if (unlikely (!regions.serialize_serialize (c, &src_regions, region_map)))
+      return_trace (false);
 
-    /* TODO: The following code could be simplified when
-     * OffsetListOf::subset () can take a custom param to be passed to VarData::serialize ()
-     */
     dataSets.len = set_count;
+    if (unlikely (!c->extend (dataSets))) return_trace (false);
+
+    /* TODO: The following code could be simplified when
+     * List16OfOffset16To::subset () can take a custom param to be passed to VarData::serialize () */
     unsigned int set_index = 0;
     for (unsigned int i = 0; i < inner_maps.length; i++)
     {
-      if (inner_maps[i].get_population () == 0) continue;
-      if (unlikely (!dataSets[set_index++].serialize (c, this)
-                     .serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map)))
+      if (!inner_maps[i].get_population ()) continue;
+      if (unlikely (!dataSets[set_index++]
+                    .serialize_serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map)))
        return_trace (false);
     }
 
@@ -2509,8 +2930,6 @@ struct VariationStore
 
     hb_vector_t<hb_inc_bimap_t> inner_maps;
     inner_maps.resize ((unsigned) dataSets.len);
-    for (unsigned i = 0; i < inner_maps.length; i++)
-      inner_maps[i].init ();
 
     for (unsigned idx : c->plan->layout_variation_indices->iter ())
     {
@@ -2518,27 +2937,23 @@ struct VariationStore
       uint16_t minor = idx & 0xFFFF;
 
       if (major >= inner_maps.length)
-      {
-        for (unsigned i = 0; i < inner_maps.length; i++)
-          inner_maps[i].fini ();
-        return_trace (false);
-      }
+       return_trace (false);
       inner_maps[major].add (minor);
     }
     varstore_prime->serialize (c->serializer, this, inner_maps.as_array ());
 
-    for (unsigned i = 0; i < inner_maps.length; i++)
-      inner_maps[i].fini ();
-    return_trace (bool (varstore_prime->dataSets));
+    return_trace (
+        !c->serializer->in_error()
+        && varstore_prime->dataSets);
   }
 
-  unsigned int get_region_index_count (unsigned int ivs) const
-  { return (this+dataSets[ivs]).get_region_index_count (); }
+  unsigned int get_region_index_count (unsigned int major) const
+  { return (this+dataSets[major]).get_region_index_count (); }
 
-  void get_scalars (unsigned int ivs,
-                   const int *coords, unsigned int coord_count,
-                   float *scalars /*OUT*/,
-                   unsigned int num_scalars) const
+  void get_region_scalars (unsigned int major,
+                          const int *coords, unsigned int coord_count,
+                          float *scalars /*OUT*/,
+                          unsigned int num_scalars) const
   {
 #ifdef HB_NO_VAR
     for (unsigned i = 0; i < num_scalars; i++)
@@ -2546,18 +2961,19 @@ struct VariationStore
     return;
 #endif
 
-    (this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions,
-                                     &scalars[0], num_scalars);
+    (this+dataSets[major]).get_region_scalars (coords, coord_count,
+                                              this+regions,
+                                              &scalars[0], num_scalars);
   }
 
   unsigned int get_sub_table_count () const { return dataSets.len; }
 
   protected:
   HBUINT16                             format;
-  LOffsetTo<VarRegionList>             regions;
-  LOffsetArrayOf<VarData>              dataSets;
+  Offset32To<VarRegionList>            regions;
+  Array16OfOffset32To<VarData>         dataSets;
   public:
-  DEFINE_SIZE_ARRAY (8, dataSets);
+  DEFINE_SIZE_ARRAY_SIZED (8, dataSets);
 };
 
 /*
@@ -2614,7 +3030,7 @@ struct Condition
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
     switch (u.format) {
-    case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+    case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
     default:return_trace (c->default_return_value ());
     }
   }
@@ -2658,7 +3074,8 @@ struct ConditionSet
     + conditions.iter ()
     | hb_apply (subset_offset_array (c, out->conditions, this))
     ;
-    return_trace (true);
+
+    return_trace (bool (out->conditions));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2668,7 +3085,7 @@ struct ConditionSet
   }
 
   protected:
-  LOffsetArrayOf<Condition>    conditions;
+  Array16OfOffset32To<Condition>       conditions;
   public:
   DEFINE_SIZE_ARRAY (2, conditions);
 };
@@ -2693,6 +3110,12 @@ struct FeatureTableSubstitutionRecord
   bool subset (hb_subset_layout_context_t *c, const void *base) const
   {
     TRACE_SUBSET (this);
+    if (!c->feature_index_map->has (featureIndex)) {
+      // Feature that is being substituted is not being retained, so we don't
+      // need this.
+      return_trace (false);
+    }
+
     auto *out = c->subset_context->serializer->embed (this);
     if (unlikely (!out)) return_trace (false);
 
@@ -2709,7 +3132,7 @@ struct FeatureTableSubstitutionRecord
 
   protected:
   HBUINT16             featureIndex;
-  LOffsetTo<Feature>   feature;
+  Offset32To<Feature>  feature;
   public:
   DEFINE_SIZE_STATIC (6);
 };
@@ -2745,6 +3168,15 @@ struct FeatureTableSubstitution
       record.closure_features (this, lookup_indexes, feature_indexes);
   }
 
+  bool intersects_features (const hb_map_t *feature_index_map) const
+  {
+    for (const FeatureTableSubstitutionRecord& record : substitutions)
+    {
+      if (feature_index_map->has (record.featureIndex)) return true;
+    }
+    return false;
+  }
+
   bool subset (hb_subset_context_t        *c,
               hb_subset_layout_context_t *l) const
   {
@@ -2758,7 +3190,8 @@ struct FeatureTableSubstitution
     + substitutions.iter ()
     | hb_apply (subset_record_array (l, &(out->substitutions), this))
     ;
-    return_trace (true);
+
+    return_trace (bool (out->substitutions));
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2771,7 +3204,7 @@ struct FeatureTableSubstitution
 
   protected:
   FixedVersion<>       version;        /* Version--0x00010000u */
-  ArrayOf<FeatureTableSubstitutionRecord>
+  Array16Of<FeatureTableSubstitutionRecord>
                        substitutions;
   public:
   DEFINE_SIZE_ARRAY (6, substitutions);
@@ -2795,6 +3228,11 @@ struct FeatureVariationRecord
     (base+substitutions).closure_features (lookup_indexes, feature_indexes);
   }
 
+  bool intersects_features (const void *base, const hb_map_t *feature_index_map) const
+  {
+    return (base+substitutions).intersects_features (feature_index_map);
+  }
+
   bool subset (hb_subset_layout_context_t *c, const void *base) const
   {
     TRACE_SUBSET (this);
@@ -2815,9 +3253,9 @@ struct FeatureVariationRecord
   }
 
   protected:
-  LOffsetTo<ConditionSet>
+  Offset32To<ConditionSet>
                        conditions;
-  LOffsetTo<FeatureTableSubstitution>
+  Offset32To<FeatureTableSubstitution>
                        substitutions;
   public:
   DEFINE_SIZE_STATIC (8);
@@ -2828,7 +3266,7 @@ struct FeatureVariations
   static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFFFFFu;
 
   bool find_index (const int *coords, unsigned int coord_len,
-                         unsigned int *index) const
+                  unsigned int *index) const
   {
     unsigned int count = varRecords.len;
     for (unsigned int i = 0; i < count; i++)
@@ -2881,9 +3319,18 @@ struct FeatureVariations
     out->version.major = version.major;
     out->version.minor = version.minor;
 
-    + varRecords.iter ()
-    | hb_apply (subset_record_array (l, &(out->varRecords), this))
-    ;
+    int keep_up_to = -1;
+    for (int i = varRecords.len - 1; i >= 0; i--) {
+      if (varRecords[i].intersects_features (this, l->feature_index_map)) {
+        keep_up_to = i;
+        break;
+      }
+    }
+
+    unsigned count = (unsigned) (keep_up_to + 1);
+    for (unsigned i = 0; i < count; i++) {
+      subset_record_array (l, &(out->varRecords), this) (varRecords[i]);
+    }
     return_trace (bool (out->varRecords));
   }
 
@@ -2897,7 +3344,7 @@ struct FeatureVariations
 
   protected:
   FixedVersion<>       version;        /* Version--0x00010000u */
-  LArrayOf<FeatureVariationRecord>
+  Array32Of<FeatureVariationRecord>
                        varRecords;
   public:
   DEFINE_SIZE_ARRAY_SIZED (8, varRecords);
@@ -3010,22 +3457,20 @@ struct VariationDevice
     if (unlikely (!out)) return_trace (nullptr);
     if (!layout_variation_idx_map || layout_variation_idx_map->is_empty ()) return_trace (out);
 
-    unsigned org_idx = (outerIndex << 16) + innerIndex;
-    if (!layout_variation_idx_map->has (org_idx))
+    /* TODO Just get() and bail if NO_VARIATION. Needs to setup the map to return that. */
+    if (!layout_variation_idx_map->has (varIdx))
     {
       c->revert (snap);
       return_trace (nullptr);
     }
-    unsigned new_idx = layout_variation_idx_map->get (org_idx);
-    out->outerIndex = new_idx >> 16;
-    out->innerIndex = new_idx & 0xFFFF;
+    unsigned new_idx = layout_variation_idx_map->get (varIdx);
+    out->varIdx = new_idx;
     return_trace (out);
   }
 
   void record_variation_index (hb_set_t *layout_variation_indices) const
   {
-    unsigned var_idx = (outerIndex << 16) + innerIndex;
-    layout_variation_indices->add (var_idx);
+    layout_variation_indices->add (varIdx);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -3038,12 +3483,11 @@ struct VariationDevice
 
   float get_delta (hb_font_t *font, const VariationStore &store) const
   {
-    return store.get_delta (outerIndex, innerIndex, font->coords, font->num_coords);
+    return store.get_delta (varIdx, font->coords, font->num_coords);
   }
 
   protected:
-  HBUINT16     outerIndex;
-  HBUINT16     innerIndex;
+  VarIdx       varIdx;
   HBUINT16     deltaFormat;    /* Format identifier for this table: 0x0x8000 */
   public:
   DEFINE_SIZE_STATIC (6);