/* * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include namespace WTF { template class SingleRootGraphNode { WTF_MAKE_FAST_ALLOCATED; public: // We use "#root" to refer to the synthetic root we have created. static const char* rootName() { return "#root"; }; SingleRootGraphNode(typename Graph::Node node = typename Graph::Node()) : m_node(node) { } static SingleRootGraphNode root() { SingleRootGraphNode result; result.m_node = 0; result.m_isRoot = true; return result; } bool operator==(const SingleRootGraphNode& other) const { return m_node == other.m_node && m_isRoot == other.m_isRoot; } bool operator!=(const SingleRootGraphNode& other) const { return !(*this == other); } explicit operator bool() const { return *this != SingleRootGraphNode(); } bool isRoot() const { return m_isRoot; } typename Graph::Node node() const { ASSERT(!m_isRoot); return m_node; } private: typename Graph::Node m_node; bool m_isRoot { false }; }; template class SingleRootGraphSet { WTF_MAKE_FAST_ALLOCATED; using Node = SingleRootGraphNode; public: SingleRootGraphSet() = default; bool add(const Node& node) { if (node.isRoot()) return checkAndSet(m_hasRoot, true); return m_set.add(node.node()); } bool remove(const Node& node) { if (node.isRoot()) return checkAndSet(m_hasRoot, false); return m_set.remove(node.node()); } bool contains(const Node& node) { if (node.isRoot()) return m_hasRoot; return m_set.contains(node.node()); } void dump(PrintStream& out) const { if (m_hasRoot) out.print(Node::rootName(), " "); out.print(m_set); } private: typename Graph::Set m_set; bool m_hasRoot { false }; }; template class SingleRootMap { WTF_MAKE_FAST_ALLOCATED; using Node = SingleRootGraphNode; public: SingleRootMap(Graph& graph) : m_map(graph.template newMap()) { } SingleRootMap(SingleRootMap&& other) : m_map(WTFMove(other.m_map)) , m_root(WTFMove(other.m_root)) { } void clear() { m_map.clear(); m_root = T(); } size_t size() const { return m_map.size() + 1; } T& operator[](size_t index) { if (!index) return m_root; return m_map[index - 1]; } const T& operator[](size_t index) const { return (*const_cast(this))[index]; } T& operator[](const Node& node) { if (node.isRoot()) return m_root; return m_map[node.node()]; } const T& operator[](const Node& node) const { return (*const_cast(this))[node]; } private: typename Graph::template Map m_map; T m_root; }; template class SingleRootGraph { WTF_MAKE_NONCOPYABLE(SingleRootGraph); WTF_MAKE_FAST_ALLOCATED; public: using Node = SingleRootGraphNode; using Set = SingleRootGraphSet; template using Map = SingleRootMap; using List = Vector; SingleRootGraph(Graph& graph) : m_graph(graph) { for (typename Graph::Node realRoot : m_graph.roots()) { ASSERT(m_graph.predecessors(realRoot).isEmpty()); m_rootSuccessorList.append(realRoot); m_rootSuccessorSet.add(realRoot); } ASSERT(m_rootSuccessorList.size()); } Node root() const { return Node::root(); } template Map newMap() { return Map(m_graph); } List successors(const Node& node) const { assertIsConsistent(); if (node.isRoot()) return m_rootSuccessorList; List result; for (typename Graph::Node successor : m_graph.successors(node.node())) result.append(successor); return result; } List predecessors(const Node& node) const { assertIsConsistent(); if (node.isRoot()) return List { }; if (m_rootSuccessorSet.contains(node.node())) { ASSERT(m_graph.predecessors(node.node()).isEmpty()); return List { root() }; } List result; for (typename Graph::Node predecessor : m_graph.predecessors(node.node())) result.append(predecessor); return result; } unsigned index(const Node& node) const { if (node.isRoot()) return 0; return m_graph.index(node.node()) + 1; } Node node(unsigned index) const { if (!index) return root(); return m_graph.node(index - 1); } unsigned numNodes() const { return m_graph.numNodes() + 1; } CString dump(Node node) const { StringPrintStream out; if (!node) out.print(""); else if (node.isRoot()) out.print(Node::rootName()); else out.print(m_graph.dump(node.node())); return out.toCString(); } void dump(PrintStream& out) const { for (unsigned i = 0; i < numNodes(); ++i) { Node node = this->node(i); if (!node) continue; out.print(dump(node), ":\n"); out.print(" Preds: "); CommaPrinter comma; for (Node predecessor : predecessors(node)) out.print(comma, dump(predecessor)); out.print("\n"); out.print(" Succs: "); comma = CommaPrinter(); for (Node successor : successors(node)) out.print(comma, dump(successor)); out.print("\n"); } } private: ALWAYS_INLINE void assertIsConsistent() const { #if ASSERT_ENABLED // We expect the roots() function to be idempotent while we're alive so we can cache // the result in the constructor. If a user of this changes the result of its roots() // function, it's expected that the user will create a new instance of this class. List rootSuccessorList; for (typename Graph::Node realRoot : m_graph.roots()) { ASSERT(m_graph.predecessors(realRoot).isEmpty()); rootSuccessorList.append(realRoot); } ASSERT(rootSuccessorList.size()); ASSERT(rootSuccessorList == m_rootSuccessorList); #endif } Graph& m_graph; List m_rootSuccessorList; typename Graph::Set m_rootSuccessorSet; }; } // namespace WTF using WTF::SingleRootGraph;