[repacker] begin implementing the ability to isolate extension subtables.

Adds isolate_subgraph operation to the repacker. This severs any links from outside a subgraph by duplicating the affected vertices. This will be used to isolate the subgraphs of a extension subtable from the rest of object graph. Thus allowing the extension subtable to be packed far away from the rest of the objects.
diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh
index cbab791..0bbfc35 100644
--- a/src/hb-repacker.hh
+++ b/src/hb-repacker.hh
@@ -329,24 +329,71 @@
   }
 
   /*
-   * Creates a copy of child and re-assigns the link from
-   * parent to the clone. The copy is a shallow copy, objects
-   * linked from child are not duplicated.
+   * Finds any links using 32 bits and isolates the subgraphs they point too.
    */
-  void duplicate (unsigned parent_idx, unsigned child_idx)
+  void isolate_32bit_links ()
   {
-    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
-               parent_idx, child_idx);
+    for (const vertex_t& v : vertices_)
+    {
+      for (auto& l : v.obj.links)
+      {
+        if (l.width == 4 && !l.is_signed)
+          isolate_subgraph (l.objidx);
+      }
+    }
+  }
 
+  /*
+   * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph
+   * that originate from outside of the subgraph will be removed by duplicating the linked to
+   * object.
+   */
+  void isolate_subgraph (unsigned root_idx)
+  {
+    hb_hashmap_t<unsigned, unsigned> subgraph;
+    hb_hashmap_t<unsigned, unsigned> index_map;
+    find_subgraph(root_idx, subgraph);
+
+    for (auto entry : subgraph.iter ())
+    {
+      const auto& node = vertices_[entry.first];
+      unsigned subgraph_incoming_edges = entry.second;
+
+      if (node.incoming_edges <= subgraph_incoming_edges)
+        // Only  de-dup objects with incoming links from outside the subgraph.
+        index_map.set (entry.first, duplicate (entry.first));
+    }
+
+    remap_obj_indices (index_map, subgraph.keys ());
+  }
+
+  void find_subgraph (unsigned node_idx, hb_hashmap_t<unsigned, unsigned>& subgraph)
+  {
+    if (subgraph.has (node_idx))
+    {
+      subgraph.set (node_idx, subgraph[node_idx] + 1);
+      return;
+    }
+
+    subgraph.set (node_idx, 1);
+    for (const auto& link : vertices_[node_idx].obj.links)
+      find_subgraph (link.objidx, subgraph);
+  }
+
+  /*
+   * Creates a copy of node_idx and returns it's new index.
+   */
+  unsigned duplicate (unsigned node_idx)
+  {
     positions_invalid = true;
 
     auto* clone = vertices_.push ();
-    auto& child = vertices_[child_idx];
+    auto& child = vertices_[node_idx];
     clone_buffer_t* buffer = clone_buffers_.push ();
     if (vertices_.in_error ()
         || clone_buffers_.in_error ()
         || !check_success (buffer->copy (child.obj))) {
-      return;
+      return -1;
     }
 
     clone->obj.head = buffer->head;
@@ -358,25 +405,44 @@
 
     check_success (!clone->obj.links.in_error ());
 
-    auto& parent = vertices_[parent_idx];
-    unsigned clone_idx = vertices_.length - 2;
-    for (unsigned i = 0; i < parent.obj.links.length; i++)
-    {
-      auto& l = parent.obj.links[i];
-      if (l.objidx == child_idx)
-      {
-        l.objidx = clone_idx;
-        clone->incoming_edges++;
-        child.incoming_edges--;
-      }
-    }
-
     // The last object is the root of the graph, so swap back the root to the end.
     // The root's obj idx does change, however since it's root nothing else refers to it.
     // all other obj idx's will be unaffected.
     vertex_t root = vertices_[vertices_.length - 2];
     vertices_[vertices_.length - 2] = *clone;
     vertices_[vertices_.length - 1] = root;
+
+    return vertices_.length - 2;
+  }
+
+  /*
+   * Creates a copy of child and re-assigns the link from
+   * parent to the clone. The copy is a shallow copy, objects
+   * linked from child are not duplicated.
+   */
+  void duplicate (unsigned parent_idx, unsigned child_idx)
+  {
+    DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
+               parent_idx, child_idx);
+
+    unsigned clone_idx = duplicate (child_idx);
+    if (clone_idx == (unsigned) -1) return;
+    // duplicate shifts the root node idx, so if parent_idx was root update it.
+    if (parent_idx == clone_idx) parent_idx++;
+    auto& clone = vertices_[clone_idx];
+    auto& child = vertices_[child_idx];
+
+    auto& parent = vertices_[parent_idx];
+    for (unsigned i = 0; i < parent.obj.links.length; i++)
+    {
+      auto& l = parent.obj.links[i];
+      if (l.objidx == child_idx)
+      {
+        l.objidx = clone_idx;
+        clone.incoming_edges++;
+        child.incoming_edges--;
+      }
+    }
   }
 
   /*
@@ -599,6 +665,27 @@
   }
 
   /*
+   * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts.
+   */
+  template<typename Iterator, hb_requires (hb_is_iterator (Iterator))>
+  void remap_obj_indices (const hb_hashmap_t<unsigned, unsigned>& id_map,
+                          Iterator subgraph)
+  {
+    if (!id_map) return;
+    for (unsigned i : subgraph)
+    {
+      for (unsigned j = 0; j < vertices_[i].obj.links.length; j++)
+      {
+        auto& link = vertices_[i].obj.links[j];
+        if (!id_map.has (link.objidx)) continue;
+        vertices_[link.objidx].incoming_edges--;
+        link.objidx = id_map[link.objidx];
+        vertices_[link.objidx].incoming_edges++;
+      }
+    }
+  }
+
+  /*
    * Updates all objidx's in all links using the provided mapping.
    */
   void remap_obj_indices (const hb_vector_t<unsigned>& id_map,
@@ -747,6 +834,7 @@
 
       // TODO(garretrieger): add additional offset resolution strategies
       // - Promotion to extension lookups.
+      // - Isolate the sub graphs of extension sub tables.
       // - Table splitting.
     }