Imported Upstream version 2.3.1
[platform/upstream/harfbuzz.git] / src / hb-ot-layout-gsub-table.hh
index bd72fe6..33b8f0e 100644 (file)
 #ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
 #define HB_OT_LAYOUT_GSUB_TABLE_HH
 
-#include "hb-ot-layout-gsubgpos-private.hh"
+#include "hb-ot-layout-gsubgpos.hh"
 
 
 namespace OT {
 
 
+static inline void SingleSubst_serialize (hb_serialize_context_t *c,
+                                         hb_array_t<const GlyphID> glyphs,
+                                         hb_array_t<const GlyphID> substitutes);
+
 struct SingleSubstFormat1
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
-    Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
        * https://github.com/harfbuzz/harfbuzz/issues/363 */
       hb_codepoint_t glyph_id = iter.get_glyph ();
       if (c->glyphs->has (glyph_id))
-       c->glyphs->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
+       c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       /* TODO Switch to range-based API to work around malicious fonts.
        * https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -65,18 +68,15 @@ struct SingleSubstFormat1
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
@@ -91,19 +91,37 @@ struct SingleSubstFormat1
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        unsigned int num_glyphs,
-                        int delta)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 int delta)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
-    deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
+    deltaGlyphID.set (delta); /* TODO(serialize) overflow? */
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    hb_vector_t<GlyphID> from;
+    hb_vector_t<GlyphID> to;
+    hb_codepoint_t delta = deltaGlyphID;
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
+    {
+      if (!glyphset.has (iter.get_glyph ())) continue;
+      from.push ()->set (glyph_map[iter.get_glyph ()]);
+      to.push ()->set (glyph_map[(iter.get_glyph () + delta) & 0xFFFF]);
+    }
+    c->serializer->propagate_error (from, to);
+    SingleSubst_serialize (c->serializer, from, to);
+    return_trace (from.length);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
@@ -122,27 +140,26 @@ struct SingleSubstFormat1
 
 struct SingleSubstFormat2
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = substitute.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
-       c->glyphs->add (substitute[iter.get_coverage ()]);
+       c->out->add (substitute[iter.get_coverage ()]);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
     unsigned int count = substitute.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -150,45 +167,57 @@ struct SingleSubstFormat2
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
-    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     if (unlikely (index >= substitute.len)) return_trace (false);
 
-    glyph_id = substitute[index];
-    c->replace_glyph (glyph_id);
+    c->replace_glyph (substitute[index]);
 
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        Supplier<GlyphID> &substitutes,
-                        unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 hb_array_t<const GlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return_trace (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
+    if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    const hb_set_t &glyphset = *c->plan->glyphset;
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+    hb_vector_t<GlyphID> from;
+    hb_vector_t<GlyphID> to;
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
+    {
+      if (!glyphset.has (iter.get_glyph ())) continue;
+      from.push ()->set (glyph_map[iter.get_glyph ()]);
+      to.push ()->set (glyph_map[substitute[iter.get_coverage ()]]);
+    }
+    c->serializer->propagate_error (from, to);
+    SingleSubst_serialize (c->serializer, from, to);
+    return_trace (from.length);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
@@ -208,35 +237,35 @@ struct SingleSubstFormat2
 
 struct SingleSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        Supplier<GlyphID> &substitutes,
-                        unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 hb_array_t<const GlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 2;
     int delta = 0;
-    if (num_glyphs) {
+    if (glyphs.length)
+    {
       format = 1;
       /* TODO(serialize) check for wrap-around */
       delta = substitutes[0] - glyphs[0];
-      for (unsigned int i = 1; i < num_glyphs; i++)
-       if (delta != substitutes[i] - glyphs[i]) {
+      for (unsigned int i = 1; i < glyphs.length; i++)
+       if (delta != (int) (substitutes[i] - glyphs[i])) {
          format = 2;
          break;
        }
     }
     u.format.set (format);
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, num_glyphs, delta));
-    case 2: return_trace (u.format2.serialize (c, glyphs, substitutes, num_glyphs));
+    case 1: return_trace (u.format1.serialize (c, glyphs, delta));
+    case 2: return_trace (u.format2.serialize (c, glyphs, substitutes));
     default:return_trace (false);
     }
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -255,24 +284,25 @@ struct SingleSubst
   } u;
 };
 
