1
0
Fork 0

USe smart-pointer in Priority Queue, it seems the usages have been correct everywhere (so no leaks) but memory responibility was hard to understand, should now be much simpler. (#8768)

This commit is contained in:
Michael Hackstein 2019-04-16 12:06:42 +02:00 committed by GitHub
parent 7fd456a83b
commit b3205dada8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 425 additions and 420 deletions

View File

@ -52,13 +52,14 @@ AttributeWeightShortestPathFinder::Searcher::Searcher(
_start(start), _start(start),
_isBackward(isBackward) {} _isBackward(isBackward) {}
void AttributeWeightShortestPathFinder::Searcher::insertNeighbor(Step* step, double newWeight) { void AttributeWeightShortestPathFinder::Searcher::insertNeighbor(std::unique_ptr<Step>&& step,
double newWeight) {
Step* s = _myInfo._pq.find(step->_vertex); Step* s = _myInfo._pq.find(step->_vertex);
// Not found, so insert it: // Not found, so insert it:
if (s == nullptr) { if (s == nullptr) {
step->setWeight(newWeight); step->setWeight(newWeight);
_myInfo._pq.insert(step->_vertex, step); _myInfo._pq.insert(step->_vertex, std::move(step));
return; return;
} }
if (!s->_done && s->weight() > newWeight) { if (!s->_done && s->weight() > newWeight) {
@ -66,7 +67,6 @@ void AttributeWeightShortestPathFinder::Searcher::insertNeighbor(Step* step, dou
std::swap(s->_edge, step->_edge); std::swap(s->_edge, step->_edge);
_myInfo._pq.lowerWeight(s->_vertex, newWeight); _myInfo._pq.lowerWeight(s->_vertex, newWeight);
} }
delete step;
} }
void AttributeWeightShortestPathFinder::Searcher::lookupPeer(arangodb::velocypack::StringRef& vertex, void AttributeWeightShortestPathFinder::Searcher::lookupPeer(arangodb::velocypack::StringRef& vertex,
@ -119,8 +119,9 @@ void AttributeWeightShortestPathFinder::Searcher::lookupPeer(arangodb::velocypac
bool AttributeWeightShortestPathFinder::Searcher::oneStep() { bool AttributeWeightShortestPathFinder::Searcher::oneStep() {
arangodb::velocypack::StringRef v; arangodb::velocypack::StringRef v;
// Do not steal responsibility.
Step* s = nullptr; Step* s = nullptr;
bool b = _myInfo._pq.popMinimal(v, s, true); bool b = _myInfo._pq.popMinimal(v, s);
if (_pathFinder->_bingo || !b) { if (_pathFinder->_bingo || !b) {
// We can leave this functino only under 2 conditions: // We can leave this functino only under 2 conditions:
@ -134,11 +135,13 @@ bool AttributeWeightShortestPathFinder::Searcher::oneStep() {
TRI_ASSERT(s != nullptr); TRI_ASSERT(s != nullptr);
std::vector<Step*> neighbors; std::vector<std::unique_ptr<Step>> neighbors;
_pathFinder->expandVertex(_isBackward, v, neighbors); _pathFinder->expandVertex(_isBackward, v, neighbors);
for (Step* neighbor : neighbors) { for (std::unique_ptr<Step>& neighbor : neighbors) {
insertNeighbor(neighbor, s->weight() + neighbor->weight()); insertNeighbor(std::move(neighbor), s->weight() + neighbor->weight());
} }
// All neighbours are moved out.
neighbors.clear();
lookupPeer(v, s->weight()); lookupPeer(v, s->weight());
Step* s2 = _myInfo._pq.find(v); Step* s2 = _myInfo._pq.find(v);
@ -170,17 +173,21 @@ bool AttributeWeightShortestPathFinder::shortestPath(arangodb::velocypack::Slice
_bingo = false; _bingo = false;
_intermediateSet = false; _intermediateSet = false;
arangodb::velocypack::StringRef start = _options.cache()->persistString(arangodb::velocypack::StringRef(st)); arangodb::velocypack::StringRef start =
arangodb::velocypack::StringRef target = _options.cache()->persistString(arangodb::velocypack::StringRef(ta)); _options.cache()->persistString(arangodb::velocypack::StringRef(st));
arangodb::velocypack::StringRef target =
_options.cache()->persistString(arangodb::velocypack::StringRef(ta));
// Forward with initialization: // Forward with initialization:
arangodb::velocypack::StringRef emptyVertex; arangodb::velocypack::StringRef emptyVertex;
ThreadInfo forward; ThreadInfo forward;
forward._pq.insert(start, new Step(start, emptyVertex, 0, EdgeDocumentToken())); forward._pq.insert(start, std::make_unique<Step>(start, emptyVertex, 0,
EdgeDocumentToken()));
// backward with initialization: // backward with initialization:
ThreadInfo backward; ThreadInfo backward;
backward._pq.insert(target, new Step(target, emptyVertex, 0, EdgeDocumentToken())); backward._pq.insert(target, std::make_unique<Step>(target, emptyVertex, 0,
EdgeDocumentToken()));
// Now the searcher threads: // Now the searcher threads:
Searcher forwardSearcher(this, forward, backward, start, false); Searcher forwardSearcher(this, forward, backward, start, false);
@ -266,17 +273,17 @@ bool AttributeWeightShortestPathFinder::shortestPath(arangodb::velocypack::Slice
void AttributeWeightShortestPathFinder::inserter( void AttributeWeightShortestPathFinder::inserter(
std::unordered_map<arangodb::velocypack::StringRef, size_t>& candidates, std::unordered_map<arangodb::velocypack::StringRef, size_t>& candidates,
std::vector<Step*>& result, arangodb::velocypack::StringRef const& s, arangodb::velocypack::StringRef const& t, std::vector<std::unique_ptr<Step>>& result,
arangodb::velocypack::StringRef const& s, arangodb::velocypack::StringRef const& t,
double currentWeight, EdgeDocumentToken&& edge) { double currentWeight, EdgeDocumentToken&& edge) {
auto cand = candidates.find(t); auto cand = candidates.find(t);
if (cand == candidates.end()) { if (cand == candidates.end()) {
// Add weight // Add weight
auto step = std::make_unique<Step>(t, s, currentWeight, std::move(edge)); result.emplace_back(std::make_unique<Step>(t, s, currentWeight, std::move(edge)));
result.emplace_back(step.release());
candidates.emplace(t, result.size() - 1); candidates.emplace(t, result.size() - 1);
} else { } else {
// Compare weight // Compare weight
auto old = result[cand->second]; auto& old = result[cand->second];
auto oldWeight = old->weight(); auto oldWeight = old->weight();
if (currentWeight < oldWeight) { if (currentWeight < oldWeight) {
old->setWeight(currentWeight); old->setWeight(currentWeight);
@ -286,9 +293,9 @@ void AttributeWeightShortestPathFinder::inserter(
} }
} }
void AttributeWeightShortestPathFinder::expandVertex(bool isBackward, void AttributeWeightShortestPathFinder::expandVertex(
arangodb::velocypack::StringRef const& vertex, bool isBackward, arangodb::velocypack::StringRef const& vertex,
std::vector<Step*>& result) { std::vector<std::unique_ptr<Step>>& result) {
std::unique_ptr<EdgeCursor> edgeCursor; std::unique_ptr<EdgeCursor> edgeCursor;
if (isBackward) { if (isBackward) {
edgeCursor.reset(_options.nextReverseCursor(vertex)); edgeCursor.reset(_options.nextReverseCursor(vertex));
@ -301,14 +308,16 @@ void AttributeWeightShortestPathFinder::expandVertex(bool isBackward,
if (edge.isString()) { if (edge.isString()) {
VPackSlice doc = _options.cache()->lookupToken(eid); VPackSlice doc = _options.cache()->lookupToken(eid);
double currentWeight = _options.weightEdge(doc); double currentWeight = _options.weightEdge(doc);
arangodb::velocypack::StringRef other = _options.cache()->persistString(arangodb::velocypack::StringRef(edge)); arangodb::velocypack::StringRef other =
_options.cache()->persistString(arangodb::velocypack::StringRef(edge));
if (other.compare(vertex) != 0) { if (other.compare(vertex) != 0) {
inserter(candidates, result, vertex, other, currentWeight, std::move(eid)); inserter(candidates, result, vertex, other, currentWeight, std::move(eid));
} else { } else {
inserter(candidates, result, other, vertex, currentWeight, std::move(eid)); inserter(candidates, result, other, vertex, currentWeight, std::move(eid));
} }
} else { } else {
arangodb::velocypack::StringRef fromTmp(transaction::helpers::extractFromFromDocument(edge)); arangodb::velocypack::StringRef fromTmp(
transaction::helpers::extractFromFromDocument(edge));
arangodb::velocypack::StringRef toTmp(transaction::helpers::extractToFromDocument(edge)); arangodb::velocypack::StringRef toTmp(transaction::helpers::extractToFromDocument(edge));
arangodb::velocypack::StringRef from = _options.cache()->persistString(fromTmp); arangodb::velocypack::StringRef from = _options.cache()->persistString(fromTmp);
arangodb::velocypack::StringRef to = _options.cache()->persistString(toTmp); arangodb::velocypack::StringRef to = _options.cache()->persistString(toTmp);

View File

@ -58,8 +58,9 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
arangodb::graph::EdgeDocumentToken _edge; arangodb::graph::EdgeDocumentToken _edge;
bool _done; bool _done;
Step(arangodb::velocypack::StringRef const& vert, arangodb::velocypack::StringRef const& pred, Step(arangodb::velocypack::StringRef const& vert,
double weig, EdgeDocumentToken&& edge); arangodb::velocypack::StringRef const& pred, double weig,
EdgeDocumentToken&& edge);
double weight() const { return _weight; } double weight() const { return _weight; }
@ -152,7 +153,8 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
class Searcher { class Searcher {
public: public:
Searcher(AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo, Searcher(AttributeWeightShortestPathFinder* pathFinder, ThreadInfo& myInfo,
ThreadInfo& peerInfo, arangodb::velocypack::StringRef const& start, bool isBackward); ThreadInfo& peerInfo, arangodb::velocypack::StringRef const& start,
bool isBackward);
public: public:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -166,7 +168,7 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
/// @brief Insert a neighbor to the todo list. /// @brief Insert a neighbor to the todo list.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void insertNeighbor(Step* step, double newWeight); void insertNeighbor(std::unique_ptr<Step>&& step, double newWeight);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Lookup our current vertex in the data of our peer. /// @brief Lookup our current vertex in the data of our peer.
@ -211,12 +213,13 @@ class AttributeWeightShortestPathFinder : public ShortestPathFinder {
arangodb::graph::ShortestPathResult& result) override; arangodb::graph::ShortestPathResult& result) override;
void inserter(std::unordered_map<arangodb::velocypack::StringRef, size_t>& candidates, void inserter(std::unordered_map<arangodb::velocypack::StringRef, size_t>& candidates,
std::vector<Step*>& result, arangodb::velocypack::StringRef const& s, std::vector<std::unique_ptr<Step>>& result,
arangodb::velocypack::StringRef const& s,
arangodb::velocypack::StringRef const& t, double currentWeight, arangodb::velocypack::StringRef const& t, double currentWeight,
graph::EdgeDocumentToken&& edge); graph::EdgeDocumentToken&& edge);
void expandVertex(bool isBackward, arangodb::velocypack::StringRef const& source, void expandVertex(bool isBackward, arangodb::velocypack::StringRef const& source,
std::vector<Step*>& result); std::vector<std::unique_ptr<Step>>& result);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief return the shortest path between the start and target vertex, /// @brief return the shortest path between the start and target vertex,

View File

@ -64,10 +64,10 @@ bool KShortestPathsFinder::startKShortestPathsTraversal(
return true; return true;
} }
bool KShortestPathsFinder::computeShortestPath( bool KShortestPathsFinder::computeShortestPath(VertexRef const& start, VertexRef const& end,
VertexRef const& start, VertexRef const& end, std::unordered_set<VertexRef> const& forbiddenVertices,
std::unordered_set<VertexRef> const& forbiddenVertices, std::unordered_set<Edge> const& forbiddenEdges,
std::unordered_set<Edge> const& forbiddenEdges, Path& result) { Path& result) {
bool found = false; bool found = false;
Ball left(start, FORWARD); Ball left(start, FORWARD);
Ball right(end, BACKWARD); Ball right(end, BACKWARD);
@ -91,8 +91,8 @@ bool KShortestPathsFinder::computeShortestPath(
return found; return found;
} }
void KShortestPathsFinder::computeNeighbourhoodOfVertex( void KShortestPathsFinder::computeNeighbourhoodOfVertex(VertexRef vertex, Direction direction,
VertexRef vertex, Direction direction, std::vector<Step>& steps) { std::vector<Step>& steps) {
std::unique_ptr<EdgeCursor> edgeCursor; std::unique_ptr<EdgeCursor> edgeCursor;
switch (direction) { switch (direction) {
@ -150,13 +150,14 @@ void KShortestPathsFinder::computeNeighbourhoodOfVertex(
} }
} }
bool KShortestPathsFinder::advanceFrontier( bool KShortestPathsFinder::advanceFrontier(Ball& source, Ball const& target,
Ball& source, Ball const& target, std::unordered_set<VertexRef> const& forbiddenVertices, std::unordered_set<VertexRef> const& forbiddenVertices,
std::unordered_set<Edge> const& forbiddenEdges, VertexRef& join) { std::unordered_set<Edge> const& forbiddenEdges,
VertexRef& join) {
VertexRef vr; VertexRef vr;
FoundVertex* v; FoundVertex* v;
bool success = source._frontier.popMinimal(vr, v, true); bool success = source._frontier.popMinimal(vr, v);
if (!success) { if (!success) {
return false; return false;
} }
@ -178,7 +179,8 @@ bool KShortestPathsFinder::advanceFrontier(
} }
} else { } else {
source._frontier.insert(s._vertex, source._frontier.insert(s._vertex,
new FoundVertex(s._vertex, vr, std::move(s._edge), weight)); std::make_unique<FoundVertex>(s._vertex, vr,
std::move(s._edge), weight));
auto found = target._frontier.find(s._vertex); auto found = target._frontier.find(s._vertex);
if (found != nullptr) { if (found != nullptr) {
@ -192,8 +194,7 @@ bool KShortestPathsFinder::advanceFrontier(
} }
void KShortestPathsFinder::reconstructPath(Ball const& left, Ball const& right, void KShortestPathsFinder::reconstructPath(Ball const& left, Ball const& right,
VertexRef const& join, VertexRef const& join, Path& result) {
Path& result) {
result.clear(); result.clear();
TRI_ASSERT(!join.empty()); TRI_ASSERT(!join.empty());
result._vertices.emplace_back(join); result._vertices.emplace_back(join);
@ -276,13 +277,15 @@ bool KShortestPathsFinder::computeNextShortestPath(Path& result) {
// indeed one that removes duplicates automatically // indeed one that removes duplicates automatically
// Sorted in reverse to have pop_back // Sorted in reverse to have pop_back
if (_options.useWeight()) { if (_options.useWeight()) {
std::sort(_candidatePaths.begin(), _candidatePaths.end(), [](Path const& p1, Path const& p2) { std::sort(_candidatePaths.begin(), _candidatePaths.end(),
return p1._weight > p2._weight; [](Path const& p1, Path const& p2) {
}); return p1._weight > p2._weight;
});
} else { } else {
std::sort(_candidatePaths.begin(), _candidatePaths.end(), [](Path const& p1, Path const& p2) { std::sort(_candidatePaths.begin(), _candidatePaths.end(),
return p1._vertices.size() > p2._vertices.size(); [](Path const& p1, Path const& p2) {
}); return p1._vertices.size() > p2._vertices.size();
});
} }
// FIXME: this is of course bad. // FIXME: this is of course bad.

View File

@ -126,7 +126,8 @@ class KShortestPathsFinder : public ShortestPathFinder {
double _weight; double _weight;
// Using negative weight to signifiy start/end vertex // Using negative weight to signifiy start/end vertex
explicit FoundVertex(VertexRef const& vertex) : _vertex(vertex), _weight(0) {} explicit FoundVertex(VertexRef const& vertex)
: _vertex(vertex), _weight(0) {}
FoundVertex(VertexRef const& vertex, VertexRef const& pred, Edge&& edge, double weight) FoundVertex(VertexRef const& vertex, VertexRef const& pred, Edge&& edge, double weight)
: _vertex(vertex), _pred(pred), _edge(std::move(edge)), _weight(weight) {} : _vertex(vertex), _pred(pred), _edge(std::move(edge)), _weight(weight) {}
double weight() const { return _weight; } double weight() const { return _weight; }
@ -150,13 +151,8 @@ class KShortestPathsFinder : public ShortestPathFinder {
Ball(void) {} Ball(void) {}
Ball(VertexRef const& centre, Direction direction) Ball(VertexRef const& centre, Direction direction)
: _centre(centre), _direction(direction) { : _centre(centre), _direction(direction) {
auto v = new FoundVertex(centre); auto v = std::make_unique<FoundVertex>(centre);
try { _frontier.insert(centre, std::move(v));
_frontier.insert(centre, v);
} catch (...) {
delete v;
throw;
}
} }
~Ball() { ~Ball() {
// TODO free all vertices // TODO free all vertices

View File

@ -66,12 +66,8 @@ class ShortestPathPriorityQueue {
ShortestPathPriorityQueue() : _popped(0), _isHeap(false), _maxWeight(0) {} ShortestPathPriorityQueue() : _popped(0), _isHeap(false), _maxWeight(0) {}
~ShortestPathPriorityQueue() { ~ShortestPathPriorityQueue() {
for (Value* v : _heap) { _heap.clear();
delete v; _history.clear();
}
for (Value* v : _history) {
delete v;
}
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -91,7 +87,7 @@ class ShortestPathPriorityQueue {
/// yet exist, and false, in which case nothing else is changed. /// yet exist, and false, in which case nothing else is changed.
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
bool insert(Key const& k, Value* v) { bool insert(Key const& k, std::unique_ptr<Value>&& v) {
auto it = _lookup.find(k); auto it = _lookup.find(k);
if (it != _lookup.end()) { if (it != _lookup.end()) {
return false; return false;
@ -108,7 +104,7 @@ class ShortestPathPriorityQueue {
if (w > _maxWeight) { if (w > _maxWeight) {
_maxWeight = w; _maxWeight = w;
} }
_heap.push_back(v); _heap.push_back(std::move(v));
try { try {
_lookup.insert(std::make_pair(k, static_cast<ssize_t>(_heap.size() - 1 + _popped))); _lookup.insert(std::make_pair(k, static_cast<ssize_t>(_heap.size() - 1 + _popped)));
} catch (...) { } catch (...) {
@ -119,7 +115,7 @@ class ShortestPathPriorityQueue {
} }
} }
// If we get here, we have to insert into a proper binary heap: // If we get here, we have to insert into a proper binary heap:
_heap.push_back(v); _heap.push_back(std::move(v));
try { try {
size_t newpos = _heap.size() - 1; size_t newpos = _heap.size() - 1;
_lookup.insert(std::make_pair(k, static_cast<ssize_t>(newpos + _popped))); _lookup.insert(std::make_pair(k, static_cast<ssize_t>(newpos + _popped)));
@ -146,9 +142,9 @@ class ShortestPathPriorityQueue {
} }
if (it->second >= 0) { // still in the queue if (it->second >= 0) { // still in the queue
return _heap.at(static_cast<size_t>(it->second) - _popped); return _heap.at(static_cast<size_t>(it->second) - _popped).get();
} else { // already in the history } else { // already in the history
return _history.at(static_cast<size_t>(-it->second) - 1); return _history.at(static_cast<size_t>(-it->second) - 1).get();
} }
} }
@ -186,36 +182,73 @@ class ShortestPathPriorityQueue {
if (_heap.empty()) { if (_heap.empty()) {
return nullptr; return nullptr;
} }
return _heap[0]; return _heap[0].get();
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief popMinimal, returns true if something was returned and false /// @brief popMinimal, returns true if something was returned and false
/// if the structure is empty. Key and Value are stored in k and v. /// if the structure is empty. Key and Value are stored in k and v.
/// If keepForLookup is true then the Value is kept for lookup in the /// This will keep the unique_ptr inside the history for further lookup.
/// hash table but removed from the priority queue. /// In case you doe not want to lookup the value, you need to call stealMinimal
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
bool popMinimal(Key& k, Value*& v, bool keepForLookup = false) { bool popMinimal(Key& k, Value*& v) {
if (_heap.empty()) {
v = nullptr;
return false;
}
k = _heap[0]->getKey();
// Note: the pointer of _heap[0] stays valid.
// The unique-responsiblity is handed over to history.
v = _heap[0].get();
auto it = _lookup.find(k);
TRI_ASSERT(it != _lookup.end());
// move val into history and hand over responsibility
_history.push_back(std::move(_heap[0]));
it->second = -static_cast<ssize_t>(_history.size());
if (!_isHeap) {
// remove nullptr from heap
_heap.pop_front();
// Note: This is intentionally one too large to shift by 1
_popped++;
} else {
repairHeapAfterPop();
}
return true;
}
//////////////////////////////////////////////////////////////////////////////
/// @brief stealMinimal, returns true if something was returned and false
/// if the structure is empty. Key and Value are stored in k and v.
/// This will hand over responsiblity to the caller, and will remove
/// the value from further lookups. If you need it within the history here
/// you need to call popMinimal.
//////////////////////////////////////////////////////////////////////////////
bool stealMinimal(Key& k, std::unique_ptr<Value>& v) {
if (_heap.empty()) { if (_heap.empty()) {
return false; return false;
} }
k = _heap[0]->getKey(); k = _heap[0]->getKey();
v = _heap[0];
auto it = _lookup.find(k);
TRI_ASSERT(it != _lookup.end());
_lookup.erase(it);
// Responsibility handed over to v
// Note: _heap[0] is nullptr now.
// If we crash now this is save.
// The code below is not allowed to use _heap[0]
// anymore
v = std::make_unique<Value>(_heap[0].release());
TRI_ASSERT(v != nullptr);
if (!_isHeap) { if (!_isHeap) {
auto it = _lookup.find(k); // Remove it from heap asap.
TRI_ASSERT(it != _lookup.end());
if (keepForLookup) {
_history.push_back(_heap[0]);
it->second = -static_cast<ssize_t>(_history.size());
// Note: This is intentionally one too large to shift by 1
} else {
_lookup.erase(it);
}
_heap.pop_front(); _heap.pop_front();
_popped++; _popped++;
} else { } else {
removeFromHeap(keepForLookup); repairHeapAfterPop();
} }
return true; return true;
} }
@ -226,9 +259,7 @@ class ShortestPathPriorityQueue {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
void swap(size_t p, size_t q) { void swap(size_t p, size_t q) {
Value* v = _heap[p]; _heap[p].swap(_heap[q]);
_heap[p] = _heap[q];
_heap[q] = v;
// Now fix the lookup: // Now fix the lookup:
Key const& keyp(_heap[p]->getKey()); Key const& keyp(_heap[p]->getKey());
@ -319,19 +350,10 @@ class ShortestPathPriorityQueue {
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief removeFromHeap, remove first position in the heap /// @brief repairHeapAfterPop, remove first position in the heap and repair it
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
void removeFromHeap(bool keepForLookup) { void repairHeapAfterPop() {
auto it = _lookup.find(_heap[0]->getKey());
TRI_ASSERT(it != _lookup.end());
if (keepForLookup) {
_history.push_back(_heap[0]);
it->second = -static_cast<ssize_t>(_history.size());
// Note: This is intentionally one too large to shift by 1
} else {
_lookup.erase(it);
}
if (_heap.size() == 1) { if (_heap.size() == 1) {
_heap.clear(); _heap.clear();
_popped = 0; _popped = 0;
@ -340,9 +362,11 @@ class ShortestPathPriorityQueue {
return; return;
} }
// Move one in front: // Move one in front:
_heap[0] = _heap.back(); _heap[0].swap(_heap.back());
// Throw away the POINTER (might be moved before).
_heap.pop_back(); _heap.pop_back();
it = _lookup.find(_heap[0]->getKey()); // Find the lokkup of new value
auto it = _lookup.find(_heap[0]->getKey());
TRI_ASSERT(it != _lookup.end()); TRI_ASSERT(it != _lookup.end());
it->second = static_cast<ssize_t>(_popped); it->second = static_cast<ssize_t>(_popped);
repairDown(); repairDown();
@ -376,7 +400,7 @@ class ShortestPathPriorityQueue {
/// @brief _heap, the actual data /// @brief _heap, the actual data
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
std::deque<Value*> _heap; std::deque<std::unique_ptr<Value>> _heap;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief _maxWeight, the current maximal weight ever seen /// @brief _maxWeight, the current maximal weight ever seen
@ -388,7 +412,7 @@ class ShortestPathPriorityQueue {
/// @brief _history, the actual data that is only in the key/value store /// @brief _history, the actual data that is only in the key/value store
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
std::vector<Value*> _history; std::vector<std::unique_ptr<Value>> _history;
}; };
} // namespace graph } // namespace graph

View File

@ -43,395 +43,365 @@ using namespace std;
struct MyValue { struct MyValue {
std::string _key; std::string _key;
unsigned int _weight; unsigned int _weight;
unsigned int weight() const { unsigned int weight() const { return _weight; }
return _weight; std::string const& getKey() const { return _key; }
} void setWeight(unsigned int const w) { _weight = w; }
std::string const& getKey() const { MyValue(std::string k, unsigned int w) : _key(k), _weight(w) {}
return _key;
}
void setWeight(unsigned int const w) {
_weight = w;
}
MyValue (std::string k, unsigned int w) : _key(k), _weight(w) {
}
}; };
TEST_CASE("CPriorityQueueTest", "[cpriorityqueue]") { TEST_CASE("CPriorityQueueTest", "[cpriorityqueue]") {
////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in ascending weight order
////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// SECTION("tst_deque_case") {
/// @brief test filling in ascending weight order arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_deque_case") { CHECK(0 == (int)pq.size());
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq; CHECK(true == pq.empty());
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b; bool b;
MyValue* v; MyValue* v = nullptr;
b = pq.insert("a", new MyValue("a", 1)); b = pq.insert("a", std::make_unique<MyValue>("a", 1));
CHECK(b == true); CHECK(b == true);
b = pq.insert("b", new MyValue("b", 2)); b = pq.insert("b", std::make_unique<MyValue>("b", 2));
CHECK(b == true); CHECK(b == true);
b = pq.insert("c", new MyValue("c", 2)); b = pq.insert("c", std::make_unique<MyValue>("c", 2));
CHECK(b == true); CHECK(b == true);
b = pq.insert("d", new MyValue("d", 4)); b = pq.insert("d", std::make_unique<MyValue>("d", 4));
CHECK(b == true); CHECK(b == true);
v = new MyValue("c", 5); b = pq.insert("c", std::make_unique<MyValue>("c", 5));
b = pq.insert("c", v); CHECK(b == false);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size()); CHECK(4 == (int)pq.size());
CHECK(false == pq.empty()); CHECK(false == pq.empty());
MyValue const* p; MyValue const* p;
p = pq.find("a"); p = pq.find("a");
CHECK((int) p->_weight == 1); CHECK((int)p->_weight == 1);
p = pq.find("b"); p = pq.find("b");
CHECK((int) p->_weight == 2); CHECK((int)p->_weight == 2);
p = pq.find("c"); p = pq.find("c");
CHECK((int) p->_weight == 2); CHECK((int)p->_weight == 2);
p = pq.find("d"); p = pq.find("d");
CHECK((int) p->_weight == 4); CHECK((int)p->_weight == 4);
p = pq.find("abc"); p = pq.find("abc");
CHECK(p == nullptr); CHECK(p == nullptr);
std::string k; std::string k;
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "a"); CHECK(p->_key == "a");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "a"); CHECK(k == "a");
CHECK(v->_key == "a"); REQUIRE(v != nullptr);
CHECK((int) v->_weight == 1); CHECK(v->_key == "a");
delete v; CHECK((int)v->_weight == 1);
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "b"); CHECK(p->_key == "b");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "b"); CHECK(k == "b");
CHECK(v->_key == "b"); CHECK(v->_key == "b");
CHECK((int) v->_weight == 2); CHECK((int)v->_weight == 2);
delete v;
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "c"); CHECK(p->_key == "c");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "c"); CHECK(k == "c");
CHECK(v->_key == "c"); CHECK(v->_key == "c");
CHECK((int) v->_weight == 2); CHECK((int)v->_weight == 2);
delete v;
CHECK((int) pq.size() == 1); CHECK((int)pq.size() == 1);
CHECK(pq.empty() == false); CHECK(pq.empty() == false);
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "d"); CHECK(p->_key == "d");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "d"); CHECK(k == "d");
CHECK(v->_key == "d"); CHECK(v->_key == "d");
CHECK((int) v->_weight == 4); CHECK((int)v->_weight == 4);
delete v;
CHECK((int) pq.size() == 0); CHECK((int)pq.size() == 0);
CHECK(pq.empty() == true); CHECK(pq.empty() == true);
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p == nullptr); CHECK(p == nullptr);
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == false); CHECK(b == false);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief test filling in random weight order /// @brief test filling in random weight order
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SECTION("tst_heap_case") { SECTION("tst_heap_case") {
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq; arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b; CHECK(0 == (int)pq.size());
MyValue* v; CHECK(true == pq.empty());
b = pq.insert("a", new MyValue("a", 4)); bool b;
CHECK(b == true); MyValue* v = nullptr;
b = pq.insert("b", new MyValue("b", 1));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 2));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 2));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size()); b = pq.insert("a", std::make_unique<MyValue>("a", 4));
CHECK(false == pq.empty()); CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 1));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 2));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
MyValue const* p; CHECK(4 == (int)pq.size());
CHECK(false == pq.empty());
p = pq.find("a"); MyValue const* p;
CHECK((int) p->_weight == 4);
p = pq.find("b");
CHECK((int) p->_weight == 1);
p = pq.find("c");
CHECK((int) p->_weight == 2);
p = pq.find("d");
CHECK((int) p->_weight == 2);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k; p = pq.find("a");
CHECK((int)p->_weight == 4);
p = pq.find("b");
CHECK((int)p->_weight == 1);
p = pq.find("c");
CHECK((int)p->_weight == 2);
p = pq.find("d");
CHECK((int)p->_weight == 2);
p = pq.find("abc");
CHECK(p == nullptr);
p = pq.getMinimal(); std::string k;
CHECK(p->_key == "b");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "d"); CHECK(p->_key == "b");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "d"); CHECK(k == "b");
CHECK(v->_key == "d"); CHECK(v->_key == "b");
CHECK((int) v->_weight == 2); CHECK((int)v->_weight == 1);
delete v;
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "c"); CHECK(p->_key == "d");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "c"); CHECK(k == "d");
CHECK(v->_key == "c"); CHECK(v->_key == "d");
CHECK((int) v->_weight == 2); CHECK((int)v->_weight == 2);
delete v;
CHECK((int) pq.size() == 1); p = pq.getMinimal();
CHECK(pq.empty() == false); CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int)v->_weight == 2);
p = pq.getMinimal(); CHECK((int)pq.size() == 1);
CHECK(p->_key == "a"); CHECK(pq.empty() == false);
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int) v->_weight == 4);
delete v;
CHECK((int) pq.size() == 0); p = pq.getMinimal();
CHECK(pq.empty() == true); CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int)v->_weight == 4);
p = pq.getMinimal(); CHECK((int)pq.size() == 0);
CHECK(p == nullptr); CHECK(pq.empty() == true);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
//////////////////////////////////////////////////////////////////////////////// p = pq.getMinimal();
/// @brief test filling in ascending weight order, but then doing lowerWeight CHECK(p == nullptr);
//////////////////////////////////////////////////////////////////////////////// b = pq.popMinimal(k, v);
CHECK(b == false);
}
SECTION("tst_deque_case_with_lowering") { ////////////////////////////////////////////////////////////////////////////////
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq; /// @brief test filling in ascending weight order, but then doing lowerWeight
////////////////////////////////////////////////////////////////////////////////
CHECK(0 == (int) pq.size());
CHECK(true == pq.empty());
bool b; SECTION("tst_deque_case_with_lowering") {
MyValue* v; arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
b = pq.insert("a", new MyValue("a", 1)); CHECK(0 == (int)pq.size());
CHECK(b == true); CHECK(true == pq.empty());
b = pq.insert("b", new MyValue("b", 2));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 2));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 4));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size()); bool b;
CHECK(false == pq.empty()); MyValue* v = nullptr;
pq.lowerWeight("d", 1); // This moves "d" before "b" and "c" b = pq.insert("a", std::make_unique<MyValue>("a", 1));
CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 2));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 4));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
MyValue const* p; CHECK(4 == (int)pq.size());
CHECK(false == pq.empty());
p = pq.find("a"); pq.lowerWeight("d", 1); // This moves "d" before "b" and "c"
CHECK((int) p->_weight == 1);
p = pq.find("b");
CHECK((int) p->_weight == 2);
p = pq.find("c");
CHECK((int) p->_weight == 2);
p = pq.find("d");
CHECK((int) p->_weight == 1);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k; MyValue const* p;
p = pq.getMinimal(); p = pq.find("a");
CHECK(p->_key == "a"); CHECK((int)p->_weight == 1);
b = pq.popMinimal(k, v); p = pq.find("b");
CHECK(b == true); CHECK((int)p->_weight == 2);
CHECK(k == "a"); p = pq.find("c");
CHECK(v->_key == "a"); CHECK((int)p->_weight == 2);
CHECK((int) v->_weight == 1); p = pq.find("d");
delete v; CHECK((int)p->_weight == 1);
p = pq.find("abc");
CHECK(p == nullptr);
p = pq.getMinimal(); std::string k;
CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "c"); CHECK(p->_key == "a");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "c"); CHECK(k == "a");
CHECK(v->_key == "c"); CHECK(v->_key == "a");
CHECK((int) v->_weight == 2); CHECK((int)v->_weight == 1);
delete v;
CHECK((int) pq.size() == 1); p = pq.getMinimal();
CHECK(pq.empty() == false); CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int)v->_weight == 1);
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "b"); CHECK(p->_key == "c");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "b"); CHECK(k == "c");
CHECK(v->_key == "b"); CHECK(v->_key == "c");
CHECK((int) v->_weight == 2); CHECK((int)v->_weight == 2);
delete v;
CHECK((int) pq.size() == 0); CHECK((int)pq.size() == 1);
CHECK(pq.empty() == true); CHECK(pq.empty() == false);
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p == nullptr); CHECK(p->_key == "b");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == false); CHECK(b == true);
} CHECK(k == "b");
CHECK(v->_key == "b");
CHECK((int)v->_weight == 2);
//////////////////////////////////////////////////////////////////////////////// CHECK((int)pq.size() == 0);
/// @brief test filling in random weight order, and later lowering some weight CHECK(pq.empty() == true);
////////////////////////////////////////////////////////////////////////////////
SECTION("tst_heap_case_with_lowering") { p = pq.getMinimal();
arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq; CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(0 == (int) pq.size()); CHECK(b == false);
CHECK(true == pq.empty()); }
bool b; ////////////////////////////////////////////////////////////////////////////////
MyValue* v; /// @brief test filling in random weight order, and later lowering some weight
////////////////////////////////////////////////////////////////////////////////
b = pq.insert("a", new MyValue("a", 4)); SECTION("tst_heap_case_with_lowering") {
CHECK(b == true); arangodb::graph::ShortestPathPriorityQueue<std::string, MyValue, unsigned int> pq;
b = pq.insert("b", new MyValue("b", 2));
CHECK(b == true);
b = pq.insert("c", new MyValue("c", 3));
CHECK(b == true);
b = pq.insert("d", new MyValue("d", 3));
CHECK(b == true);
v = new MyValue("c", 5);
b = pq.insert("c", v);
CHECK(b == false);
delete v;
CHECK(4 == (int) pq.size()); CHECK(0 == (int)pq.size());
CHECK(false == pq.empty()); CHECK(true == pq.empty());
pq.lowerWeight("a", 1); // This moves "a" before all others bool b;
MyValue* v = nullptr;
MyValue const* p; b = pq.insert("a", std::make_unique<MyValue>("a", 4));
CHECK(b == true);
b = pq.insert("b", std::make_unique<MyValue>("b", 2));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 3));
CHECK(b == true);
b = pq.insert("d", std::make_unique<MyValue>("d", 3));
CHECK(b == true);
b = pq.insert("c", std::make_unique<MyValue>("c", 5));
CHECK(b == false);
delete v;
p = pq.find("a"); CHECK(4 == (int)pq.size());
CHECK((int) p->_weight == 1); CHECK(false == pq.empty());
p = pq.find("b");
CHECK((int) p->_weight == 2);
p = pq.find("c");
CHECK((int) p->_weight == 3);
p = pq.find("d");
CHECK((int) p->_weight == 3);
p = pq.find("abc");
CHECK(p == nullptr);
std::string k; pq.lowerWeight("a", 1); // This moves "a" before all others
p = pq.getMinimal(); MyValue const* p;
CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int) v->_weight == 1);
delete v;
p = pq.getMinimal(); p = pq.find("a");
CHECK(p->_key == "b"); CHECK((int)p->_weight == 1);
b = pq.popMinimal(k, v); p = pq.find("b");
CHECK(b == true); CHECK((int)p->_weight == 2);
CHECK(k == "b"); p = pq.find("c");
CHECK(v->_key == "b"); CHECK((int)p->_weight == 3);
CHECK((int) v->_weight == 2); p = pq.find("d");
delete v; CHECK((int)p->_weight == 3);
p = pq.find("abc");
CHECK(p == nullptr);
p = pq.getMinimal(); std::string k;
CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int) v->_weight == 3);
delete v;
CHECK((int) pq.size() == 1); p = pq.getMinimal();
CHECK(pq.empty() == false); CHECK(p->_key == "a");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "a");
CHECK(v->_key == "a");
CHECK((int)v->_weight == 1);
p = pq.getMinimal(); p = pq.getMinimal();
CHECK(p->_key == "d"); CHECK(p->_key == "b");
b = pq.popMinimal(k, v); b = pq.popMinimal(k, v);
CHECK(b == true); CHECK(b == true);
CHECK(k == "d"); CHECK(k == "b");
CHECK(v->_key == "d"); CHECK(v->_key == "b");
CHECK((int) v->_weight == 3); CHECK((int)v->_weight == 2);
delete v;
CHECK((int) pq.size() == 0); p = pq.getMinimal();
CHECK(pq.empty() == true); CHECK(p->_key == "c");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "c");
CHECK(v->_key == "c");
CHECK((int)v->_weight == 3);
p = pq.getMinimal(); CHECK((int)pq.size() == 1);
CHECK(p == nullptr); CHECK(pq.empty() == false);
b = pq.popMinimal(k, v);
CHECK(b == false); p = pq.getMinimal();
} CHECK(p->_key == "d");
b = pq.popMinimal(k, v);
CHECK(b == true);
CHECK(k == "d");
CHECK(v->_key == "d");
CHECK((int)v->_weight == 3);
CHECK((int)pq.size() == 0);
CHECK(pq.empty() == true);
p = pq.getMinimal();
CHECK(p == nullptr);
b = pq.popMinimal(k, v);
CHECK(b == false);
}
} }
// Local Variables: // Local Variables:
// mode: outline-minor // mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|//
// End: // --SECTION--\\|/// @\\}\\)" End: