[layout] Add (back) inplace implementation

This is a forward-port from the (8yr old) accelerate-lookups branch.
The overhead is too high though, so going to abandon it. Posting for
posterity.
diff --git a/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
index af1cd7b..0b7b821 100644
--- a/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
+++ b/src/OT/Layout/GSUB/AlternateSubstFormat1.hh
@@ -27,6 +27,8 @@
     return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
+  inline bool is_inplace () const { return true; }
+
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
diff --git a/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
index 19dfe98..1564d68 100644
--- a/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
+++ b/src/OT/Layout/GSUB/LigatureSubstFormat1.hh
@@ -27,6 +27,8 @@
     return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
+  inline bool is_inplace () const { return false; }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     return
diff --git a/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
index 54c6dc8..1f3130f 100644
--- a/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
+++ b/src/OT/Layout/GSUB/MultipleSubstFormat1.hh
@@ -27,6 +27,17 @@
     return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
+  inline bool is_inplace () const
+  {
+    /* Some tools generate MultipleSubst with each substitute having length 1!
+     * So, check them. */
+    unsigned int count = sequence.len;
+    for (unsigned int i = 0; i < count; i++)
+       if (!(this+sequence[i]).is_inplace ())
+	 return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
diff --git a/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
index 7a79a9d..0466990 100644
--- a/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
+++ b/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh
@@ -40,6 +40,8 @@
     return_trace (substitute.sanitize (c));
   }
 
+  inline bool is_inplace () const { return true; }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverage).intersects (glyphs))
diff --git a/src/OT/Layout/GSUB/Sequence.hh b/src/OT/Layout/GSUB/Sequence.hh
index ebd451e..488542f 100644
--- a/src/OT/Layout/GSUB/Sequence.hh
+++ b/src/OT/Layout/GSUB/Sequence.hh
@@ -21,6 +21,8 @@
     return_trace (substitute.sanitize (c));
   }
 
+  inline bool is_inplace () const { return substitute.len == 1; }
+
   bool intersects (const hb_set_t *glyphs) const
   { return hb_all (substitute, glyphs); }
 
diff --git a/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/OT/Layout/GSUB/SingleSubstFormat1.hh
index 3c6b295..9b70609 100644
--- a/src/OT/Layout/GSUB/SingleSubstFormat1.hh
+++ b/src/OT/Layout/GSUB/SingleSubstFormat1.hh
@@ -26,6 +26,8 @@
     return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
+  inline bool is_inplace () const { return true; }
+
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
diff --git a/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/OT/Layout/GSUB/SingleSubstFormat2.hh
index df75bb5..de31d9b 100644
--- a/src/OT/Layout/GSUB/SingleSubstFormat2.hh
+++ b/src/OT/Layout/GSUB/SingleSubstFormat2.hh
@@ -27,6 +27,8 @@
     return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
+  inline bool is_inplace () const { return true; }
+
   bool intersects (const hb_set_t *glyphs) const
   { return (this+coverage).intersects (glyphs); }
 
diff --git a/src/OT/Layout/GSUB/SubstLookup.hh b/src/OT/Layout/GSUB/SubstLookup.hh
index 3419b5a..f8cd7e8 100644
--- a/src/OT/Layout/GSUB/SubstLookup.hh
+++ b/src/OT/Layout/GSUB/SubstLookup.hh
@@ -29,6 +29,13 @@
     return lookup_type_is_reverse (type);
   }
 
+  bool is_inplace (hb_face_t *face) const
+  {
+    hb_is_inplace_context_t c (face);
+    c.set_recurse_func (dispatch_recurse_func<hb_is_inplace_context_t>);
+    return dispatch (&c);
+  }
+
   bool may_have_non_1to1 () const
   {
     hb_have_non_1to1_context_t c;
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 0dbfa01..9fe1a81 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -2892,6 +2892,11 @@
     return false;
   }
 
+  bool is_inplace (hb_face_t *face) const
+  {
+    return true;
+  }
+
   bool apply (hb_ot_apply_context_t *c) const
   {
     TRACE_APPLY (this);
diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh
index adfec33..45be8dd 100644
--- a/src/hb-ot-layout-gsubgpos.hh
+++ b/src/hb-ot-layout-gsubgpos.hh
@@ -55,6 +55,49 @@
                             glyphs (glyphs_) {}
 };
 