+static inline void
+SingleSubst_serialize (hb_serialize_context_t *c,
+                      hb_array_t<const GlyphID> glyphs,
+                      hb_array_t<const GlyphID> substitutes)
+{ c->start_embed<SingleSubst> ()->serialize (c, glyphs, substitutes); }
 
 struct Sequence
 {
-  inline void closure (hb_closure_context_t *c) const
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
     unsigned int count = substitute.len;
     for (unsigned int i = 0; i < count; i++)
-      c->glyphs->add (substitute[i]);
+      c->out->add (substitute[i]);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
-  {
-    TRACE_COLLECT_GLYPHS (this);
-    c->output->add_array (substitute.arrayZ, substitute.len);
-  }
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  { c->output->add_array (substitute.arrayZ, substitute.len); }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int count = substitute.len;
@@ -304,17 +334,14 @@ struct Sequence
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        unsigned int num_glyphs)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    return_trace (substitute.serialize (c, glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (substitute.sanitize (c));
@@ -329,12 +356,13 @@ struct Sequence
 
 struct MultipleSubstFormat1
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = sequence.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -343,27 +371,23 @@ struct MultipleSubstFormat1
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
     unsigned int count = sequence.len;
     for (unsigned int i = 0; i < count; i++)
-       (this+sequence[i]).collect_glyphs (c);
+      (this+sequence[i]).collect_glyphs (c);
   }
 
-  inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
 
@@ -373,25 +397,33 @@ struct MultipleSubstFormat1
     return_trace ((this+sequence[index]).apply (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        Supplier<unsigned int> &substitute_len_list,
-                        unsigned int num_glyphs,
-                        Supplier<GlyphID> &substitute_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 hb_array_t<const unsigned int> substitute_len_list,
+                 hb_array_t<const GlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!sequence.serialize (c, num_glyphs))) return_trace (false);
-    for (unsigned int i = 0; i < num_glyphs; i++)
-      if (unlikely (!sequence[i].serialize (c, this).serialize (c,
-                                                               substitute_glyphs_list,
-                                                               substitute_len_list[i]))) return_trace (false);
-    substitute_len_list += num_glyphs;
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
+    for (unsigned int i = 0; i < glyphs.length; i++)
+    {
+      unsigned int substitute_len = substitute_len_list[i];
+      if (unlikely (!sequence[i].serialize (c, this)
+                               .serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
+       return_trace (false);
+      substitute_glyphs_list += substitute_len;
+    }
+    return_trace (coverage.serialize (c, this).serialize (c, glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    // TODO(subset)
+    return_trace (false);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
@@ -411,24 +443,23 @@ struct MultipleSubstFormat1
 
 struct MultipleSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        Supplier<unsigned int> &substitute_len_list,
-                        unsigned int num_glyphs,
-                        Supplier<GlyphID> &substitute_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 hb_array_t<const unsigned int> substitute_len_list,
+                 hb_array_t<const GlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list));
+    case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
     default:return_trace (false);
     }
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -445,103 +476,138 @@ struct MultipleSubst
   } u;
 };
 
+struct AlternateSet
+{
+  void closure (hb_closure_context_t *c) const
+  {
+    unsigned int count = alternates.len;
+    for (unsigned int i = 0; i < count; i++)
+      c->out->add (alternates[i]);
+  }
+
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  { c->output->add_array (alternates.arrayZ, alternates.len); }
+
+  bool apply (hb_ot_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    unsigned int count = alternates.len;
+
+    if (unlikely (!count)) return_trace (false);
+
+    hb_mask_t glyph_mask = c->buffer->cur().mask;
+    hb_mask_t lookup_mask = c->lookup_mask;
+
+    /* Note: This breaks badly if two features enabled this lookup together. */
+    unsigned int shift = hb_ctz (lookup_mask);
+    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
+
+    /* If alt_index is MAX, randomize feature if it is the rand feature. */
+    if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
+      alt_index = c->random_number () % count + 1;
 
-typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
+    if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
+
+    c->replace_glyph (alternates[alt_index - 1]);
+
+    return_trace (true);
+  }
+
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    return_trace (alternates.serialize (c, glyphs));
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (alternates.sanitize (c));
+  }
+
+  protected:
+  ArrayOf<GlyphID>
+               alternates;             /* Array of alternate GlyphIDs--in
                                         * arbitrary order */
