/* * Copyright (C) 2016 The Android Open Source Project * * 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. */ // Based on system/update_engine/payload_generator/tarjan.cc #ifndef LIBMEMUNREACHABLE_TARJAN_H_ #define LIBMEMUNREACHABLE_TARJAN_H_ #include <assert.h> #include <algorithm> #include "Allocator.h" template<class T> class Node { public: allocator::set<Node<T>*> references_in; allocator::set<Node<T>*> references_out; size_t index; size_t lowlink; T* ptr; Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator), ptr(ptr) {}; Node(Node&& rhs) = default; void Edge(Node<T>* ref) { references_out.emplace(ref); ref->references_in.emplace(this); } template<class F> void Foreach(F&& f) { for (auto& node: references_out) { f(node->ptr); } } private: DISALLOW_COPY_AND_ASSIGN(Node<T>); }; template<class T> using Graph = allocator::vector<Node<T>*>; template<class T> using SCC = allocator::vector<Node<T>*>; template<class T> using SCCList = allocator::vector<SCC<T>>; template<class T> class TarjanAlgorithm { public: explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0), stack_(allocator), components_(allocator) {} void Execute(Graph<T>& graph, SCCList<T>& out); private: static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1); void Tarjan(Node<T>* vertex, Graph<T>& graph); size_t index_; allocator::vector<Node<T>*> stack_; SCCList<T> components_; }; template<class T> void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) { stack_.clear(); components_.clear(); index_ = 0; for (auto& it: graph) { it->index = UNDEFINED_INDEX; it->lowlink = UNDEFINED_INDEX; } for (auto& it: graph) { if (it->index == UNDEFINED_INDEX) { Tarjan(it, graph); } } out.swap(components_); } template<class T> void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) { assert(vertex->index == UNDEFINED_INDEX); vertex->index = index_; vertex->lowlink = index_; index_++; stack_.push_back(vertex); for (auto& it: vertex->references_out) { Node<T>* vertex_next = it; if (vertex_next->index == UNDEFINED_INDEX) { Tarjan(vertex_next, graph); vertex->lowlink = std::min(vertex->lowlink, vertex_next->lowlink); } else if (std::find(stack_.begin(), stack_.end(), vertex_next) != stack_.end()) { vertex->lowlink = std::min(vertex->lowlink, vertex_next->index); } } if (vertex->lowlink == vertex->index) { SCC<T> component{components_.get_allocator()}; Node<T>* other_vertex; do { other_vertex = stack_.back(); stack_.pop_back(); component.push_back(other_vertex); } while (other_vertex != vertex && !stack_.empty()); components_.emplace_back(component); } } template<class T> void Tarjan(Graph<T>& graph, SCCList<T>& out) { TarjanAlgorithm<T> tarjan{graph.get_allocator()}; tarjan.Execute(graph, out); } #endif // LIBMEMUNREACHABLE_TARJAN_H_