You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.5 KiB
138 lines
3.5 KiB
/* |
|
* 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" |
|
|
|
namespace android { |
|
|
|
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); |
|
} |
|
|
|
} // namespace android |
|
|
|
#endif // LIBMEMUNREACHABLE_TARJAN_H_
|
|
|