+  public:
+  DEFINE_SIZE_ARRAY (2, alternates);
+};
 
 struct AlternateSubstFormat1
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  { return (this+coverage).intersects (glyphs); }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = alternateSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
-      if (c->glyphs->has (iter.get_glyph ())) {
-       const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
-       unsigned int count = alt_set.len;
-       for (unsigned int i = 0; i < count; i++)
-         c->glyphs->add (alt_set[i]);
-      }
+       break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+      if (c->glyphs->has (iter.get_glyph ()))
+       (this+alternateSet[iter.get_coverage ()]).closure (c);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
     unsigned int count = alternateSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
-        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
-      const AlternateSet &alt_set = this+alternateSet[iter.get_coverage ()];
-      c->output->add_array (alt_set.arrayZ, alt_set.len);
+       break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+      (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c);
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
-    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
-    const AlternateSet &alt_set = this+alternateSet[index];
-
-    if (unlikely (!alt_set.len)) return_trace (false);
-
-    hb_mask_t glyph_mask = c->buffer->cur().mask;
-    hb_mask_t lookup_mask = c->lookup_mask;
-
-    /* Note: This breaks badly if two features enabled this lookup together. */
-    unsigned int shift = _hb_ctz (lookup_mask);
-    unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
-
-    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return_trace (false);
-
-    glyph_id = alt_set[alt_index - 1];
-
-    c->replace_glyph (glyph_id);
-
-    return_trace (true);
+    return_trace ((this+alternateSet[index]).apply (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        Supplier<unsigned int> &alternate_len_list,
-                        unsigned int num_glyphs,
-                        Supplier<GlyphID> &alternate_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 hb_array_t<const unsigned int> alternate_len_list,
+                 hb_array_t<const GlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!alternateSet.serialize (c, num_glyphs))) return_trace (false);
-    for (unsigned int i = 0; i < num_glyphs; i++)
-      if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
-                                                                   alternate_glyphs_list,
-                                                                   alternate_len_list[i]))) return_trace (false);
-    alternate_len_list += num_glyphs;
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return_trace (false);
-    return_trace (true);
+    if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
+    for (unsigned int i = 0; i < glyphs.length; i++)
+    {
+      unsigned int alternate_len = alternate_len_list[i];
+      if (unlikely (!alternateSet[i].serialize (c, this)
+                                   .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
+       return_trace (false);
+      alternate_glyphs_list += alternate_len;
+    }
+    return_trace (coverage.serialize (c, this).serialize (c, glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    // TODO(subset)
+    return_trace (false);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
@@ -561,24 +627,23 @@ struct AlternateSubstFormat1
 
 struct AlternateSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &glyphs,
-                        Supplier<unsigned int> &alternate_len_list,
-                        unsigned int num_glyphs,
-                        Supplier<GlyphID> &alternate_glyphs_list)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> glyphs,
+                 hb_array_t<const unsigned int> alternate_len_list,
+                 hb_array_t<const GlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
     unsigned int format = 1;
     u.format.set (format);
     switch (u.format) {
-    case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list));
+    case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
     default:return_trace (false);
     }
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -598,27 +663,34 @@ struct AlternateSubst
 
 struct Ligature
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
   {
-    TRACE_CLOSURE (this);
-    unsigned int count = component.len;
+    unsigned int count = component.lenP1;
+    for (unsigned int i = 1; i < count; i++)
+      if (!glyphs->has (component[i]))
+        return false;
+    return true;
+  }
+
+  void closure (hb_closure_context_t *c) const
+  {
+    unsigned int count = component.lenP1;
     for (unsigned int i = 1; i < count; i++)
       if (!c->glyphs->has (component[i]))
         return;
-    c->glyphs->add (ligGlyph);
+    c->out->add (ligGlyph);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
-    c->input->add_array (component.arrayZ, component.len ? component.len - 1 : 0);
+    c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0);
     c->output->add (ligGlyph);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
-    if (c->len != component.len)
+    if (c->len != component.lenP1)
       return_trace (false);
 
     for (unsigned int i = 1; i < c->len; i++)
@@ -628,10 +700,10 @@ struct Ligature
     return_trace (true);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    unsigned int count = component.len;
+    unsigned int count = component.lenP1;
 
     if (unlikely (!count)) return_trace (false);
 
@@ -643,7 +715,6 @@ struct Ligature
       return_trace (true);
     }
 
