[repacker] add space assignment based on connected components.
Assign each connected component that is underneath one or more 32 bit offsets into a unique space. This ensures that 32 bit subgraphs which are connected are packed into the same space.
diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh
index 49a4b24..fdcef0f 100644
--- a/src/hb-repacker.hh
+++ b/src/hb-repacker.hh
@@ -361,6 +361,50 @@
}
/*
+ * Assign unique space numbers to each connected subgraph of 32 bit offset(s).
+ */
+ bool assign_32bit_spaces ()
+ {
+ unsigned root_index = root_idx ();
+ hb_set_t visited;
+ hb_set_t roots;
+ for (unsigned i = 0; i <= root_index; i++)
+ {
+ for (auto& l : vertices_[i].obj.links)
+ {
+ if (l.width == 4 && !l.is_signed)
+ {
+ visited.add (i);
+ roots.add (l.objidx);
+ }
+ }
+ }
+
+ if (!roots) return false;
+
+ unsigned space = 0;
+ while (roots)
+ {
+ unsigned next = HB_SET_VALUE_INVALID;
+ if (!roots.next (&next)) break;
+
+ hb_set_t connected_roots;
+ find_connected_nodes (next, roots, visited, connected_roots);
+
+ space++;
+ for (unsigned root : connected_roots)
+ {
+ DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, space);
+ vertices_[root].space = space;
+ distance_invalid = true;
+ positions_invalid = true;
+ }
+ }
+
+ return true;
+ }
+
+ /*
* Finds any links using 32 bits and isolates the subgraphs they point too.
*/
bool isolate_32bit_links ()
@@ -867,6 +911,37 @@
}
}
+ /*
+ * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped.
+ * For this search the graph is treated as being undirected.
+ *
+ * Connected targets will be added to connected and removed from targets. All visited nodes
+ * will be added to visited.
+ */
+ void find_connected_nodes (unsigned start_idx,
+ hb_set_t& targets,
+ hb_set_t& visited,
+ hb_set_t& connected)
+ {
+ if (visited.has (start_idx)) return;
+ visited.add (start_idx);
+
+ if (targets.has (start_idx))
+ {
+ targets.del (start_idx);
+ connected.add (start_idx);
+ }
+
+ const auto& v = vertices_[start_idx];
+
+ // Graph is treated as undirected so search children and parents of start_idx
+ for (const auto& l : v.obj.links)
+ find_connected_nodes (l.objidx, targets, visited, connected);
+
+ for (unsigned p : v.parents)
+ find_connected_nodes (p, targets, visited, connected);
+ }
+
public:
// TODO(garretrieger): make private, will need to move most of offset overflow code into graph.
hb_vector_t<vertex_t> vertices_;
@@ -956,9 +1031,9 @@
|| table_tag == HB_OT_TAG_GSUB)
&& sorted_graph.will_overflow ())
{
- if (sorted_graph.isolate_32bit_links ())
+ if (sorted_graph.assign_32bit_spaces ())
{
- DEBUG_MSG (SUBSET_REPACK, nullptr, "Isolated extension sub tables.");
+ DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs.");
sorted_graph.sort_shortest_distance ();
}
}
diff --git a/src/test-repacker.cc b/src/test-repacker.cc
index 32228f7..b377676 100644
--- a/src/test-repacker.cc
+++ b/src/test-repacker.cc
@@ -75,7 +75,7 @@
start_object ("abc", 3, c);
add_offset (obj_2, c);
add_offset (obj_1, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -94,7 +94,7 @@
add_offset (obj_3, c);
add_offset (obj_2, c);
add_offset (obj_1, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -129,16 +129,16 @@
start_object (large_string.c_str(), 60000, c);
add_offset (obj_4, c);
- unsigned obj_3 = c->pop_pack ();
+ unsigned obj_3 = c->pop_pack (false);
start_object (large_string.c_str(), 10000, c);
add_offset (obj_4, c);
- unsigned obj_2 = c->pop_pack ();
+ unsigned obj_2 = c->pop_pack (false);
start_object ("1", 1, c);
add_wide_offset (obj_3, c);
add_offset (obj_2, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -153,29 +153,33 @@
start_object ("e", 1, c);
add_offset (obj_f, c);
- unsigned obj_e = c->pop_pack ();
+ unsigned obj_e = c->pop_pack (false);
start_object ("cc", 2, c);
add_offset (obj_e, c);
- unsigned obj_c = c->pop_pack ();
+ unsigned obj_c = c->pop_pack (false);
start_object ("d", 1, c);
add_offset (obj_e, c);
- unsigned obj_d = c->pop_pack ();
+ unsigned obj_d = c->pop_pack (false);
+
+ start_object (large_string.c_str(), 60000, c);
+ add_offset (obj_d, c);
+ unsigned obj_h = c->pop_pack (false);
start_object (large_string.c_str(), 60000, c);
add_offset (obj_c, c);
- add_offset (obj_d, c);
- unsigned obj_b = c->pop_pack ();
+ add_offset (obj_h, c);
+ unsigned obj_b = c->pop_pack (false);
start_object (large_string.c_str(), 10000, c);
add_offset (obj_d, c);
- unsigned obj_g = c->pop_pack ();
+ unsigned obj_g = c->pop_pack (false);
start_object ("a", 1, c);
add_wide_offset (obj_b, c);
add_offset (obj_g, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -256,6 +260,57 @@
}
static void
+populate_serializer_spaces (hb_serialize_context_t* c, bool with_overflow)
+{
+ std::string large_string(70000, 'a');
+ c->start_serialize<char> ();
+
+ unsigned obj_i;
+
+ if (with_overflow)
+ obj_i = add_object ("i", 1, c);
+
+ // Space 2
+ unsigned obj_h = add_object ("h", 1, c);
+
+ start_object (large_string.c_str(), 30000, c);
+ add_offset (obj_h, c);
+ unsigned obj_e = c->pop_pack (false);
+
+ start_object ("b", 1, c);
+ add_offset (obj_e, c);
+ unsigned obj_b = c->pop_pack (false);
+
+ // Space 1
+ if (!with_overflow)
+ obj_i = add_object ("i", 1, c);
+
+ start_object (large_string.c_str(), 30000, c);
+ add_offset (obj_i, c);
+ unsigned obj_g = c->pop_pack (false);
+
+ start_object (large_string.c_str(), 30000, c);
+ add_offset (obj_i, c);
+ unsigned obj_f = c->pop_pack (false);
+
+ start_object ("d", 1, c);
+ add_offset (obj_g, c);
+ unsigned obj_d = c->pop_pack (false);
+
+ start_object ("c", 1, c);
+ add_offset (obj_f, c);
+ unsigned obj_c = c->pop_pack (false);
+
+ start_object ("a", 1, c);
+ add_wide_offset (obj_b, c);
+ add_wide_offset (obj_c, c);
+ add_wide_offset (obj_d, c);
+ c->pop_pack (false);
+
+ c->end_serialize();
+}
+
+static void
populate_serializer_complex_1 (hb_serialize_context_t* c)
{
c->start_serialize<char> ();
@@ -270,7 +325,7 @@
start_object ("abc", 3, c);
add_offset (obj_2, c);
add_offset (obj_4, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -296,7 +351,7 @@
add_offset (obj_2, c);
add_offset (obj_4, c);
add_offset (obj_5, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -326,7 +381,7 @@
add_offset (obj_2, c);
add_offset (obj_4, c);
add_offset (obj_5, c);
- c->pop_pack ();
+ c->pop_pack (false);
c->end_serialize();
}
@@ -608,6 +663,38 @@
free (out_buffer);
}
+static void test_resolve_overflows_via_space_assignment ()
+{
+ size_t buffer_size = 160000;
+ void* buffer = malloc (buffer_size);
+ hb_serialize_context_t c (buffer, buffer_size);
+ populate_serializer_spaces (&c, true);
+ graph_t graph (c.object_graph ());
+
+ void* out_buffer = malloc (buffer_size);
+ hb_serialize_context_t out (out_buffer, buffer_size);
+
+ assert (c.offset_overflow ());
+ hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), &out, 0);
+ assert (!out.offset_overflow ());
+ hb_bytes_t result = out.copy_bytes ();
+
+ void* expected_buffer = malloc (buffer_size);
+ hb_serialize_context_t e (expected_buffer, buffer_size);
+ assert (!e.offset_overflow ());
+ populate_serializer_spaces (&e, false);
+ hb_bytes_t expected_result = e.copy_bytes ();
+
+ assert (result.length == expected_result.length);
+ for (unsigned i = 0; i < result.length; i++)
+ assert (result[i] == expected_result[i]);
+
+ result.fini ();
+ expected_result.fini ();
+ free (buffer);
+ free (expected_buffer);
+ free (out_buffer);
+}
static void test_resolve_overflows_via_isolation ()
{
@@ -621,7 +708,7 @@
hb_serialize_context_t out (out_buffer, buffer_size);
assert (c.offset_overflow ());
- hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), &out, 0);
+ hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), &out, 1);
assert (!out.offset_overflow ());
hb_bytes_t result = out.copy_bytes ();
assert (result.length == (1 + 10000 + 60000 + 1 + 1
@@ -644,7 +731,7 @@
hb_serialize_context_t out (out_buffer, buffer_size);
assert (c.offset_overflow ());
- hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), &out, 0);
+ hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), &out, 1);
assert (!out.offset_overflow ());
hb_bytes_t result = out.copy_bytes ();
@@ -705,6 +792,7 @@
test_will_overflow_3 ();
test_resolve_overflows_via_sort ();
test_resolve_overflows_via_duplication ();
+ test_resolve_overflows_via_space_assignment ();
test_resolve_overflows_via_isolation ();
test_resolve_overflows_via_isolation_with_recursive_duplication ();
test_resolve_overflows_via_isolation_spaces ();