• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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