-    bool is_mark_ligature = false;
     unsigned int total_component_count = 0;
 
     unsigned int match_length = 0;
@@ -655,7 +726,6 @@ struct Ligature
                              nullptr,
                              &match_length,
                              match_positions,
-                             &is_mark_ligature,
                              &total_component_count)))
       return_trace (false);
 
@@ -664,26 +734,24 @@ struct Ligature
                  match_positions,
                  match_length,
                  ligGlyph,
-                 is_mark_ligature,
                  total_component_count);
 
     return_trace (true);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        GlyphID ligature,
-                        Supplier<GlyphID> &components, /* Starting from second */
-                        unsigned int num_components /* Including first component */)
+  bool serialize (hb_serialize_context_t *c,
+                 GlyphID ligature,
+                 hb_array_t<const GlyphID> components /* Starting from second */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
     ligGlyph = ligature;
-    if (unlikely (!component.serialize (c, components, num_components))) return_trace (false);
+    if (unlikely (!component.serialize (c, components))) return_trace (false);
     return_trace (true);
   }
 
   public:
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
@@ -701,23 +769,30 @@ struct Ligature
 
 struct LigatureSet
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    unsigned int num_ligs = ligature.len;
+    for (unsigned int i = 0; i < num_ligs; i++)
+      if ((this+ligature[i]).intersects (glyphs))
+        return true;
+    return false;
+  }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
       (this+ligature[i]).closure (c);
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     unsigned int num_ligs = ligature.len;
     for (unsigned int i = 0; i < num_ligs; i++)
       (this+ligature[i]).collect_glyphs (c);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int num_ligs = ligature.len;
@@ -730,7 +805,7 @@ struct LigatureSet
     return_trace (false);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     unsigned int num_ligs = ligature.len;
@@ -743,26 +818,28 @@ struct LigatureSet
     return_trace (false);
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &ligatures,
-                        Supplier<unsigned int> &component_count_list,
-                        unsigned int num_ligatures,
-                        Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> ligatures,
+                 hb_array_t<const unsigned int> component_count_list,
+                 hb_array_t<const GlyphID> &component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!ligature.serialize (c, num_ligatures))) return_trace (false);
-    for (unsigned int i = 0; i < num_ligatures; i++)
-      if (unlikely (!ligature[i].serialize (c, this).serialize (c,
-                                                               ligatures[i],
-                                                               component_list,
-                                                               component_count_list[i]))) return_trace (false);
-    ligatures += num_ligatures;
-    component_count_list += num_ligatures;
+    if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
+    for (unsigned int i = 0; i < ligatures.length; i++)
+    {
+      unsigned int component_count = MAX<int> (component_count_list[i] - 1, 0);
+      if (unlikely (!ligature[i].serialize (c, this)
+                               .serialize (c,
+                                           ligatures[i],
+                                           component_list.sub_array (0, component_count))))
+       return_trace (false);
+      component_list += component_count;
+    }
     return_trace (true);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (ligature.sanitize (c, this));
@@ -778,12 +855,24 @@ struct LigatureSet
 
 struct LigatureSubstFormat1
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    unsigned int count = ligatureSet.len;
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
+    {
+      if (unlikely (iter.get_coverage () >= count))
+        break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
+      if (glyphs->has (iter.get_glyph ()) &&
+         (this+ligatureSet[iter.get_coverage ()]).intersects (glyphs))
+        return true;
+    }
+    return false;
+  }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
-    Coverage::Iter iter;
     unsigned int count = ligatureSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -792,13 +881,11 @@ struct LigatureSubstFormat1
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
-    Coverage::Iter iter;
     unsigned int count = ligatureSet.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
@@ -806,12 +893,9 @@ struct LigatureSubstFormat1
     }
   }
 
-  inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
@@ -821,41 +905,49 @@ struct LigatureSubstFormat1
     return_trace (lig_set.would_apply (c));
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
 
