diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2020-07-27 20:45:34 +0000 |
---|---|---|
committer | John Ericson <John.Ericson@Obsidian.Systems> | 2020-07-27 20:45:34 +0000 |
commit | 8065c6d1606402e936b048aa75ad98ffdd7c8d60 (patch) | |
tree | ae0f2d7acbedeaf41389c8d1fc477b11cb96dd35 /src/libutil | |
parent | 86805a2c0a25f5ceefac0d64e64ba57ace73b7f5 (diff) |
Abstract out topo sorting logic
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/topo-sort.hh | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/src/libutil/topo-sort.hh b/src/libutil/topo-sort.hh new file mode 100644 index 000000000..7a68ff169 --- /dev/null +++ b/src/libutil/topo-sort.hh @@ -0,0 +1,40 @@ +#include "error.hh" + +namespace nix { + +template<typename T> +std::vector<T> topoSort(std::set<T> items, + std::function<std::set<T>(const T &)> getChildren, + std::function<Error(const T &, const T &)> makeCycleError) +{ + std::vector<T> sorted; + std::set<T> visited, parents; + + std::function<void(const T & path, const T * parent)> dfsVisit; + + dfsVisit = [&](const T & path, const T * parent) { + if (parents.count(path)) throw makeCycleError(path, *parent); + + if (!visited.insert(path).second) return; + parents.insert(path); + + std::set<T> references = getChildren(path); + + for (auto & i : references) + /* Don't traverse into items that don't exist in our starting set. */ + if (i != path && items.count(i)) + dfsVisit(i, &path); + + sorted.push_back(path); + parents.erase(path); + }; + + for (auto & i : items) + dfsVisit(i, nullptr); + + std::reverse(sorted.begin(), sorted.end()); + + return sorted; +} + +} |