[subset] GPOS4 MarkBase subsetting support
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index 725f294..743a736 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -73,6 +73,13 @@
 
   HB_INTERNAL static int cmp (const IntType *a, const IntType *b)
   { return b->cmp (*a); }
+  HB_INTERNAL static int cmp (const void *a, const void *b)
+  {
+    IntType *pa = (IntType *) a;
+    IntType *pb = (IntType *) b;
+
+    return pb->cmp (*pa);
+  }
   template <typename Type2>
   int cmp (Type2 a) const
   {
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index eb4b889..e0459c2 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -34,6 +34,11 @@
 
 namespace OT {
 
+struct MarkArray;
+static void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
+						 const MarkArray &mark_array,
+						 const hb_set_t  &glyphset,
+						 hb_map_t*        klass_mapping /* INOUT */);
 
 /* buffer **position** var allocations */
 #define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */
@@ -465,6 +470,27 @@
     return this+matrixZ[row * cols + col];
   }
 
+  template <typename Iterator,
+	    hb_requires (hb_is_iterator (Iterator))>
+  bool serialize (hb_serialize_context_t *c,
+		  unsigned                num_rows,
+		  AnchorMatrix const     *offset_matrix,
+		  Iterator                index_iter)
+  {
+    TRACE_SERIALIZE (this);
+    if (!index_iter.len ()) return_trace (false);
+    if (unlikely (!c->extend_min ((*this))))  return_trace (false);
+
+    this->rows = num_rows;
+    for (const unsigned i : index_iter)
+    {
+      auto *offset = c->embed (offset_matrix->matrixZ[i]);
+      offset->serialize_copy (c, offset_matrix->matrixZ[i], offset_matrix, this);
+    }
+
+    return_trace (true);
+  }
+
   bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
   {
     TRACE_SANITIZE (this);
@@ -478,7 +504,6 @@
   }
 
   HBUINT16	rows;			/* Number of rows */
-  protected:
   UnsizedArrayOf<OffsetTo<Anchor>>
 		matrixZ;		/* Matrix of offsets to Anchor tables--
 					 * from beginning of AnchorMatrix table */
@@ -491,12 +516,27 @@
 {
   friend struct MarkArray;
 
+  unsigned get_class () const { return (unsigned) klass; }
   bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
+  MarkRecord *copy (hb_serialize_context_t *c,
+		    const void             *src_base,
+		    const void             *dst_base,
+		    const hb_map_t         *klass_mapping) const
+  {
+    TRACE_SERIALIZE (this);
+    auto *out = c->embed (this);
+    if (unlikely (!out)) return_trace (nullptr);
+    
+    out->klass = klass_mapping->get (klass);
+    out->markAnchor.serialize_copy (c, markAnchor, src_base, dst_base);
+    return_trace (out);
+  }
+
   protected:
   HBUINT16	klass;			/* Class defined for this mark */
   OffsetTo<Anchor>
@@ -542,6 +582,20 @@
     return_trace (true);
   }
 
+  template<typename Iterator,
+	   hb_requires (hb_is_source_of (Iterator, MarkRecord))>
+  bool serialize (hb_serialize_context_t *c,
+		  const hb_map_t         *klass_mapping,
+		  const void             *src_base,
+		  Iterator                it)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    if (unlikely (!c->check_assign (len, it.len ()))) return_trace (false);
+    c->copy_all (it, src_base, this, klass_mapping);
+    return_trace (true);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -1514,6 +1568,29 @@
 					 * mark-minor--
 					 * ordered by class--zero-based. */
 