-    unsigned int index = (this+coverage).get_coverage (glyph_id);
+    unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return_trace (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
     return_trace (lig_set.apply (c));
   }
 
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &first_glyphs,
-                        Supplier<unsigned int> &ligature_per_first_glyph_count_list,
-                        unsigned int num_first_glyphs,
-                        Supplier<GlyphID> &ligatures_list,
-                        Supplier<unsigned int> &component_count_list,
-                        Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> first_glyphs,
+                 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+                 hb_array_t<const GlyphID> ligatures_list,
+                 hb_array_t<const unsigned int> component_count_list,
+                 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (*this))) return_trace (false);
-    if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return_trace (false);
-    for (unsigned int i = 0; i < num_first_glyphs; i++)
-      if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
-                                                                  ligatures_list,
-                                                                  component_count_list,
-                                                                  ligature_per_first_glyph_count_list[i],
-                                                                  component_list))) return_trace (false);
-    ligature_per_first_glyph_count_list += num_first_glyphs;
-    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return_trace (false);
-    return_trace (true);
+    if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
+    for (unsigned int i = 0; i < first_glyphs.length; i++)
+    {
+      unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
+      if (unlikely (!ligatureSet[i].serialize (c, this)
+                                  .serialize (c,
+                                              ligatures_list.sub_array (0, ligature_count),
+                                              component_count_list.sub_array (0, ligature_count),
+                                              component_list))) return_trace (false);
+      ligatures_list += ligature_count;
+      component_count_list += ligature_count;
+    }
+    return_trace (coverage.serialize (c, this).serialize (c, first_glyphs));
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    // TODO(subset)
+    return_trace (false);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
@@ -875,13 +967,12 @@ struct LigatureSubstFormat1
 
 struct LigatureSubst
 {
-  inline bool serialize (hb_serialize_context_t *c,
-                        Supplier<GlyphID> &first_glyphs,
-                        Supplier<unsigned int> &ligature_per_first_glyph_count_list,
-                        unsigned int num_first_glyphs,
-                        Supplier<GlyphID> &ligatures_list,
-                        Supplier<unsigned int> &component_count_list,
-                        Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize (hb_serialize_context_t *c,
+                 hb_array_t<const GlyphID> first_glyphs,
+                 hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+                 hb_array_t<const GlyphID> ligatures_list,
+                 hb_array_t<const unsigned int> component_count_list,
+                 hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
     if (unlikely (!c->extend_min (u.format))) return_trace (false);
@@ -891,7 +982,6 @@ struct LigatureSubst
     case 1: return_trace (u.format1.serialize (c,
                                               first_glyphs,
                                               ligature_per_first_glyph_count_list,
-                                              num_first_glyphs,
                                               ligatures_list,
                                               component_count_list,
                                               component_list));
@@ -900,7 +990,7 @@ struct LigatureSubst
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -924,17 +1014,38 @@ struct ChainContextSubst : ChainContext {};
 
 struct ExtensionSubst : Extension<ExtensionSubst>
 {
-  typedef struct SubstLookupSubTable LookupSubTable;
+  typedef struct SubstLookupSubTable SubTable;
 
-  inline bool is_reverse (void) const;
+  bool is_reverse () const;
 };
 
 
 struct ReverseChainSingleSubstFormat1
 {
-  inline void closure (hb_closure_context_t *c) const
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    if (!(this+coverage).intersects (glyphs))
+      return false;
+
+    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
+
+    unsigned int count;
+
+    count = backtrack.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+backtrack[i]).intersects (glyphs))
+        return false;
+
+    count = lookahead.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+lookahead[i]).intersects (glyphs))
+        return false;
+
+    return true;
+  }
+
+  void closure (hb_closure_context_t *c) const
   {
-    TRACE_CLOSURE (this);
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
     unsigned int count;
@@ -950,20 +1061,18 @@ struct ReverseChainSingleSubstFormat1
         return;
 
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
-    Coverage::Iter iter;
     count = substitute.len;
-    for (iter.init (this+coverage); iter.more (); iter.next ())
+    for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ())
     {
       if (unlikely (iter.get_coverage () >= count))
         break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */
       if (c->glyphs->has (iter.get_glyph ()))
-       c->glyphs->add (substitute[iter.get_coverage ()]);
+       c->out->add (substitute[iter.get_coverage ()]);
     }
   }
 
-  inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
+  void collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     if (unlikely (!(this+coverage).add_coverage (c->input))) return;
 
     unsigned int count;
@@ -982,18 +1091,15 @@ struct ReverseChainSingleSubstFormat1
     c->output->add_array (substitute.arrayZ, substitute.len);
   }
 
