1
0
Fork 0
arangodb/lib/Basics/Traverser.h

202 lines
7.3 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_BASICS_TRAVERSER_H
#define ARANGODB_BASICS_TRAVERSER_H 1
#include "Basics/Common.h"
#include "Basics/Exceptions.h"
#include "Basics/Mutex.h"
#include "Basics/MutexLocker.h"
#include <deque>
#include <stack>
#include <thread>
namespace arangodb {
namespace basics {
template <typename edgeIdentifier, typename vertexIdentifier>
struct EnumeratedPath {
std::vector<edgeIdentifier> edges;
std::vector<vertexIdentifier> vertices;
EnumeratedPath() {}
};
template <typename edgeIdentifier, typename vertexIdentifier>
struct VertexGetter {
VertexGetter() = default;
virtual ~VertexGetter() = default;
virtual bool getVertex(edgeIdentifier const&, vertexIdentifier const&, size_t,
vertexIdentifier&) = 0;
};
template <typename edgeIdentifier, typename vertexIdentifier, typename edgeItem>
class PathEnumerator {
private:
//////////////////////////////////////////////////////////////////////////////
/// @brief List of the last path is used to
//////////////////////////////////////////////////////////////////////////////
EnumeratedPath<edgeIdentifier, vertexIdentifier> _enumeratedPath;
//////////////////////////////////////////////////////////////////////////////
/// @brief The pointers returned for edge indexes on this path. Used to
/// continue
/// the search on respective levels.
//////////////////////////////////////////////////////////////////////////////
std::stack<edgeItem*> _lastEdges;
//////////////////////////////////////////////////////////////////////////////
/// @brief The boolean value indicating the direction for 'any' search
//////////////////////////////////////////////////////////////////////////////
std::stack<bool> _lastEdgesDir;
//////////////////////////////////////////////////////////////////////////////
/// @brief An internal index for the edge collection used at each depth level
//////////////////////////////////////////////////////////////////////////////
std::stack<size_t> _lastEdgesIdx;
//////////////////////////////////////////////////////////////////////////////
/// @brief Function to get the next edge from index.
//////////////////////////////////////////////////////////////////////////////
std::function<void(vertexIdentifier&, std::vector<edgeIdentifier>&,
edgeItem*&, size_t&, bool&)> _getEdge;
//////////////////////////////////////////////////////////////////////////////
/// @brief Function to get the connected vertex from index.
/// Returns false if the vertex does not match the filter
//////////////////////////////////////////////////////////////////////////////
VertexGetter<edgeIdentifier, vertexIdentifier>* _vertexGetter;
//////////////////////////////////////////////////////////////////////////////
/// @brief Indicates if we issue next() the first time.
/// It shall return an empty path in this case.
//////////////////////////////////////////////////////////////////////////////
bool _isFirst;
//////////////////////////////////////////////////////////////////////////////
/// @brief Maximal path length which should be enumerated.
//////////////////////////////////////////////////////////////////////////////
size_t _maxDepth;
public:
PathEnumerator(
std::function<void(vertexIdentifier const&, std::vector<edgeIdentifier>&,
edgeItem*&, size_t&, bool&)> getEdge,
VertexGetter<edgeIdentifier, vertexIdentifier>* vertexGetter,
vertexIdentifier const& startVertex, size_t maxDepth)
: _getEdge(getEdge), _vertexGetter(vertexGetter), _isFirst(true), _maxDepth(maxDepth) {
_enumeratedPath.vertices.push_back(startVertex);
_lastEdges.push(nullptr);
_lastEdgesDir.push(false);
_lastEdgesIdx.push(0);
TRI_ASSERT(_enumeratedPath.vertices.size() == 1);
TRI_ASSERT(_lastEdges.size() == 1);
TRI_ASSERT(_lastEdgesDir.size() == 1);
}
~PathEnumerator() {}
//////////////////////////////////////////////////////////////////////////////
/// @brief Get the next Path element from the traversal.
//////////////////////////////////////////////////////////////////////////////
const EnumeratedPath<edgeIdentifier, vertexIdentifier>& next() {
if (_isFirst) {
_isFirst = false;
return _enumeratedPath;
}
if (_enumeratedPath.edges.size() == _maxDepth) {
// we have reached the maximal search depth.
// We can prune this path and go to the next.
prune();
}
// Avoid tail recusion. May crash on high search depth
while (true) {
if (_lastEdges.empty()) {
_enumeratedPath.edges.clear();
_enumeratedPath.vertices.clear();
return _enumeratedPath;
}
_getEdge(_enumeratedPath.vertices.back(), _enumeratedPath.edges,
_lastEdges.top(), _lastEdgesIdx.top(), _lastEdgesDir.top());
if (_lastEdges.top() != nullptr) {
// Could continue the path in the next depth.
_lastEdges.push(nullptr);
_lastEdgesDir.push(false);
_lastEdgesIdx.push(0);
vertexIdentifier v;
bool isValid = _vertexGetter->getVertex(
_enumeratedPath.edges.back(), _enumeratedPath.vertices.back(),
_enumeratedPath.vertices.size(), v);
_enumeratedPath.vertices.push_back(v);
TRI_ASSERT(_enumeratedPath.vertices.size() ==
_enumeratedPath.edges.size() + 1);
if (isValid) {
return _enumeratedPath;
}
} else {
if (_enumeratedPath.edges.empty()) {
// We are done with enumerating paths
_enumeratedPath.edges.clear();
_enumeratedPath.vertices.clear();
return _enumeratedPath;
}
}
// This either modifies the stack or _lastEdges is empty.
// This will return in next depth
prune();
}
}
//////////////////////////////////////////////////////////////////////////////
/// @brief Prunes the current path prefix, the next function should not return
/// any path having this prefix anymore.
//////////////////////////////////////////////////////////////////////////////
void prune() {
if (!_lastEdges.empty()) {
_lastEdges.pop();
_lastEdgesDir.pop();
_lastEdgesIdx.pop();
if (!_enumeratedPath.edges.empty()) {
_enumeratedPath.edges.pop_back();
_enumeratedPath.vertices.pop_back();
}
}
}
};
}
}
#endif