[subset] add error tracking to the repacker.
Also check for allocation failures as needed.
This commit is contained in:
parent
b8b8c58b9f
commit
bb5c80a7c2
|
@ -36,9 +36,6 @@
|
||||||
|
|
||||||
struct graph_t
|
struct graph_t
|
||||||
{
|
{
|
||||||
// TODO(garretrieger): add an error tracking system similar to what serialize_context_t
|
|
||||||
// does.
|
|
||||||
|
|
||||||
struct vertex_t
|
struct vertex_t
|
||||||
{
|
{
|
||||||
vertex_t () :
|
vertex_t () :
|
||||||
|
@ -100,13 +97,16 @@ struct graph_t
|
||||||
{
|
{
|
||||||
clone_buffer_t () : head (nullptr), tail (nullptr) {}
|
clone_buffer_t () : head (nullptr), tail (nullptr) {}
|
||||||
|
|
||||||
void copy (const hb_serialize_context_t::object_t& object)
|
bool copy (const hb_serialize_context_t::object_t& object)
|
||||||
{
|
{
|
||||||
fini ();
|
fini ();
|
||||||
unsigned size = object.tail - object.head;
|
unsigned size = object.tail - object.head;
|
||||||
head = (char*) malloc (size);
|
head = (char*) malloc (size);
|
||||||
|
if (!head) return false;
|
||||||
|
|
||||||
memcpy (head, object.head, size);
|
memcpy (head, object.head, size);
|
||||||
tail = head + size;
|
tail = head + size;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* head;
|
char* head;
|
||||||
|
@ -130,7 +130,8 @@ struct graph_t
|
||||||
graph_t (const hb_vector_t<hb_serialize_context_t::object_t *>& objects)
|
graph_t (const hb_vector_t<hb_serialize_context_t::object_t *>& objects)
|
||||||
: edge_count_invalid (true),
|
: edge_count_invalid (true),
|
||||||
distance_invalid (true),
|
distance_invalid (true),
|
||||||
positions_invalid (true)
|
positions_invalid (true),
|
||||||
|
successful (true)
|
||||||
{
|
{
|
||||||
bool removed_nil = false;
|
bool removed_nil = false;
|
||||||
for (unsigned i = 0; i < objects.length; i++)
|
for (unsigned i = 0; i < objects.length; i++)
|
||||||
|
@ -160,6 +161,11 @@ struct graph_t
|
||||||
clone_buffers_.fini_deep ();
|
clone_buffers_.fini_deep ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool in_error () const
|
||||||
|
{
|
||||||
|
return !successful || vertices_.in_error () || clone_buffers_.in_error ();
|
||||||
|
}
|
||||||
|
|
||||||
const vertex_t& root () const
|
const vertex_t& root () const
|
||||||
{
|
{
|
||||||
return vertices_[root_idx ()];
|
return vertices_[root_idx ()];
|
||||||
|
@ -219,38 +225,35 @@ struct graph_t
|
||||||
hb_vector_t<unsigned> queue;
|
hb_vector_t<unsigned> queue;
|
||||||
hb_vector_t<vertex_t> sorted_graph;
|
hb_vector_t<vertex_t> sorted_graph;
|
||||||
hb_vector_t<unsigned> id_map;
|
hb_vector_t<unsigned> id_map;
|
||||||
id_map.resize (vertices_.length);
|
check_success (id_map.resize (vertices_.length));
|
||||||
|
|
||||||
hb_vector_t<unsigned> removed_edges;
|
hb_vector_t<unsigned> removed_edges;
|
||||||
removed_edges.resize (vertices_.length);
|
check_success (removed_edges.resize (vertices_.length));
|
||||||
update_incoming_edge_count ();
|
update_incoming_edge_count ();
|
||||||
|
|
||||||
queue.push (root_idx ());
|
queue.push (root_idx ());
|
||||||
int new_id = vertices_.length - 1;
|
int new_id = vertices_.length - 1;
|
||||||
|
|
||||||
while (queue.length)
|
while (!queue.in_error () && queue.length)
|
||||||
{
|
{
|
||||||
unsigned next_id = queue[0];
|
unsigned next_id = queue[0];
|
||||||
queue.remove(0);
|
queue.remove (0);
|
||||||
|
|
||||||
vertex_t& next = vertices_[next_id];
|
vertex_t& next = vertices_[next_id];
|
||||||
sorted_graph.push (next);
|
sorted_graph.push (next);
|
||||||
id_map[next_id] = new_id--;
|
id_map[next_id] = new_id--;
|
||||||
|
|
||||||
for (const auto& link : next.obj.links) {
|
for (const auto& link : next.obj.links) {
|
||||||
// TODO(garretrieger): sort children from smallest to largest
|
|
||||||
removed_edges[link.objidx]++;
|
removed_edges[link.objidx]++;
|
||||||
if (!(vertices_[link.objidx].incoming_edges - removed_edges[link.objidx]))
|
if (!(vertices_[link.objidx].incoming_edges - removed_edges[link.objidx]))
|
||||||
queue.push (link.objidx);
|
queue.push (link.objidx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_id != -1)
|
check_success (!queue.in_error ());
|
||||||
{
|
check_success (!sorted_graph.in_error ());
|
||||||
// Graph is not fully connected, there are unsorted objects.
|
if (!check_success (new_id == -1))
|
||||||
// TODO(garretrieger): handle this.
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
|
||||||
assert (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
remap_obj_indices (id_map, &sorted_graph);
|
remap_obj_indices (id_map, &sorted_graph);
|
||||||
|
|
||||||
|
@ -279,10 +282,10 @@ struct graph_t
|
||||||
hb_priority_queue_t queue;
|
hb_priority_queue_t queue;
|
||||||
hb_vector_t<vertex_t> sorted_graph;
|
hb_vector_t<vertex_t> sorted_graph;
|
||||||
hb_vector_t<unsigned> id_map;
|
hb_vector_t<unsigned> id_map;
|
||||||
id_map.resize (vertices_.length);
|
check_success (id_map.resize (vertices_.length));
|
||||||
|
|
||||||
hb_vector_t<unsigned> removed_edges;
|
hb_vector_t<unsigned> removed_edges;
|
||||||
removed_edges.resize (vertices_.length);
|
check_success (removed_edges.resize (vertices_.length));
|
||||||
update_incoming_edge_count ();
|
update_incoming_edge_count ();
|
||||||
|
|
||||||
// Object graphs are in reverse order, the first object is at the end
|
// Object graphs are in reverse order, the first object is at the end
|
||||||
|
@ -291,7 +294,7 @@ struct graph_t
|
||||||
queue.insert (root_idx (), root ().modified_distance (0));
|
queue.insert (root_idx (), root ().modified_distance (0));
|
||||||
int new_id = root_idx ();
|
int new_id = root_idx ();
|
||||||
unsigned order = 1;
|
unsigned order = 1;
|
||||||
while (!queue.is_empty ())
|
while (!queue.in_error () && !queue.is_empty ())
|
||||||
{
|
{
|
||||||
unsigned next_id = queue.extract_minimum().first;
|
unsigned next_id = queue.extract_minimum().first;
|
||||||
|
|
||||||
|
@ -312,12 +315,10 @@ struct graph_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_id != -1)
|
check_success (!queue.in_error ());
|
||||||
{
|
check_success (!sorted_graph.in_error ());
|
||||||
// Graph is not fully connected, there are unsorted objects.
|
if (!check_success (new_id == -1))
|
||||||
// TODO(garretrieger): handle this.
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
|
||||||
assert (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
remap_obj_indices (id_map, &sorted_graph);
|
remap_obj_indices (id_map, &sorted_graph);
|
||||||
|
|
||||||
|
@ -343,7 +344,9 @@ struct graph_t
|
||||||
auto* clone = vertices_.push ();
|
auto* clone = vertices_.push ();
|
||||||
auto& child = vertices_[child_idx];
|
auto& child = vertices_[child_idx];
|
||||||
clone_buffer_t* buffer = clone_buffers_.push ();
|
clone_buffer_t* buffer = clone_buffers_.push ();
|
||||||
buffer->copy (child.obj);
|
if (!check_success (buffer->copy (child.obj))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clone->obj.head = buffer->head;
|
clone->obj.head = buffer->head;
|
||||||
clone->obj.tail = buffer->tail;
|
clone->obj.tail = buffer->tail;
|
||||||
|
@ -352,6 +355,8 @@ struct graph_t
|
||||||
for (const auto& l : child.obj.links)
|
for (const auto& l : child.obj.links)
|
||||||
clone->obj.links.push (l);
|
clone->obj.links.push (l);
|
||||||
|
|
||||||
|
check_success (!clone->obj.links.in_error ());
|
||||||
|
|
||||||
auto& parent = vertices_[parent_idx];
|
auto& parent = vertices_[parent_idx];
|
||||||
unsigned clone_idx = vertices_.length - 2;
|
unsigned clone_idx = vertices_.length - 2;
|
||||||
for (unsigned i = 0; i < parent.obj.links.length; i++)
|
for (unsigned i = 0; i < parent.obj.links.length; i++)
|
||||||
|
@ -433,8 +438,13 @@ struct graph_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void err_other_error () { this->successful = false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool check_success (bool success)
|
||||||
|
{ return this->successful && (success || (err_other_error (), false)); }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Creates a map from objid to # of incoming edges.
|
* Creates a map from objid to # of incoming edges.
|
||||||
*/
|
*/
|
||||||
|
@ -506,7 +516,7 @@ struct graph_t
|
||||||
|
|
||||||
hb_set_t visited;
|
hb_set_t visited;
|
||||||
|
|
||||||
while (!queue.is_empty ())
|
while (!queue.in_error () && !queue.is_empty ())
|
||||||
{
|
{
|
||||||
unsigned next_idx = queue.extract_minimum ().first;
|
unsigned next_idx = queue.extract_minimum ().first;
|
||||||
if (visited.has (next_idx)) continue;
|
if (visited.has (next_idx)) continue;
|
||||||
|
@ -530,8 +540,14 @@ struct graph_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO(garretrieger): Handle this. If anything is left, part of the graph is disconnected.
|
|
||||||
assert (queue.is_empty ());
|
check_success (!queue.in_error ());
|
||||||
|
if (!check_success (queue.is_empty ()))
|
||||||
|
{
|
||||||
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
distance_invalid = false;
|
distance_invalid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,6 +652,7 @@ struct graph_t
|
||||||
bool edge_count_invalid;
|
bool edge_count_invalid;
|
||||||
bool distance_invalid;
|
bool distance_invalid;
|
||||||
bool positions_invalid;
|
bool positions_invalid;
|
||||||
|
bool successful;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -657,7 +674,9 @@ hb_resolve_overflows (const hb_vector_t<hb_serialize_context_t::object_t *>& pac
|
||||||
unsigned round = 0;
|
unsigned round = 0;
|
||||||
hb_vector_t<graph_t::overflow_record_t> overflows;
|
hb_vector_t<graph_t::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.will_overflow (&overflows) && round++ < 10) {
|
while (!sorted_graph.in_error ()
|
||||||
|
&& sorted_graph.will_overflow (&overflows)
|
||||||
|
&& round++ < 10) {
|
||||||
DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Over flow resolution round %d ===", round);
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Over flow resolution round %d ===", round);
|
||||||
sorted_graph.print_overflows (overflows);
|
sorted_graph.print_overflows (overflows);
|
||||||
|
|
||||||
|
@ -682,6 +701,12 @@ hb_resolve_overflows (const hb_vector_t<hb_serialize_context_t::object_t *>& pac
|
||||||
// TODO(garretrieger): initially limiting this to leaf's but likely
|
// TODO(garretrieger): initially limiting this to leaf's but likely
|
||||||
// can be used for non-leafs as well.
|
// can be used for non-leafs as well.
|
||||||
// TODO(garretrieger): add a maximum priority, don't try to raise past this.
|
// TODO(garretrieger): add a maximum priority, don't try to raise past this.
|
||||||
|
// TODO(garretrieger): also try lowering priority of the parent. Make it
|
||||||
|
// get placed further up in the ordering, closer to it's children.
|
||||||
|
// this is probably preferable if the total size of the parent object
|
||||||
|
// is < then the total size of the children (and the parent can be moved).
|
||||||
|
// Since in that case moving the parent will cause a smaller increase in
|
||||||
|
// the length of other offsets.
|
||||||
sorted_graph.raise_childrens_priority (r.parent);
|
sorted_graph.raise_childrens_priority (r.parent);
|
||||||
priority_bumped_parents.add (r.parent);
|
priority_bumped_parents.add (r.parent);
|
||||||
resolution_attempted = true;
|
resolution_attempted = true;
|
||||||
|
@ -700,9 +725,15 @@ hb_resolve_overflows (const hb_vector_t<hb_serialize_context_t::object_t *>& pac
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_MSG (SUBSET_REPACK, nullptr, "No resolution available :(");
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "No resolution available :(");
|
||||||
break;
|
c->err_offset_overflow ();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sorted_graph.in_error ())
|
||||||
|
{
|
||||||
|
c->err_other_error ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
sorted_graph.serialize (c);
|
sorted_graph.serialize (c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue