[repacker] Handle the case where a subgraph root has an incoming 32 and 16 bit edge.

In this case the entire subgraph from that root will be duplicated.
This commit is contained in:
Garret Rieger 2021-09-29 14:28:27 -07:00
parent 816c5302a7
commit 0dccbf368f
2 changed files with 146 additions and 8 deletions

View File

@ -416,22 +416,21 @@ struct graph_t
* that originate from outside of the subgraph will be removed by duplicating the linked to * that originate from outside of the subgraph will be removed by duplicating the linked to
* object. * object.
*/ */
bool isolate_subgraph (hb_set_t roots) bool isolate_subgraph (const hb_set_t& roots)
{ {
update_parents (); update_parents ();
hb_hashmap_t<unsigned, unsigned> subgraph; hb_hashmap_t<unsigned, unsigned> subgraph;
// incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these
// set the subgraph incoming edge count to match all of root_idx's incoming edges // set the subgraph incoming edge count to match all of root_idx's incoming edges
// hb_set_t parents;
// TODO(grieger): the above assumption does not always hold, as there are 16 bit incoming
// edges, handle that case here by not including them in the count.
for (unsigned root_idx : roots) for (unsigned root_idx : roots)
{ {
subgraph.set (root_idx, vertices_[root_idx].incoming_edges ()); subgraph.set (root_idx, wide_parents (root_idx, parents));
find_subgraph (root_idx, subgraph); find_subgraph (root_idx, subgraph);
} }
unsigned original_root_idx = root_idx ();
hb_hashmap_t<unsigned, unsigned> index_map; hb_hashmap_t<unsigned, unsigned> index_map;
bool made_changes = false; bool made_changes = false;
for (auto entry : subgraph.iter ()) for (auto entry : subgraph.iter ())
@ -450,6 +449,14 @@ struct graph_t
if (!made_changes) if (!made_changes)
return false; return false;
if (original_root_idx != root_idx ()
&& parents.has (original_root_idx))
{
// If the root idx has changed since parents was determined, update root idx in parents
parents.add (root_idx ());
parents.del (original_root_idx);
}
auto new_subgraph = auto new_subgraph =
+ subgraph.keys () + subgraph.keys ()
| hb_map([&] (unsigned node_idx) { | hb_map([&] (unsigned node_idx) {
@ -457,7 +464,10 @@ struct graph_t
return node_idx; return node_idx;
}) })
; ;
remap_obj_indices (index_map, new_subgraph); remap_obj_indices (index_map, new_subgraph);
remap_obj_indices (index_map, parents.iter ());
return true; return true;
} }
@ -538,6 +548,10 @@ struct graph_t
vertices_[clone_idx] = *clone; vertices_[clone_idx] = *clone;
vertices_[vertices_.length - 1] = root; vertices_[vertices_.length - 1] = root;
// Since the root moved, update the parents arrays of all children on the root.
for (const auto& l : root.obj.links)
vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ());
return clone_idx; return clone_idx;
} }
@ -571,15 +585,12 @@ struct graph_t
unsigned clone_idx = duplicate (child_idx); unsigned clone_idx = duplicate (child_idx);
if (clone_idx == (unsigned) -1) return false; if (clone_idx == (unsigned) -1) return false;
// duplicate shifts the root node idx, so if parent_idx was root update it. // duplicate shifts the root node idx, so if parent_idx was root update it.
unsigned original_parent_idx = parent_idx;
if (parent_idx == clone_idx) parent_idx++; if (parent_idx == clone_idx) parent_idx++;
auto& parent = vertices_[parent_idx]; auto& parent = vertices_[parent_idx];
for (unsigned i = 0; i < parent.obj.links.length; i++) for (unsigned i = 0; i < parent.obj.links.length; i++)
{ {
auto& l = parent.obj.links[i]; auto& l = parent.obj.links[i];
if (original_parent_idx != parent_idx)
vertices_[l.objidx].remap_parent (original_parent_idx, parent_idx);
if (l.objidx != child_idx) if (l.objidx != child_idx)
continue; continue;
@ -657,6 +668,26 @@ struct graph_t
private: private:
/*
* Returns the numbers of incoming edges that are 32bits wide.
*/
unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const
{
unsigned count = 0;
for (unsigned p : vertices_[node_idx].parents)
{
for (const auto& l : vertices_[p].obj.links)
{
if (l.objidx == node_idx && l.width == 4 && !l.is_signed)
{
count++;
parents.add (p);
}
}
}
return count;
}
bool check_success (bool success) bool check_success (bool success)
{ return this->successful && (success || (err_other_error (), false)); } { return this->successful && (success || (err_other_error (), false)); }
@ -673,7 +704,9 @@ struct graph_t
for (unsigned p = 0; p < vertices_.length; p++) for (unsigned p = 0; p < vertices_.length; p++)
{ {
for (auto& l : vertices_[p].obj.links) for (auto& l : vertices_[p].obj.links)
{
vertices_[l.objidx].parents.push (p); vertices_[l.objidx].parents.push (p);
}
} }
parents_invalid = false; parents_invalid = false;

View File

@ -415,6 +415,74 @@ populate_serializer_spaces_16bit_connection_expected (hb_serialize_context_t* c)
c->end_serialize (); c->end_serialize ();
} }
static void
populate_serializer_short_and_wide_subgraph_root (hb_serialize_context_t* c)
{
std::string large_string(70000, 'a');
c->start_serialize<char> ();
unsigned obj_e = add_object ("e", 1, c);
start_object (large_string.c_str (), 40000, c);
add_offset (obj_e, c);
unsigned obj_c = c->pop_pack (false);
start_object (large_string.c_str (), 40000, c);
add_offset (obj_c, c);
unsigned obj_d = c->pop_pack (false);
start_object ("b", 1, c);
add_offset (obj_c, c);
add_offset (obj_e, c);
unsigned obj_b = c->pop_pack (false);
start_object ("a", 1, c);
add_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_short_and_wide_subgraph_root_expected (hb_serialize_context_t* c)
{
std::string large_string(70000, 'a');
c->start_serialize<char> ();
unsigned obj_e_prime = add_object ("e", 1, c);
start_object (large_string.c_str (), 40000, c);
add_offset (obj_e_prime, c);
unsigned obj_c_prime = c->pop_pack (false);
start_object (large_string.c_str (), 40000, c);
add_offset (obj_c_prime, c);
unsigned obj_d = c->pop_pack (false);
unsigned obj_e = add_object ("e", 1, c);
start_object (large_string.c_str (), 40000, c);
add_offset (obj_e, c);
unsigned obj_c = c->pop_pack (false);
start_object ("b", 1, c);
add_offset (obj_c, c);
add_offset (obj_e, c);
unsigned obj_b = c->pop_pack (false);
start_object ("a", 1, c);
add_offset (obj_b, c);
add_wide_offset (obj_c_prime, c);
add_wide_offset (obj_d, c);
c->pop_pack (false);
c->end_serialize();
}
static void static void
populate_serializer_complex_1 (hb_serialize_context_t* c) populate_serializer_complex_1 (hb_serialize_context_t* c)
{ {
@ -890,6 +958,42 @@ static void test_resolve_overflows_via_isolating_16bit_space ()
free (out_buffer); free (out_buffer);
} }
static void test_resolve_overflows_via_isolating_16bit_space_2 ()
{
size_t buffer_size = 160000;
void* buffer = malloc (buffer_size);
hb_serialize_context_t c (buffer, buffer_size);
populate_serializer_short_and_wide_subgraph_root (&c);
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_short_and_wide_subgraph_root_expected (&e);
hb_bytes_t expected_result = e.copy_bytes ();
assert (result.length == expected_result.length);
for (unsigned i = 0; i < expected_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_spaces () static void test_resolve_overflows_via_isolation_spaces ()
{ {
@ -936,6 +1040,7 @@ main (int argc, char **argv)
test_resolve_overflows_via_isolation_with_recursive_duplication (); test_resolve_overflows_via_isolation_with_recursive_duplication ();
test_resolve_overflows_via_isolation_spaces (); test_resolve_overflows_via_isolation_spaces ();
test_resolve_overflows_via_isolating_16bit_space (); test_resolve_overflows_via_isolating_16bit_space ();
test_resolve_overflows_via_isolating_16bit_space_2 ();
test_duplicate_leaf (); test_duplicate_leaf ();
test_duplicate_interior (); test_duplicate_interior ();
} }