LCOV - code coverage report
Current view: top level - js/public - UbiNodeBreadthFirst.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 57 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 61 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13