-  inline const Coverage &get_coverage (void) const
-  {
-    return this+coverage;
-  }
+  const Coverage &get_coverage () const { return this+coverage; }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  bool would_apply (hb_would_apply_context_t *c) const
   {
     TRACE_WOULD_APPLY (this);
     return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
@@ -1026,7 +1132,14 @@ struct ReverseChainSingleSubstFormat1
     return_trace (false);
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
+  bool subset (hb_subset_context_t *c) const
+  {
+    TRACE_SUBSET (this);
+    // TODO(subset)
+    return_trace (false);
+  }
+
+  bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
@@ -1045,7 +1158,7 @@ struct ReverseChainSingleSubstFormat1
                                         * beginning of table */
   OffsetArrayOf<Coverage>
                backtrack;              /* Array of coverage tables
-                                        * in backtracking sequence, in  glyph
+                                        * in backtracking sequence, in glyph
                                         * sequence order */
   OffsetArrayOf<Coverage>
                lookaheadX;             /* Array of coverage tables
@@ -1061,7 +1174,7 @@ struct ReverseChainSingleSubstFormat1
 struct ReverseChainSingleSubst
 {
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
+  typename context_t::return_t dispatch (context_t *c) const
   {
     TRACE_DISPATCH (this, u.format);
     if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
@@ -1086,6 +1199,7 @@ struct ReverseChainSingleSubst
 
 struct SubstLookupSubTable
 {
+  friend struct Lookup;
   friend struct SubstLookup;
 
   enum Type {
@@ -1100,10 +1214,9 @@ struct SubstLookupSubTable
   };
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
+  typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
     TRACE_DISPATCH (this, lookup_type);
-    if (unlikely (!c->may_dispatch (this, &u.sub_format))) return_trace (c->no_dispatch_return_value ());
     switch (lookup_type) {
     case Single:               return_trace (u.single.dispatch (c));
     case Multiple:             return_trace (u.multiple.dispatch (c));
@@ -1119,7 +1232,6 @@ struct SubstLookupSubTable
 
   protected:
   union {
-  HBUINT16                     sub_format;
   SingleSubst                  single;
   MultipleSubst                        multiple;
   AlternateSubst               alternate;
@@ -1130,58 +1242,69 @@ struct SubstLookupSubTable
   ReverseChainSingleSubst      reverseChainContextSingle;
   } u;
   public:
-  DEFINE_SIZE_UNION (2, sub_format);
+  DEFINE_SIZE_MIN (0);
 };
 
 
 struct SubstLookup : Lookup
 {
-  inline const SubstLookupSubTable& get_subtable (unsigned int i) const
-  { return Lookup::get_subtable<SubstLookupSubTable> (i); }
+  typedef SubstLookupSubTable SubTable;
+
+  const SubTable& get_subtable (unsigned int i) const
+  { return Lookup::get_subtable<SubTable> (i); }
 
-  inline static bool lookup_type_is_reverse (unsigned int lookup_type)
-  { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
+  static bool lookup_type_is_reverse (unsigned int lookup_type)
+  { return lookup_type == SubTable::ReverseChainSingle; }
 
-  inline bool is_reverse (void) const
+  bool is_reverse () const
   {
     unsigned int type = get_type ();
-    if (unlikely (type == SubstLookupSubTable::Extension))
+    if (unlikely (type == SubTable::Extension))
       return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
     return lookup_type_is_reverse (type);
   }
 
-  inline bool apply (hb_ot_apply_context_t *c) const
+  bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
     return_trace (dispatch (c));
   }
 
-  inline hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
+  bool intersects (const hb_set_t *glyphs) const
+  {
+    hb_intersects_context_t c (glyphs);
+    return dispatch (&c);
+  }
+
+  hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
   {
-    TRACE_CLOSURE (this);
     if (!c->should_visit_lookup (this_index))
-      return_trace (HB_VOID);
+      return hb_closure_context_t::default_return_value ();
 
     c->set_recurse_func (dispatch_closure_recurse_func);
-    return_trace (dispatch (c));
+
+    hb_closure_context_t::return_t ret = dispatch (c);
+
+    c->flush ();
+
+    return ret;
   }
 
-  inline hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
+  hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
   {
-    TRACE_COLLECT_GLYPHS (this);
     c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
-    return_trace (dispatch (c));
+    return dispatch (c);
   }
 
   template <typename set_t>
-  inline void add_coverage (set_t *glyphs) const
+  void add_coverage (set_t *glyphs) const
   {
     hb_add_coverage_context_t<set_t> c (glyphs);
     dispatch (&c);
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c,
-                          const hb_ot_layout_lookup_accelerator_t *accel) const
+  bool would_apply (hb_would_apply_context_t *c,
+                   const hb_ot_layout_lookup_accelerator_t *accel) const
   {
     TRACE_WOULD_APPLY (this);
     if (unlikely (!c->len))  return_trace (false);
@@ -1191,111 +1314,95 @@ struct SubstLookup : Lookup
 
   static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
 
-  inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c,
-                                                 unsigned int i)
-  { return get_subtables<SubstLookupSubTable> ()[i].serialize (c, this); }
+  SubTable& serialize_subtable (hb_serialize_context_t *c,
+                                      unsigned int i)
+  { return get_subtables<SubTable> ()[i].serialize (c, this); }
 
-  inline bool serialize_single (hb_serialize_context_t *c,
-                               uint32_t lookup_props,
-                               Supplier<GlyphID> &glyphs,
-                               Supplier<GlyphID> &substitutes,
-                               unsigned int num_glyphs)
+  bool serialize_single (hb_serialize_context_t *c,
+                        uint32_t lookup_props,
+                        hb_array_t<const GlyphID> glyphs,
+                        hb_array_t<const GlyphID> substitutes)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return_trace (false);
-    return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs));
+    if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
+    return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes));
   }
 
-  inline bool serialize_multiple (hb_serialize_context_t *c,
-                                 uint32_t lookup_props,
-                                 Supplier<GlyphID> &glyphs,
-                                 Supplier<unsigned int> &substitute_len_list,
-                                 unsigned int num_glyphs,
-                                 Supplier<GlyphID> &substitute_glyphs_list)
+  bool serialize_multiple (hb_serialize_context_t *c,
+                          uint32_t lookup_props,
+                          hb_array_t<const GlyphID> glyphs,
+                          hb_array_t<const unsigned int> substitute_len_list,
+                          hb_array_t<const GlyphID> substitute_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return_trace (false);
+    if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
     return_trace (serialize_subtable (c, 0).u.multiple.serialize (c,
                                                                  glyphs,
                                                                  substitute_len_list,
-                                                                 num_glyphs,
                                                                  substitute_glyphs_list));
   }
 
-  inline bool serialize_alternate (hb_serialize_context_t *c,
-                                  uint32_t lookup_props,
-                                  Supplier<GlyphID> &glyphs,
-                                  Supplier<unsigned int> &alternate_len_list,
-                                  unsigned int num_glyphs,
-                                  Supplier<GlyphID> &alternate_glyphs_list)
+  bool serialize_alternate (hb_serialize_context_t *c,
+                           uint32_t lookup_props,
+                           hb_array_t<const GlyphID> glyphs,
+                           hb_array_t<const unsigned int> alternate_len_list,
+                           hb_array_t<const GlyphID> alternate_glyphs_list)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return_trace (false);
+    if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
     return_trace (serialize_subtable (c, 0).u.alternate.serialize (c,
                                                                   glyphs,
                                                                   alternate_len_list,
-                                                                  num_glyphs,
                                                                   alternate_glyphs_list));
   }
 
-  inline bool serialize_ligature (hb_serialize_context_t *c,
-                                 uint32_t lookup_props,
-                                 Supplier<GlyphID> &first_glyphs,
-                                 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
-                                 unsigned int num_first_glyphs,
-                                 Supplier<GlyphID> &ligatures_list,
-                                 Supplier<unsigned int> &component_count_list,
-                                 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  bool serialize_ligature (hb_serialize_context_t *c,
+                          uint32_t lookup_props,
+                          hb_array_t<const GlyphID> first_glyphs,
+                          hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+                          hb_array_t<const GlyphID> ligatures_list,
+                          hb_array_t<const unsigned int> component_count_list,
+                          hb_array_t<const GlyphID> component_list /* Starting from second for each ligature */)
   {
     TRACE_SERIALIZE (this);
-    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return_trace (false);
+    if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
     return_trace (serialize_subtable (c, 0).u.ligature.serialize (c,
                                                                  first_glyphs,
                                                                  ligature_per_first_glyph_count_list,
-                                                                 num_first_glyphs,
                                                                  ligatures_list,
                                                                  component_count_list,
                                                                  component_list));
   }
 
   template <typename context_t>
