UbiNodeBreadthFirst.h 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sts=4 et sw=4 tw=99:
  3. * This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #ifndef js_UbiNodeBreadthFirst_h
  7. #define js_UbiNodeBreadthFirst_h
  8. #include "js/UbiNode.h"
  9. #include "js/Utility.h"
  10. #include "js/Vector.h"
  11. namespace JS {
  12. namespace ubi {
  13. // A breadth-first traversal template for graphs of ubi::Nodes.
  14. //
  15. // No GC may occur while an instance of this template is live.
  16. //
  17. // The provided Handler type should have two members:
  18. //
  19. // typename NodeData;
  20. //
  21. // The value type of |BreadthFirst<Handler>::visited|, the HashMap of
  22. // ubi::Nodes that have been visited so far. Since the algorithm needs a
  23. // hash table like this for its own use anyway, it is simple to let
  24. // Handler store its own metadata about each node in the same table.
  25. //
  26. // For example, if you want to find a shortest path to each node from any
  27. // traversal starting point, your |NodeData| type could record the first
  28. // edge to reach each node, and the node from which it originates. Then,
  29. // when the traversal is complete, you can walk backwards from any node
  30. // to some starting point, and the path recorded will be a shortest path.
  31. //
  32. // This type must have a default constructor. If this type owns any other
  33. // resources, move constructors and assignment operators are probably a
  34. // good idea, too.
  35. //
  36. // bool operator() (BreadthFirst& traversal,
  37. // Node origin, const Edge& edge,
  38. // Handler::NodeData* referentData, bool first);
  39. //
  40. // The visitor function, called to report that we have traversed
  41. // |edge| from |origin|. This is called once for each edge we traverse.
  42. // As this is a breadth-first search, any prior calls to the visitor function
  43. // were for origin nodes not further from the start nodes than |origin|.
  44. //
  45. // |traversal| is this traversal object, passed along for convenience.
  46. //
  47. // |referentData| is a pointer to the value of the entry in
  48. // |traversal.visited| for |edge.referent|; the visitor function can
  49. // store whatever metadata it likes about |edge.referent| there.
  50. //
  51. // |first| is true if this is the first time we have visited an edge
  52. // leading to |edge.referent|. This could be stored in NodeData, but
  53. // the algorithm knows whether it has just created the entry in
  54. // |traversal.visited|, so it passes it along for convenience.
  55. //
  56. // The visitor function may call |traversal.abandonReferent()| if it
  57. // doesn't want to traverse the outgoing edges of |edge.referent|. You can
  58. // use this to limit the traversal to a given portion of the graph: it will
  59. // never visit nodes reachable only through nodes that you have abandoned.
  60. // Note that |abandonReferent| must be called the first time the given node
  61. // is reached; that is, |first| must be true.
  62. //
  63. // The visitor function may call |traversal.stop()| if it doesn't want
  64. // to visit any more nodes at all.
  65. //
  66. // The visitor function may consult |traversal.visited| for information
  67. // about other nodes, but it should not add or remove entries.
  68. //
  69. // The visitor function should return true on success, or false if an
  70. // error occurs. A false return value terminates the traversal
  71. // immediately, and causes BreadthFirst<Handler>::traverse to return
  72. // false.
  73. template<typename Handler>
  74. struct BreadthFirst {
  75. // Construct a breadth-first traversal object that reports the nodes it
  76. // reaches to |handler|. The traversal asserts that no GC happens in its
  77. // runtime during its lifetime.
  78. //
  79. // We do nothing with noGC, other than require it to exist, with a lifetime
  80. // that encloses our own.
  81. BreadthFirst(JSContext* cx, Handler& handler, const JS::AutoCheckCannotGC& noGC)
  82. : wantNames(true), cx(cx), visited(), handler(handler), pending(),
  83. traversalBegun(false), stopRequested(false), abandonRequested(false)
  84. { }
  85. // Initialize this traversal object. Return false on OOM.
  86. bool init() { return visited.init(); }
  87. // Add |node| as a starting point for the traversal. You may add
  88. // as many starting points as you like. Return false on OOM.
  89. bool addStart(Node node) { return pending.append(node); }
  90. // Add |node| as a starting point for the traversal (see addStart) and also
  91. // add it to the |visited| set. Return false on OOM.
  92. bool addStartVisited(Node node) {
  93. typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
  94. if (!ptr && !visited.add(ptr, node, typename Handler::NodeData()))
  95. return false;
  96. return addStart(node);
  97. }
  98. // True if the handler wants us to compute edge names; doing so can be
  99. // expensive in time and memory. True by default.
  100. bool wantNames;
  101. // Traverse the graph in breadth-first order, starting at the given
  102. // start nodes, applying |handler::operator()| for each edge traversed
  103. // as described above.
  104. //
  105. // This should be called only once per instance of this class.
  106. //
  107. // Return false on OOM or error return from |handler::operator()|.
  108. bool traverse()
  109. {
  110. MOZ_ASSERT(!traversalBegun);
  111. traversalBegun = true;
  112. // While there are pending nodes, visit them.
  113. while (!pending.empty()) {
  114. Node origin = pending.front();
  115. pending.popFront();
  116. // Get a range containing all origin's outgoing edges.
  117. auto range = origin.edges(cx, wantNames);
  118. if (!range)
  119. return false;
  120. // Traverse each edge.
  121. for (; !range->empty(); range->popFront()) {
  122. MOZ_ASSERT(!stopRequested);
  123. Edge& edge = range->front();
  124. typename NodeMap::AddPtr a = visited.lookupForAdd(edge.referent);
  125. bool first = !a;
  126. if (first) {
  127. // This is the first time we've reached |edge.referent|.
  128. // Mark it as visited.
  129. if (!visited.add(a, edge.referent, typename Handler::NodeData()))
  130. return false;
  131. }
  132. MOZ_ASSERT(a);
  133. // Report this edge to the visitor function.
  134. if (!handler(*this, origin, edge, &a->value(), first))
  135. return false;
  136. if (stopRequested)
  137. return true;
  138. // Arrange to traverse this edge's referent's outgoing edges
  139. // later --- unless |handler| asked us not to.
  140. if (abandonRequested) {
  141. // Skip the enqueue; reset flag for future iterations.
  142. abandonRequested = false;
  143. } else if (first) {
  144. if (!pending.append(edge.referent))
  145. return false;
  146. }
  147. }
  148. }
  149. return true;
  150. }
  151. // Stop traversal, and return true from |traverse| without visiting any
  152. // more nodes. Only |handler::operator()| should call this function; it
  153. // may do so to stop the traversal early, without returning false and
  154. // then making |traverse|'s caller disambiguate that result from a real
  155. // error.
  156. void stop() { stopRequested = true; }
  157. // Request that the current edge's referent's outgoing edges not be
  158. // traversed. This must be called the first time that referent is reached.
  159. // Other edges *to* that referent will still be traversed.
  160. void abandonReferent() { abandonRequested = true; }
  161. // The context with which we were constructed.
  162. JSContext* cx;
  163. // A map associating each node N that we have reached with a
  164. // Handler::NodeData, for |handler|'s use. This is public, so that
  165. // |handler| can access it to see the traversal thus far.
  166. using NodeMap = js::HashMap<Node, typename Handler::NodeData, js::DefaultHasher<Node>,
  167. js::SystemAllocPolicy>;
  168. NodeMap visited;
  169. private:
  170. // Our handler object.
  171. Handler& handler;
  172. // A queue template. Appending and popping the front are constant time.
  173. // Wasted space is never more than some recent actual population plus the
  174. // current population.
  175. template <typename T>
  176. class Queue {
  177. js::Vector<T, 0, js::SystemAllocPolicy> head, tail;
  178. size_t frontIndex;
  179. public:
  180. Queue() : head(), tail(), frontIndex(0) { }
  181. bool empty() { return frontIndex >= head.length(); }
  182. T& front() {
  183. MOZ_ASSERT(!empty());
  184. return head[frontIndex];
  185. }
  186. void popFront() {
  187. MOZ_ASSERT(!empty());
  188. frontIndex++;
  189. if (frontIndex >= head.length()) {
  190. head.clearAndFree();
  191. head.swap(tail);
  192. frontIndex = 0;
  193. }
  194. }
  195. bool append(const T& elt) {
  196. return frontIndex == 0 ? head.append(elt) : tail.append(elt);
  197. }
  198. };
  199. // A queue of nodes that we have reached, but whose outgoing edges we
  200. // have not yet traversed. Nodes reachable in fewer edges are enqueued
  201. // earlier.
  202. Queue<Node> pending;
  203. // True if our traverse function has been called.
  204. bool traversalBegun;
  205. // True if we've been asked to stop the traversal.
  206. bool stopRequested;
  207. // True if we've been asked to abandon the current edge's referent.
  208. bool abandonRequested;
  209. };
  210. } // namespace ubi
  211. } // namespace JS
  212. #endif // js_UbiNodeBreadthFirst_h