1 /* 2 * Copyright 2014 Google Inc. All rights reserved. 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 #ifndef SEMISTATIC_GRAPH_H 18 #define SEMISTATIC_GRAPH_H 19 20 #include "memory_pool.h" 21 #include <fruit/impl/data_structures/semistatic_map.h> 22 23 #if FRUIT_EXTRA_DEBUG 24 #include <iostream> 25 #endif 26 27 namespace fruit { 28 namespace impl { 29 30 // The alignas ensures that a SemistaticGraphInternalNodeId* always has 0 in the low-order bit. 31 struct alignas(2) alignas(alignof(std::size_t)) SemistaticGraphInternalNodeId { 32 // This stores the index in the vector times sizeof(NodeData). 33 std::size_t id; 34 35 bool operator==(const SemistaticGraphInternalNodeId& x) const; 36 bool operator<(const SemistaticGraphInternalNodeId& x) const; 37 }; 38 39 /** 40 * A direct graph implementation where most of the graph is fixed at construction time, but a few nodes and edges can be 41 * added 42 * later. 43 * 44 * Also, nodes can either be normal nodes or terminal nodes. Terminal nodes can't have outgoing edges. Note that a node 45 * with no 46 * outgoing edges may or may not be marked as terminal. 47 * 48 * While insertion of elements after construction is supported, inserting or changing the neighbors of more than O(1) 49 * nodes 50 * after construction will raise the cost of any further operations to more than O(1). 51 * 52 * Even though adding nodes/edges after construction is inefficient, it is efficient to turn non-terminal nodes into 53 * terminal ones 54 * (and therefore removing all the outgoing edges from the node) after construction. 55 * 56 * NodeId and Node must be default constructible and trivially copyable. 57 */ 58 template <typename NodeId, typename Node> 59 class SemistaticGraph { 60 private: 61 using InternalNodeId = SemistaticGraphInternalNodeId; 62 63 // The node data for nodeId is in nodes[node_index_map.at(nodeId)/sizeof(NodeData)]. 64 // To avoid hash table lookups, the edges in edges_storage are stored as indexes of `nodes' instead of as NodeIds. 65 // node_index_map contains all known NodeIds, including ones known only due to an outgoing edge ending there from 66 // another node. 67 SemistaticMap<NodeId, InternalNodeId> node_index_map; 68 69 struct NodeData { 70 #if FRUIT_EXTRA_DEBUG 71 NodeId key; 72 #endif 73 74 public: 75 // If edges_begin==0, this is a terminal node. 76 // If edges_begin==1, this node doesn't exist, it's just referenced by another node. 77 // Otherwise, reinterpret_cast<InternalNodeId*>(edges_begin) is the beginning of the edges range. 78 std::uintptr_t edges_begin; 79 80 // An explicit "public" specifier here prevents the compiler from reordering the fields. 81 // We want the edges_begin field to be stored first because that's what we're going to branch on. 82 public: 83 Node node; 84 }; 85 86 std::size_t first_unused_index; 87 88 FixedSizeVector<NodeData> nodes; 89 90 // Stores vectors of edges as contiguous chunks of node IDs. 91 // The NodeData elements in `nodes' contain indexes into this vector (stored as already multiplied by 92 // sizeof(NodeData)). 93 // The first element is unused. 94 FixedSizeVector<InternalNodeId> edges_storage; 95 96 #if FRUIT_EXTRA_DEBUG 97 template <typename NodeIter> 98 void printGraph(NodeIter first, NodeIter last); 99 #endif 100 101 NodeData* nodeAtId(InternalNodeId internalNodeId); 102 const NodeData* nodeAtId(InternalNodeId internalNodeId) const; 103 104 static NodeData* nodeAtId(NodeData* nodes_begin, InternalNodeId internalNodeId); 105 static const NodeData* nodeAtId(const NodeData* nodes_begin, InternalNodeId internalNodeId); 106 107 public: 108 class edge_iterator; 109 110 class node_iterator { 111 private: 112 NodeData* itr; 113 114 friend class SemistaticGraph<NodeId, Node>; 115 116 node_iterator(NodeData* itr); 117 118 public: 119 Node& getNode(); 120 121 bool isTerminal(); 122 123 // Turns the node into a terminal node, also removing all the deps. 124 void setTerminal(); 125 126 // Assumes !isTerminal(). 127 // neighborsEnd() is NOT provided/stored for efficiency, the client code is expected to know the number of 128 // neighbors. 129 edge_iterator neighborsBegin(); 130 131 bool operator==(const node_iterator&) const; 132 }; 133 134 class const_node_iterator { 135 private: 136 const NodeData* itr; 137 138 friend class SemistaticGraph<NodeId, Node>; 139 140 const_node_iterator(const NodeData* itr); 141 142 public: 143 const_node_iterator(node_iterator itr); 144 145 const Node& getNode(); 146 147 bool isTerminal(); 148 149 bool operator==(const const_node_iterator&) const; 150 }; 151 152 153 class edge_iterator { 154 private: 155 // Iterator on edges_storage. 156 InternalNodeId* itr; 157 158 friend class SemistaticGraph<NodeId, Node>; 159 friend class SemistaticGraph<NodeId, Node>::node_iterator; 160 161 edge_iterator(InternalNodeId* itr); 162 163 public: 164 // getNodeIterator(graph.nodes.begin()) returns the first neighbor. 165 node_iterator getNodeIterator(node_iterator nodes_begin); 166 167 void operator++(); 168 169 // Equivalent to i times operator++ followed by getNodeIterator(nodes_begin). 170 node_iterator getNodeIterator(std::size_t i, node_iterator nodes_begin); 171 }; 172 173 // Constructs an *invalid* graph (as if this graph was just moved from). 174 SemistaticGraph() = default; 175 176 /** 177 * A value x obtained dereferencing a NodeIter::value_type must support the following operations: 178 * - x.getId(), returning a NodeId 179 * - x.getValue(), returning a Node 180 * - x.isTerminal(), returning a bool 181 * - x.getEdgesBegin() and x.getEdgesEnd(), that if !x.isTerminal() define a range of values of type NodeId 182 * (the outgoing edges). 183 * 184 * This constructor is *not* defined in semistatic_graph.templates.h, but only in semistatic_graph.cc. 185 * All instantiations must have a matching instantiation in semistatic_graph.cc. 186 * 187 * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool. 188 */ 189 template <typename NodeIter> 190 SemistaticGraph(NodeIter first, NodeIter last, MemoryPool& memory_pool); 191 192 SemistaticGraph(SemistaticGraph&&) = default; 193 SemistaticGraph(const SemistaticGraph&) = delete; 194 195 /** 196 * Creates a copy of x with the additional nodes in [first, last). The requirements on NodeIter as the same as for the 197 * 2-arg 198 * constructor. 199 * The nodes in [first, last) must NOT be already in x, but can be neighbors of nodes in x. 200 * The new graph will share data with `x', so must be destroyed before `x' is destroyed. 201 * Also, after this is called, `x' must not be modified until this object has been destroyed. 202 * 203 * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool. 204 */ 205 template <typename NodeIter> 206 SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last, MemoryPool& memory_pool); 207 208 ~SemistaticGraph(); 209 210 SemistaticGraph& operator=(const SemistaticGraph&) = delete; 211 SemistaticGraph& operator=(SemistaticGraph&&) = default; 212 213 // The result is unspecified. The only guarantee is that it's the right value to pass to edge_iterator's 214 // getNodeIterator() methods. 215 node_iterator begin(); 216 217 node_iterator end(); 218 const_node_iterator end() const; 219 220 // Precondition: `nodeId' must exist in the graph. 221 // Unlike std::map::at(), this yields undefined behavior if the precondition isn't satisfied (instead of throwing). 222 node_iterator at(NodeId nodeId); 223 224 // Prefer using at() when possible, this is slightly slower. 225 // Returns end() if the node ID was not found. 226 node_iterator find(NodeId nodeId); 227 const_node_iterator find(NodeId nodeId) const; 228 229 #if FRUIT_EXTRA_DEBUG 230 // Emits a runtime error if some node was not created but there is an edge pointing to it. 231 void checkFullyConstructed(); 232 #endif 233 }; 234 235 } // namespace impl 236 } // namespace fruit 237 238 #include <fruit/impl/data_structures/semistatic_graph.defn.h> 239 240 // semistatic_graph.templates.h is not included here to limit the transitive includes. Include it explicitly (in .cpp 241 // files). 242 243 #endif // SEMISTATIC_GRAPH_H 244