-  static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+  static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
 
-  static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
+  static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
   {
     if (!c->should_visit_lookup (lookup_index))
       return HB_VOID;
-    return dispatch_recurse_func (c, lookup_index);
+
+    hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
+
+    /* While in theory we should flush here, it will cause timeouts because a recursive
+     * lookup can keep growing the glyph set.  Skip, and outer loop will retry up to
+     * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
+    //c->flush ();
+
+    return ret;
   }
 
   template <typename context_t>
-  inline typename context_t::return_t dispatch (context_t *c) const
-  { return Lookup::dispatch<SubstLookupSubTable> (c); }
+  typename context_t::return_t dispatch (context_t *c) const
+  { return Lookup::dispatch<SubTable> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!Lookup::sanitize (c))) return_trace (false);
-    if (unlikely (!dispatch (c))) return_trace (false);
+  bool subset (hb_subset_context_t *c) const
+  { return Lookup::subset<SubTable> (c); }
 
-    if (unlikely (get_type () == SubstLookupSubTable::Extension))
-    {
-      /* The spec says all subtables of an Extension lookup should
-       * have the same type, which shall not be the Extension type
-       * itself (but we already checked for that).
-       * This is specially important if one has a reverse type! */
-      unsigned int type = get_subtable (0).u.extension.get_type ();
-      unsigned int count = get_subtable_count ();
-      for (unsigned int i = 1; i < count; i++)
-        if (get_subtable (i).u.extension.get_type () != type)
-         return_trace (false);
-    }
-    return_trace (true);
-  }
+  bool sanitize (hb_sanitize_context_t *c) const
+  { return Lookup::sanitize<SubTable> (c); }
 };
 
