[graph] Use a hb_map_t to keep parents, instead of hb_vector_t
In some fonts, for example Noto Duployan-Regular, nodes can
have over a thousand parents... Speeds up 10% subsetting.
diff --git a/src/graph/classdef-graph.hh b/src/graph/classdef-graph.hh
index 4ae0c13..c143288 100644
--- a/src/graph/classdef-graph.hh
+++ b/src/graph/classdef-graph.hh
@@ -72,7 +72,7 @@
class_def_link->width = SmallTypes::size;
class_def_link->objidx = class_def_prime_id;
class_def_link->position = link_position;
- class_def_prime_vertex.parents.push (parent_id);
+ class_def_prime_vertex.add_parent (parent_id);
return true;
}
diff --git a/src/graph/coverage-graph.hh b/src/graph/coverage-graph.hh
index bd6e91a..4f44e07 100644
--- a/src/graph/coverage-graph.hh
+++ b/src/graph/coverage-graph.hh
@@ -96,7 +96,7 @@
coverage_link->width = SmallTypes::size;
coverage_link->objidx = coverage_prime_id;
coverage_link->position = link_position;
- coverage_prime_vertex.parents.push (parent_id);
+ coverage_prime_vertex.add_parent (parent_id);
return (Coverage*) coverage_prime_vertex.obj.head;
}
diff --git a/src/graph/graph.hh b/src/graph/graph.hh
index 9d35851..48c0548 100644
--- a/src/graph/graph.hh
+++ b/src/graph/graph.hh
@@ -44,10 +44,11 @@
hb_serialize_context_t::object_t obj;
int64_t distance = 0 ;
int64_t space = 0 ;
- hb_vector_t<unsigned> parents;
unsigned start = 0;
unsigned end = 0;
unsigned priority = 0;
+ unsigned incoming_edges_ = 0;
+ hb_hashmap_t<unsigned, unsigned> parents;
bool link_positions_valid (unsigned num_objects, bool removed_nil)
@@ -144,6 +145,7 @@
hb_swap (a.distance, b.distance);
hb_swap (a.space, b.space);
hb_swap (a.parents, b.parents);
+ hb_swap (a.incoming_edges_, b.incoming_edges_);
hb_swap (a.start, b.start);
hb_swap (a.end, b.end);
hb_swap (a.priority, b.priority);
@@ -164,22 +166,36 @@
bool is_shared () const
{
- return parents.length > 1;
+ return parents.get_population () > 1;
}
unsigned incoming_edges () const
{
- return parents.length;
+ return incoming_edges_;
+ }
+
+ void reset_parents ()
+ {
+ incoming_edges_ = 0;
+ parents.reset ();
+ }
+
+ void add_parent (unsigned parent_index)
+ {
+ incoming_edges_++;
+ parents.set (parent_index, parents[parent_index] + 1);
}
void remove_parent (unsigned parent_index)
{
- unsigned count = parents.length;
- for (unsigned i = 0; i < count; i++)
+ unsigned *v;
+ if (parents.has (parent_index, &v))
{
- if (parents.arrayZ[i] != parent_index) continue;
- parents.remove_unordered (i);
- break;
+ incoming_edges_--;
+ if (*v > 1)
+ *v -= 1;
+ else
+ parents.del (parent_index);
}
}
@@ -202,21 +218,35 @@
void remap_parents (const hb_vector_t<unsigned>& id_map)
{
- unsigned count = parents.length;
- for (unsigned i = 0; i < count; i++)
- parents.arrayZ[i] = id_map[parents.arrayZ[i]];
+ if (parents.get_population () == 1)
+ {
+ // Fast path for the common case.
+ const auto &_ = *parents.iter_ref ();
+ unsigned old_index = _.first;
+ unsigned new_index = id_map[old_index];
+ if (old_index != new_index)
+ {
+ unsigned v = _.second;
+ parents.del (old_index);
+ parents.set (new_index, v);
+ }
+ return;
+ }
+
+ hb_map_t new_parents;
+ new_parents.alloc (parents.get_population ());
+ for (auto _ : parents)
+ new_parents.set (id_map[_.first], _.second);
+
+ parents = std::move (new_parents);
}
void remap_parent (unsigned old_index, unsigned new_index)
{
- unsigned count = parents.length;
- for (unsigned i = 0; i < count; i++)
+ if (parents.has (old_index))
{
- if (parents.arrayZ[i] == old_index)
- {
- parents.arrayZ[i] = new_index;
- break;
- }
+ remove_parent (old_index);
+ add_parent (new_index);
}
}
@@ -423,7 +453,7 @@
link->width = 2;
link->objidx = child_id;
link->position = (char*) offset - (char*) v.obj.head;
- vertices_[child_id].parents.push (parent_id);
+ vertices_[child_id].add_parent (parent_id);
}
/*
@@ -609,7 +639,7 @@
{
unsigned child_idx = index_for_offset (node_idx, offset);
auto& child = vertices_[child_idx];
- for (unsigned p : child.parents)
+ for (unsigned p : child.parents.keys ())
{
if (p != node_idx) {
return duplicate (node_idx, child_idx);
@@ -828,7 +858,7 @@
new_link->position = (const char*) new_offset - (const char*) new_v.obj.head;
auto& child = vertices_[child_id];
- child.parents.push (new_parent_idx);
+ child.add_parent (new_parent_idx);
old_v.remove_real_link (child_id, old_offset);
child.remove_parent (old_parent_idx);
@@ -872,18 +902,18 @@
clone->obj.tail = child.obj.tail;
clone->distance = child.distance;
clone->space = child.space;
- clone->parents.reset ();
+ clone->reset_parents ();
unsigned clone_idx = vertices_.length - 2;
for (const auto& l : child.obj.real_links)
{
clone->obj.real_links.push (l);
- vertices_[l.objidx].parents.push (clone_idx);
+ vertices_[l.objidx].add_parent (clone_idx);
}
for (const auto& l : child.obj.virtual_links)
{
clone->obj.virtual_links.push (l);
- vertices_[l.objidx].parents.push (clone_idx);
+ vertices_[l.objidx].add_parent (clone_idx);
}
check_success (!clone->obj.real_links.in_error ());
@@ -1136,7 +1166,7 @@
return 0;
}
- return space_for (node.parents[0], root);
+ return space_for (*node.parents.keys (), root);
}
void err_other_error () { this->successful = false; }
@@ -1160,12 +1190,8 @@
unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const
{
unsigned count = 0;
- hb_set_t visited;
- for (unsigned p : vertices_[node_idx].parents)
+ for (unsigned p : vertices_[node_idx].parents.keys ())
{
- if (visited.has (p)) continue;
- visited.add (p);
-
// Only real links can be wide
for (const auto& l : vertices_[p].obj.real_links)
{
@@ -1195,13 +1221,13 @@
unsigned count = vertices_.length;
for (unsigned i = 0; i < count; i++)
- vertices_.arrayZ[i].parents.reset ();
+ vertices_.arrayZ[i].reset_parents ();
for (unsigned p = 0; p < count; p++)
{
for (auto& l : vertices_.arrayZ[p].obj.all_links ())
{
- vertices_[l.objidx].parents.push (p);
+ vertices_[l.objidx].add_parent (p);
}
}
@@ -1309,7 +1335,7 @@
unsigned old_idx = link.objidx;
link.objidx = new_idx;
vertices_[old_idx].remove_parent (parent_idx);
- vertices_[new_idx].parents.push (parent_idx);
+ vertices_[new_idx].add_parent (parent_idx);
}
/*
@@ -1379,7 +1405,7 @@
for (const auto& l : v.obj.all_links ())
find_connected_nodes (l.objidx, targets, visited, connected);
- for (unsigned p : v.parents)
+ for (unsigned p : v.parents.keys ())
find_connected_nodes (p, targets, visited, connected);
}
diff --git a/src/graph/gsubgpos-graph.hh b/src/graph/gsubgpos-graph.hh
index 78d5096..303517f 100644
--- a/src/graph/gsubgpos-graph.hh
+++ b/src/graph/gsubgpos-graph.hh
@@ -225,7 +225,7 @@
if (is_ext)
{
unsigned ext_id = create_extension_subtable (c, subtable_id, type);
- c.graph.vertices_[subtable_id].parents.push (ext_id);
+ c.graph.vertices_[subtable_id].add_parent (ext_id);
subtable_id = ext_id;
}
@@ -234,7 +234,7 @@
link->objidx = subtable_id;
link->position = (char*) &new_lookup->subTable[offset_index++] -
(char*) new_lookup;
- c.graph.vertices_[subtable_id].parents.push (this_index);
+ c.graph.vertices_[subtable_id].add_parent (this_index);
}
}
@@ -315,7 +315,7 @@
// Make extension point at the subtable.
auto& ext_vertex = c.graph.vertices_[ext_index];
auto& subtable_vertex = c.graph.vertices_[subtable_index];
- ext_vertex.parents.push (lookup_index);
+ ext_vertex.add_parent (lookup_index);
subtable_vertex.remap_parent (lookup_index, ext_index);
return true;
diff --git a/src/graph/pairpos-graph.hh b/src/graph/pairpos-graph.hh
index f655b71..ad158cc 100644
--- a/src/graph/pairpos-graph.hh
+++ b/src/graph/pairpos-graph.hh
@@ -419,7 +419,7 @@
class_def_link->width = SmallTypes::size;
class_def_link->objidx = class_def_2_id;
class_def_link->position = 10;
- graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id);
+ graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id);
graph.duplicate (pair_pos_prime_id, class_def_2_id);
return pair_pos_prime_id;