[repacker] extract graph serialization code into a seperate file.

This commit is contained in:
Garret Rieger 2022-06-24 19:20:20 +00:00
parent 20b02a672d
commit 7078560e33
4 changed files with 135 additions and 124 deletions

View File

@ -129,12 +129,6 @@ struct graph_t
} }
}; };
struct overflow_record_t
{
unsigned parent;
unsigned child;
};
/* /*
* A topological sorting of an object graph. Ordered * A topological sorting of an object graph. Ordered
* in reverse serialization order (first object in the * in reverse serialization order (first object in the
@ -548,36 +542,6 @@ struct graph_t
return made_change; return made_change;
} }
/*
* Will any offsets overflow on graph when it's serialized?
*/
bool will_overflow (hb_vector_t<overflow_record_t>* overflows = nullptr)
{
if (overflows) overflows->resize (0);
update_positions ();
for (int parent_idx = vertices_.length - 1; parent_idx >= 0; parent_idx--)
{
// Don't need to check virtual links for overflow
for (const auto& link : vertices_[parent_idx].obj.real_links)
{
int64_t offset = compute_offset (parent_idx, link);
if (is_valid_offset (offset, link))
continue;
if (!overflows) return true;
overflow_record_t r;
r.parent = parent_idx;
r.child = link.objidx;
overflows->push (r);
}
}
if (!overflows) return false;
return overflows->length;
}
void print_orphaned_nodes () void print_orphaned_nodes ()
{ {
if (!DEBUG_ENABLED(SUBSET_REPACK)) return; if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
@ -594,35 +558,6 @@ struct graph_t
} }
} }
void print_overflows (const hb_vector_t<overflow_record_t>& overflows)
{
if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
update_parents ();
int limit = 10;
for (const auto& o : overflows)
{
if (!limit--) break;
const auto& parent = vertices_[o.parent];
const auto& child = vertices_[o.child];
DEBUG_MSG (SUBSET_REPACK, nullptr,
" overflow from "
"%4d (%4d in, %4d out, space %2d) => "
"%4d (%4d in, %4d out, space %2d)",
o.parent,
parent.incoming_edges (),
parent.obj.real_links.length + parent.obj.virtual_links.length,
space_for (o.parent),
o.child,
child.incoming_edges (),
child.obj.real_links.length + child.obj.virtual_links.length,
space_for (o.child));
}
if (overflows.length > 10) {
DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %d more overflows.", overflows.length - 10);
}
}
unsigned num_roots_for_space (unsigned space) const unsigned num_roots_for_space (unsigned space) const
{ {
return num_roots_for_space_[space]; return num_roots_for_space_[space];
@ -710,6 +645,7 @@ struct graph_t
bool check_success (bool success) bool check_success (bool success)
{ return this->successful && (success || ((void) err_other_error (), false)); } { return this->successful && (success || ((void) err_other_error (), false)); }
public:
/* /*
* Creates a map from objid to # of incoming edges. * Creates a map from objid to # of incoming edges.
*/ */
@ -818,52 +754,7 @@ struct graph_t
distance_invalid = false; distance_invalid = false;
} }
int64_t compute_offset ( private:
unsigned parent_idx,
const hb_serialize_context_t::object_t::link_t& link) const
{
const auto& parent = vertices_[parent_idx];
const auto& child = vertices_[link.objidx];
int64_t offset = 0;
switch ((hb_serialize_context_t::whence_t) link.whence) {
case hb_serialize_context_t::whence_t::Head:
offset = child.start - parent.start; break;
case hb_serialize_context_t::whence_t::Tail:
offset = child.start - parent.end; break;
case hb_serialize_context_t::whence_t::Absolute:
offset = child.start; break;
}
assert (offset >= link.bias);
offset -= link.bias;
return offset;
}
bool is_valid_offset (int64_t offset,
const hb_serialize_context_t::object_t::link_t& link) const
{
if (unlikely (!link.width))
// Virtual links can't overflow.
return link.is_signed || offset >= 0;
if (link.is_signed)
{
if (link.width == 4)
return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
else
return offset >= -(1 << 15) && offset < (1 << 15);
}
else
{
if (link.width == 4)
return offset >= 0 && offset < ((int64_t) 1 << 32);
else if (link.width == 3)
return offset >= 0 && offset < ((int32_t) 1 << 24);
else
return offset >= 0 && offset < (1 << 16);
}
}
/* /*
* Updates a link in the graph to point to a different object. Corrects the * Updates a link in the graph to point to a different object. Corrects the
* parents vector on the previous and new child nodes. * parents vector on the previous and new child nodes.

View File

@ -29,6 +29,125 @@
namespace graph { namespace graph {
struct overflow_record_t
{
unsigned parent;
unsigned child;
};
inline
int64_t compute_offset (
const graph_t& graph,
unsigned parent_idx,
const hb_serialize_context_t::object_t::link_t& link)
{
const auto& parent = graph.vertices_[parent_idx];
const auto& child = graph.vertices_[link.objidx];
int64_t offset = 0;
switch ((hb_serialize_context_t::whence_t) link.whence) {
case hb_serialize_context_t::whence_t::Head:
offset = child.start - parent.start; break;
case hb_serialize_context_t::whence_t::Tail:
offset = child.start - parent.end; break;
case hb_serialize_context_t::whence_t::Absolute:
offset = child.start; break;
}
assert (offset >= link.bias);
offset -= link.bias;
return offset;
}
inline
bool is_valid_offset (int64_t offset,
const hb_serialize_context_t::object_t::link_t& link)
{
if (unlikely (!link.width))
// Virtual links can't overflow.
return link.is_signed || offset >= 0;
if (link.is_signed)
{
if (link.width == 4)
return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
else
return offset >= -(1 << 15) && offset < (1 << 15);
}
else
{
if (link.width == 4)
return offset >= 0 && offset < ((int64_t) 1 << 32);
else if (link.width == 3)
return offset >= 0 && offset < ((int32_t) 1 << 24);
else
return offset >= 0 && offset < (1 << 16);
}
}
/*
* Will any offsets overflow on graph when it's serialized?
*/
inline bool
will_overflow (graph_t& graph,
hb_vector_t<overflow_record_t>* overflows = nullptr)
{
if (overflows) overflows->resize (0);
graph.update_positions ();
const auto& vertices = graph.vertices_;
for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--)
{
// Don't need to check virtual links for overflow
for (const auto& link : vertices[parent_idx].obj.real_links)
{
int64_t offset = compute_offset (graph, parent_idx, link);
if (is_valid_offset (offset, link))
continue;
if (!overflows) return true;
overflow_record_t r;
r.parent = parent_idx;
r.child = link.objidx;
overflows->push (r);
}
}
if (!overflows) return false;
return overflows->length;
}
inline
void print_overflows (graph_t& graph,
const hb_vector_t<overflow_record_t>& overflows)
{
if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
graph.update_parents ();
int limit = 10;
for (const auto& o : overflows)
{
if (!limit--) break;
const auto& parent = graph.vertices_[o.parent];
const auto& child = graph.vertices_[o.child];
DEBUG_MSG (SUBSET_REPACK, nullptr,
" overflow from "
"%4d (%4d in, %4d out, space %2d) => "
"%4d (%4d in, %4d out, space %2d)",
o.parent,
parent.incoming_edges (),
parent.obj.real_links.length + parent.obj.virtual_links.length,
graph.space_for (o.parent),
o.child,
child.incoming_edges (),
child.obj.real_links.length + child.obj.virtual_links.length,
graph.space_for (o.child));
}
if (overflows.length > 10) {
DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %d more overflows.", overflows.length - 10);
}
}
template <typename O> inline void template <typename O> inline void
serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
char* head, char* head,

