/* * 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 #include #include "Allocator.h" template class Node { public: allocator::set*> references_in; allocator::set*> references_out; size_t index; size_t lowlink; T* ptr; Node(T* ptr, Allocator allocator) : references_in(allocator), references_out(allocator), ptr(ptr) {}; Node(Node&& rhs) = default; void Edge(Node* ref) { references_out.emplace(ref); ref->references_in.emplace(this); } template void Foreach(F&& f) { for (auto& node: references_out) { f(node->ptr); } } private: DISALLOW_COPY_AND_ASSIGN(Node); }; template using Graph = allocator::vector*>; template using SCC = allocator::vector*>; template using SCCList = allocator::vector>; template class TarjanAlgorithm { public: explicit TarjanAlgorithm(Allocator allocator) : index_(0), stack_(allocator), components_(allocator) {} void Execute(Graph& graph, SCCList& out); private: static constexpr size_t UNDEFINED_INDEX = static_cast(-1); void Tarjan(Node* vertex, Graph& graph); size_t index_; allocator::vector*> stack_; SCCList components_; }; template void TarjanAlgorithm::Execute(Graph& graph, SCCList& 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 void TarjanAlgorithm::Tarjan(Node* vertex, Graph& graph) { assert(vertex->index == UNDEFINED_INDEX); vertex->index = index_; vertex->lowlink = index_; index_++; stack_.push_back(vertex); for (auto& it: vertex->references_out) { Node* 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 component{components_.get_allocator()}; Node* 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 void Tarjan(Graph& graph, SCCList& out) { TarjanAlgorithm tarjan{graph.get_allocator()}; tarjan.Execute(graph, out); } #endif // LIBMEMUNREACHABLE_TARJAN_H_