+static void Markclass_closure_and_remap_indexes (const Coverage  &mark_coverage,
+						 const MarkArray &mark_array,
+						 const hb_set_t  &glyphset,
+						 hb_map_t*        klass_mapping /* INOUT */)
+{
+  hb_set_t orig_classes;
+
+  + hb_zip (mark_coverage, mark_array)
+  | hb_filter (glyphset, hb_first)
+  | hb_map (hb_second)
+  | hb_map (&MarkRecord::get_class)
+  | hb_sink (orig_classes)
+  ;
+
+  unsigned idx = 0;
+  for (auto klass : orig_classes.iter ())
+  {
+    if (klass_mapping->has (klass)) continue;
+    klass_mapping->set (klass, idx);
+    idx++;
+  }
+}
+
 struct MarkBasePosFormat1
 {
   bool intersects (const hb_set_t *glyphs) const
@@ -1573,8 +1650,70 @@
   bool subset (hb_subset_context_t *c) const
   {
     TRACE_SUBSET (this);
-    // TODO(subset)
-    return_trace (false);
+    const hb_set_t &glyphset = *c->plan->glyphset ();
+    const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+    auto *out = c->serializer->start_embed (*this);
+    if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+    out->format = format;
+
+    hb_map_t klass_mapping;
+    Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
+    
+    if (!klass_mapping.get_population ()) return_trace (false);
+    out->classCount = klass_mapping.get_population ();
+
+    auto mark_iter =
+    + hb_zip (this+markCoverage, this+markArray)
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+    + mark_iter
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    if (!out->markCoverage.serialize (c->serializer, out)
+			  .serialize (c->serializer, new_coverage.iter ()))
+      return_trace (false);
+
+    out->markArray.serialize (c->serializer, out)
+		  .serialize (c->serializer, &klass_mapping, &(this+markArray), + mark_iter
+										| hb_map (hb_second));
+
+    unsigned basecount = (this+baseArray).rows;
+    auto base_iter =
+    + hb_zip (this+baseCoverage, hb_range (basecount))
+    | hb_filter (glyphset, hb_first)
+    ;
+
+    new_coverage.reset ();
+    + base_iter
+    | hb_map (hb_first)
+    | hb_map (glyph_map)
+    | hb_sink (new_coverage)
+    ;
+
+    if (!out->baseCoverage.serialize (c->serializer, out)
+			  .serialize (c->serializer, new_coverage.iter ()))
+      return_trace (false);
+
+    hb_sorted_vector_t<unsigned> base_indexes;
+    for (const unsigned row : + base_iter
+			      | hb_map (hb_second))
+    {
+      + hb_range ((unsigned) classCount)
+      | hb_filter (klass_mapping)
+      | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
+      | hb_sink (base_indexes)
+      ;
+    }
+    out->baseArray.serialize (c->serializer, out)
+		  .serialize (c->serializer, base_iter.len (), &(this+baseArray), base_indexes.iter ());
+
+    return_trace (true);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index 4a5bb30..81aee95 100644
--- a/test/subset/data/Makefile.am
+++ b/test/subset/data/Makefile.am
@@ -16,6 +16,7 @@
 	expected/layout.gpos \
 	expected/layout.gpos2 \
 	expected/layout.gpos3 \
+	expected/layout.gpos4 \
 	expected/layout.gsub6 \
 	expected/cmap \
 	expected/cmap14 \
diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index d642db8..b5b4a1f 100644
--- a/test/subset/data/Makefile.sources
+++ b/test/subset/data/Makefile.sources
@@ -8,6 +8,7 @@
 	tests/layout.gpos.tests \
 	tests/layout.gpos2.tests \
 	tests/layout.gpos3.tests \
+	tests/layout.gpos4.tests \
 	tests/layout.gsub6.tests \
 	tests/cmap.tests \
 	tests/cmap14.tests \
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43,44.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43,44.otf
new file mode 100644
index 0000000..1c2e5a4
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43,44.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43,45.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43,45.otf
new file mode 100644
index 0000000..2fed45d
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43,45.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43.otf
new file mode 100644
index 0000000..0007615
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42,43.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42.otf
new file mode 100644
index 0000000..8eb6ace
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,42.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,44,45,46.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,44,45,46.otf
new file mode 100644
index 0000000..73315f3
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,44,45,46.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,44.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,44.otf
new file mode 100644
index 0000000..4005a0d
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,44.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,45.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,45.otf
new file mode 100644
index 0000000..c9f261c
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43,45.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43.otf
new file mode 100644
index 0000000..9ed6a9b
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41,43.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41.otf
new file mode 100644
index 0000000..f50cc90
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.41.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf
new file mode 100644
index 0000000..d4f9fc0
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout-retain-gids.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43,44.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43,44.otf
new file mode 100644
index 0000000..5447973
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43,44.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43,45.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43,45.otf
new file mode 100644
index 0000000..e6c891b
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43,45.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43.otf
new file mode 100644
index 0000000..127f798
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42,43.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42.otf
new file mode 100644
index 0000000..6cafcb8
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,42.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,44,45,46.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,44,45,46.otf
new file mode 100644
index 0000000..5f47542
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,44,45,46.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,44.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,44.otf
new file mode 100644
index 0000000..8fe4d63
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,44.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,45.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,45.otf
new file mode 100644
index 0000000..147ed57
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43,45.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43.otf
new file mode 100644
index 0000000..1b0f243
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41,43.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41.otf
new file mode 100644
index 0000000..657b686
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.41.otf
Binary files differ
diff --git a/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf
new file mode 100644
index 0000000..d4f9fc0
--- /dev/null
+++ b/test/subset/data/expected/layout.gpos4/gpos4_multiple_anchors_1.keep-layout.retain-all-codepoint.otf
Binary files differ
diff --git a/test/subset/data/fonts/gpos4_multiple_anchors_1.otf b/test/subset/data/fonts/gpos4_multiple_anchors_1.otf
new file mode 100644
index 0000000..e77cbb6
--- /dev/null
+++ b/test/subset/data/fonts/gpos4_multiple_anchors_1.otf
Binary files differ
diff --git a/test/subset/data/tests/layout.gpos4.tests b/test/subset/data/tests/layout.gpos4.tests
new file mode 100644
index 0000000..fd2b688
--- /dev/null
+++ b/test/subset/data/tests/layout.gpos4.tests
@@ -0,0 +1,18 @@
+FONTS:
+gpos4_multiple_anchors_1.otf
+
+PROFILES:
+keep-layout.txt
+keep-layout-retain-gids.txt
+
+SUBSETS:
+A
+AB
+AC
+ABC
+ACE
+ABCE
+ACD
+ACDEF
+ABCD
+*