-typedef OffsetListOf<SubstLookup> SubstLookupList;
-
 /*
  * GSUB -- Glyph Substitution
  * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
@@ -1303,61 +1410,47 @@ typedef OffsetListOf<SubstLookup> SubstLookupList;
 
 struct GSUB : GSUBGPOS
 {
-  static const hb_tag_t tableTag       = HB_OT_TAG_GSUB;
+  static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
 
-  inline const SubstLookup& get_lookup (unsigned int i) const
+  const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
-  static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
+  bool subset (hb_subset_context_t *c) const
+  { return GSUBGPOS::subset<SubstLookup> (c); }
 
-  inline bool sanitize (hb_sanitize_context_t *c) const
-  {
-    TRACE_SANITIZE (this);
-    if (unlikely (!GSUBGPOS::sanitize (c))) return_trace (false);
-    const OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
-    return_trace (list.sanitize (c, this));
-  }
-};
+  bool sanitize (hb_sanitize_context_t *c) const
+  { return GSUBGPOS::sanitize<SubstLookup> (c); }
 
+  HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
+                                  hb_face_t *face) const;
 
-void
-GSUB::substitute_start (hb_font_t *font, hb_buffer_t *buffer)
-{
-  _hb_buffer_assert_gsubgpos_vars (buffer);
+  typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
+};
 
-  const GDEF &gdef = *hb_ot_layout_from_face (font->face)->gdef;
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-  {
-    _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
-    _hb_glyph_info_clear_lig_props (&buffer->info[i]);
-    buffer->info[i].syllable() = 0;
-  }
-}
+
+struct GSUB_accelerator_t : GSUB::accelerator_t {};
 
 
 /* Out-of-class implementation for methods recursing */
 
-/*static*/ inline bool ExtensionSubst::is_reverse (void) const
+/*static*/ inline bool ExtensionSubst::is_reverse () const
 {
   unsigned int type = get_type ();
-  if (unlikely (type == SubstLookupSubTable::Extension))
-    return CastR<ExtensionSubst> (get_subtable<LookupSubTable>()).is_reverse ();
+  if (unlikely (type == SubTable::Extension))
+    return CastR<ExtensionSubst> (get_subtable<SubTable>()).is_reverse ();
   return SubstLookup::lookup_type_is_reverse (type);
 }
 
 template <typename context_t>
 /*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
-  const SubstLookup &l = gsub.get_lookup (lookup_index);
+  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
   return l.dispatch (c);
 }
 
 /*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
 {
-  const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
-  const SubstLookup &l = gsub.get_lookup (lookup_index);
+  const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
   unsigned int saved_lookup_index = c->lookup_index;
   c->set_lookup_index (lookup_index);
@@ -1368,7 +1461,6 @@ template <typename context_t>
   return ret;
 }
 
-
 } /* namespace OT */