+struct hb_is_inplace_context_t :
+       hb_dispatch_context_t<hb_is_inplace_context_t, bool>
+{
+  typedef return_t (*recurse_func_t) (hb_is_inplace_context_t *c, unsigned lookup_index);
+
+  template <typename T>
+  inline auto _dispatch (const T &obj, hb_priority<2>) HB_RETURN (return_t, obj.is_inplace (this) )
+  template <typename T>
+  inline auto _dispatch (const T &obj, hb_priority<1>) HB_RETURN (return_t, obj.is_inplace () )
+  template <typename T>
+  inline auto _dispatch (const T &obj, hb_priority<0>) HB_RETURN (return_t, false )
+  template <typename T>
+  inline return_t dispatch (const T &obj) { return _dispatch (obj, hb_prioritize); }
+
+  static return_t default_return_value (void) { return true; }
+  bool stop_sublookup_iteration (return_t r) const { return !r; }
+  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+
+  return_t recurse (unsigned int lookup_index)
+  {
+    if (memoize.has (lookup_index))
+      return memoize.get (lookup_index);
+    if (unlikely (nesting_level_left == 0) || !recurse_func)
+      return false;
+
+    nesting_level_left--;
+    bool ret = recurse_func (this, lookup_index);
+    memoize.set (lookup_index, ret);
+    nesting_level_left++;
+    return ret;
+  }
+
+  hb_face_t *face;
+  unsigned int nesting_level_left;
+  recurse_func_t recurse_func = nullptr;
+  hb_map_t memoize;
+
+  hb_is_inplace_context_t (hb_face_t *face_,
+                          unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
+                          face (face_),
+                          nesting_level_left (nesting_level_left_) {}
+};
+
 struct hb_have_non_1to1_context_t :
        hb_dispatch_context_t<hb_have_non_1to1_context_t, bool>
 {
@@ -1730,6 +1773,16 @@
 
 struct Rule
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
+    unsigned int count = lookupCount;
+    for (unsigned int i = 0; i < count; i++)
+      if (!c->recurse (lookupRecord[i].lookupListIndex))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const
   {
     return context_intersects (glyphs,
@@ -1856,6 +1909,15 @@
 
 struct RuleSet
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      if (!(this+rule[i]).is_inplace (c))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs,
 		   ContextClosureLookupContext &lookup_context) const
   {
@@ -1970,6 +2032,15 @@
 
 struct ContextFormat1
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+ruleSet[i]).is_inplace (c))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     struct ContextClosureLookupContext lookup_context = {
@@ -2119,6 +2190,15 @@
 
 struct ContextFormat2
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+ruleSet[i]).is_inplace (c))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverage).intersects (glyphs))
@@ -2367,6 +2447,16 @@
 
 struct ContextFormat3
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
+    unsigned int count = lookupCount;
+    for (unsigned int i = 0; i < count; i++)
+      if (!c->recurse (lookupRecord[i].lookupListIndex))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverageZ[0]).intersects (glyphs))
@@ -2699,6 +2789,18 @@
 
 struct ChainRule
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+    const Array16Of<HBUINT16> &lookahead = StructAfter<Array16Of<HBUINT16>> (input);
+    const Array16Of<LookupRecord> &lookup = StructAfter<Array16Of<LookupRecord>> (lookahead);
+    unsigned int count = lookup.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!c->recurse (lookup[i].lookupListIndex))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
   {
     const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
@@ -2889,6 +2991,15 @@
 
 struct ChainRuleSet
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    unsigned int num_rules = rule.len;
+    for (unsigned int i = 0; i < num_rules; i++)
+      if (!(this+rule[i]).is_inplace (c))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
   {
     return
@@ -3003,6 +3114,15 @@
 
 struct ChainContextFormat1
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+ruleSet[i]).is_inplace (c))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     struct ChainContextClosureLookupContext lookup_context = {
@@ -3150,6 +3270,15 @@
 
 struct ChainContextFormat2
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    unsigned int count = ruleSet.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!(this+ruleSet[i]).is_inplace (c))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     if (!(this+coverage).intersects (glyphs))
@@ -3457,6 +3586,18 @@
 
 struct ChainContextFormat3
 {
+  inline bool is_inplace (hb_is_inplace_context_t *c) const
+  {
+    const Array16OfOffset16To<Coverage> &input = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
+    const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (input);
+    const Array16Of<LookupRecord> &lookup = StructAfter<Array16Of<LookupRecord>> (lookahead);
+    unsigned int count = lookup.len;
+    for (unsigned int i = 0; i < count; i++)
+      if (!c->recurse (lookup[i].lookupListIndex))
+	return false;
+    return true;
+  }
+
   bool intersects (const hb_set_t *glyphs) const
   {
     const Array16OfOffset16To<Coverage> &input = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index addd4da..5d3879f 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -1918,12 +1918,19 @@
   OT::hb_ot_apply_context_t c (table_index, font, buffer);
   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
 
+  OT::hb_is_inplace_context_t inplace_c (font->face);
+  inplace_c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_is_inplace_context_t>);
+
   for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
   {
     const stage_map_t *stage = &stages[table_index][stage_index];
     for (; i < stage->last_lookup; i++)
     {
       unsigned int lookup_index = lookups[table_index][i].index;
+
+      //if (!Proxy::always_inplace)
+	//HB_UNUSED bool is_inplace = inplace_c.recurse (lookup_index); // XXX
+
       if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
       c.set_lookup_index (lookup_index);
       c.set_lookup_mask (lookups[table_index][i].mask);