Changeset - r23569:b43bf680313b
[Not reviewed]
master
0 1 0
PeterN - 5 years ago 2019-03-30 22:19:50
peter@fuzzle.org
Fix aa7ca7fe6: Linkgraph node index order must be maintained due to other references. (#7431)

Linkgraph nodes require a specific order that was maintained by swapping just the last
element for the node to be removed. std::vector::erase() changed this to removing the
node is then shuffling the remain items down, which upsets other references to this
indices.

This is fixed by switching back to the original swap & pop method.
1 file changed with 4 insertions and 1 deletions:
0 comments (0 inline, 0 general)
src/linkgraph/linkgraph.cpp
Show inline comments
 
@@ -94,97 +94,100 @@ void LinkGraph::Merge(LinkGraph *other)
 
		NodeID new_node = this->AddNode(st);
 
		this->nodes[new_node].supply = LinkGraph::Scale(other->nodes[node1].supply, age, other_age);
 
		st->goods[this->cargo].link_graph = this->index;
 
		st->goods[this->cargo].node = new_node;
 
		for (NodeID node2 = 0; node2 < node1; ++node2) {
 
			BaseEdge &forward = this->edges[new_node][first + node2];
 
			BaseEdge &backward = this->edges[first + node2][new_node];
 
			forward = other->edges[node1][node2];
 
			backward = other->edges[node2][node1];
 
			forward.capacity = LinkGraph::Scale(forward.capacity, age, other_age);
 
			forward.usage = LinkGraph::Scale(forward.usage, age, other_age);
 
			if (forward.next_edge != INVALID_NODE) forward.next_edge += first;
 
			backward.capacity = LinkGraph::Scale(backward.capacity, age, other_age);
 
			backward.usage = LinkGraph::Scale(backward.usage, age, other_age);
 
			if (backward.next_edge != INVALID_NODE) backward.next_edge += first;
 
		}
 
		BaseEdge &new_start = this->edges[new_node][new_node];
 
		new_start = other->edges[node1][node1];
 
		if (new_start.next_edge != INVALID_NODE) new_start.next_edge += first;
 
	}
 
	delete other;
 
}
 

	
 
/**
 
 * Remove a node from the link graph by overwriting it with the last node.
 
 * @param id ID of the node to be removed.
 
 */
 
void LinkGraph::RemoveNode(NodeID id)
 
{
 
	assert(id < this->Size());
 

	
 
	NodeID last_node = this->Size() - 1;
 
	for (NodeID i = 0; i <= last_node; ++i) {
 
		(*this)[i].RemoveEdge(id);
 
		BaseEdge *node_edges = this->edges[i];
 
		NodeID prev = i;
 
		NodeID next = node_edges[i].next_edge;
 
		while (next != INVALID_NODE) {
 
			if (next == last_node) {
 
				node_edges[prev].next_edge = id;
 
				break;
 
			}
 
			prev = next;
 
			next = node_edges[prev].next_edge;
 
		}
 
		node_edges[id] = node_edges[last_node];
 
	}
 
	Station::Get(this->nodes[last_node].station)->goods[this->cargo].node = id;
 
	this->nodes.erase(this->nodes.begin() + id);
 
	/* Erase node by swapping with the last element. Node index is referenced
 
	 * directly from station goods entries so the order and position must remain. */
 
	this->nodes[id] = this->nodes.back();
 
	this->nodes.pop_back();
 
	this->edges.EraseColumn(id);
 
	/* Not doing EraseRow here, as having the extra invalid row doesn't hurt
 
	 * and removing it would trigger a lot of memmove. The data has already
 
	 * been copied around in the loop above. */
 
}
 

	
 
/**
 
 * Add a node to the component and create empty edges associated with it. Set
 
 * the station's last_component to this component. Calculate the distances to all
 
 * other nodes. The distances to _all_ nodes are important as the demand
 
 * calculator relies on their availability.
 
 * @param st New node's station.
 
 * @return New node's ID.
 
 */
 
NodeID LinkGraph::AddNode(const Station *st)
 
{
 
	const GoodsEntry &good = st->goods[this->cargo];
 

	
 
	NodeID new_node = this->Size();
 
	this->nodes.emplace_back();
 
	/* Avoid reducing the height of the matrix as that is expensive and we
 
	 * most likely will increase it again later which is again expensive. */
 
	this->edges.Resize(new_node + 1U,
 
			max(new_node + 1U, this->edges.Height()));
 

	
 
	this->nodes[new_node].Init(st->xy, st->index,
 
			HasBit(good.status, GoodsEntry::GES_ACCEPTANCE));
 

	
 
	BaseEdge *new_edges = this->edges[new_node];
 

	
 
	/* Reset the first edge starting at the new node */
 
	new_edges[new_node].next_edge = INVALID_NODE;
 

	
 
	for (NodeID i = 0; i <= new_node; ++i) {
 
		new_edges[i].Init();
 
		this->edges[i][new_node].Init();
 
	}
 
	return new_node;
 
}
 

	
 
/**
 
 * Fill an edge with values from a link. Set the restricted or unrestricted
 
 * update timestamp according to the given update mode.
 
 * @param to Destination node of the link.
 
 * @param capacity Capacity of the link.
 
 * @param usage Usage to be added.
 
 * @param mode Update mode to be used.
 
 */
0 comments (0 inline, 0 general)