View File

@ -43,7 +43,7 @@ using graph::graph_t;
*/ */
static inline static inline
bool _try_isolating_subgraphs (const hb_vector_t<graph_t::overflow_record_t>& overflows, bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& overflows,
graph_t& sorted_graph) graph_t& sorted_graph)
{ {
unsigned space = 0; unsigned space = 0;
@ -51,7 +51,7 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph_t::overflow_record_t>& ov
for (int i = overflows.length - 1; i >= 0; i--) for (int i = overflows.length - 1; i >= 0; i--)
{ {
const graph_t::overflow_record_t& r = overflows[i]; const graph::overflow_record_t& r = overflows[i];
unsigned root; unsigned root;
unsigned overflow_space = sorted_graph.space_for (r.parent, &root); unsigned overflow_space = sorted_graph.space_for (r.parent, &root);
@ -93,7 +93,7 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph_t::overflow_record_t>& ov
} }
static inline static inline
bool _process_overflows (const hb_vector_t<graph_t::overflow_record_t>& overflows, bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
hb_set_t& priority_bumped_parents, hb_set_t& priority_bumped_parents,
graph_t& sorted_graph) graph_t& sorted_graph)
{ {
@ -102,7 +102,7 @@ bool _process_overflows (const hb_vector_t<graph_t::overflow_record_t>& overflow
// Try resolving the furthest overflows first. // Try resolving the furthest overflows first.
for (int i = overflows.length - 1; i >= 0; i--) for (int i = overflows.length - 1; i >= 0; i--)
{ {
const graph_t::overflow_record_t& r = overflows[i]; const graph::overflow_record_t& r = overflows[i];
const auto& child = sorted_graph.vertices_[r.child]; const auto& child = sorted_graph.vertices_[r.child];
if (child.is_shared ()) if (child.is_shared ())
{ {
@ -161,14 +161,15 @@ hb_resolve_overflows (const T& packed,
graph_t sorted_graph (packed); graph_t sorted_graph (packed);
sorted_graph.sort_shortest_distance (); sorted_graph.sort_shortest_distance ();
if (!sorted_graph.will_overflow ()) bool will_overflow = graph::will_overflow (sorted_graph);
if (!will_overflow)
{ {
return graph::serialize (sorted_graph); return graph::serialize (sorted_graph);
} }
if ((table_tag == HB_OT_TAG_GPOS if ((table_tag == HB_OT_TAG_GPOS
|| table_tag == HB_OT_TAG_GSUB) || table_tag == HB_OT_TAG_GSUB)
&& sorted_graph.will_overflow ()) && will_overflow)
{ {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs."); DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs.");
if (sorted_graph.assign_32bit_spaces ()) if (sorted_graph.assign_32bit_spaces ())
@ -176,13 +177,13 @@ hb_resolve_overflows (const T& packed,
} }
unsigned round = 0; unsigned round = 0;
hb_vector_t<graph_t::overflow_record_t> overflows; hb_vector_t<graph::overflow_record_t> overflows;
// TODO(garretrieger): select a good limit for max rounds. // TODO(garretrieger): select a good limit for max rounds.
while (!sorted_graph.in_error () while (!sorted_graph.in_error ()
&& sorted_graph.will_overflow (&overflows) && graph::will_overflow (sorted_graph, &overflows)
&& round++ < max_rounds) { && round++ < max_rounds) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Overflow resolution round %d ===", round); DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Overflow resolution round %d ===", round);
sorted_graph.print_overflows (overflows); print_overflows (sorted_graph, overflows);
hb_set_t priority_bumped_parents; hb_set_t priority_bumped_parents;
@ -204,7 +205,7 @@ hb_resolve_overflows (const T& packed,
return nullptr; return nullptr;
} }
if (sorted_graph.will_overflow ()) if (graph::will_overflow (sorted_graph))
{ {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed."); DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed.");
return nullptr; return nullptr;

View File

@ -949,7 +949,7 @@ static void test_will_overflow_1 ()
populate_serializer_complex_2 (&c); populate_serializer_complex_2 (&c);
graph_t graph (c.object_graph ()); graph_t graph (c.object_graph ());
assert (!graph.will_overflow (nullptr)); assert (!graph::will_overflow (graph, nullptr));
free (buffer); free (buffer);
} }
@ -962,7 +962,7 @@ static void test_will_overflow_2 ()
populate_serializer_with_overflow (&c); populate_serializer_with_overflow (&c);
graph_t graph (c.object_graph ()); graph_t graph (c.object_graph ());
assert (graph.will_overflow (nullptr)); assert (graph::will_overflow (graph, nullptr));
free (buffer); free (buffer);
} }
@ -975,7 +975,7 @@ static void test_will_overflow_3 ()
populate_serializer_with_dedup_overflow (&c); populate_serializer_with_dedup_overflow (&c);
graph_t graph (c.object_graph ()); graph_t graph (c.object_graph ());
assert (graph.will_overflow (nullptr)); assert (graph::will_overflow (graph, nullptr));
free (buffer); free (buffer);
} }