1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Based on system/update_engine/payload_generator/tarjan.cc 18 19 #ifndef LIBMEMUNREACHABLE_TARJAN_H_ 20 #define LIBMEMUNREACHABLE_TARJAN_H_ 21 22 #include <assert.h> 23 #include <algorithm> 24 25 #include "Allocator.h" 26 27 template<class T> 28 class Node { 29 public: 30 allocator::set<Node<T>*> references_in; 31 allocator::set<Node<T>*> references_out; 32 size_t index; 33 size_t lowlink; 34 35 T* ptr; 36 37 Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator), 38 ptr(ptr) {}; 39 Node(Node&& rhs) = default; 40 void Edge(Node<T>* ref) { 41 references_out.emplace(ref); 42 ref->references_in.emplace(this); 43 } 44 template<class F> 45 void Foreach(F&& f) { 46 for (auto& node: references_out) { 47 f(node->ptr); 48 } 49 } 50 private: 51 DISALLOW_COPY_AND_ASSIGN(Node<T>); 52 }; 53 54 template<class T> 55 using Graph = allocator::vector<Node<T>*>; 56 57 template<class T> 58 using SCC = allocator::vector<Node<T>*>; 59 60 template<class T> 61 using SCCList = allocator::vector<SCC<T>>; 62 63 template<class T> 64 class TarjanAlgorithm { 65 public: 66 explicit TarjanAlgorithm(Allocator<void> allocator) : index_(0), 67 stack_(allocator), components_(allocator) {} 68 69 void Execute(Graph<T>& graph, SCCList<T>& out); 70 private: 71 static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1); 72 void Tarjan(Node<T>* vertex, Graph<T>& graph); 73 74 size_t index_; 75 allocator::vector<Node<T>*> stack_; 76 SCCList<T> components_; 77 }; 78 79 template<class T> 80 void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) { 81 stack_.clear(); 82 components_.clear(); 83 index_ = 0; 84 for (auto& it: graph) { 85 it->index = UNDEFINED_INDEX; 86 it->lowlink = UNDEFINED_INDEX; 87 } 88 89 for (auto& it: graph) { 90 if (it->index == UNDEFINED_INDEX) { 91 Tarjan(it, graph); 92 } 93 } 94 out.swap(components_); 95 } 96 97 template<class T> 98 void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) { 99 assert(vertex->index == UNDEFINED_INDEX); 100 vertex->index = index_; 101 vertex->lowlink = index_; 102 index_++; 103 stack_.push_back(vertex); 104 for (auto& it: vertex->references_out) { 105 Node<T>* vertex_next = it; 106 if (vertex_next->index == UNDEFINED_INDEX) { 107 Tarjan(vertex_next, graph); 108 vertex->lowlink = std::min(vertex->lowlink, vertex_next->lowlink); 109 } else if (std::find(stack_.begin(), stack_.end(), vertex_next) != stack_.end()) { 110 vertex->lowlink = std::min(vertex->lowlink, vertex_next->index); 111 } 112 } 113 if (vertex->lowlink == vertex->index) { 114 SCC<T> component{components_.get_allocator()}; 115 Node<T>* other_vertex; 116 do { 117 other_vertex = stack_.back(); 118 stack_.pop_back(); 119 component.push_back(other_vertex); 120 } while (other_vertex != vertex && !stack_.empty()); 121 122 components_.emplace_back(component); 123 } 124 } 125 126 template<class T> 127 void Tarjan(Graph<T>& graph, SCCList<T>& out) { 128 TarjanAlgorithm<T> tarjan{graph.get_allocator()}; 129 tarjan.Execute(graph, out); 130 } 131 132 #endif // LIBMEMUNREACHABLE_TARJAN_H_ 133