[repacker] extract graph serialization code into a seperate file.
This commit is contained in:
parent
20b02a672d
commit
7078560e33
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue