tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FindSCCs.h (5278B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 gc_FindSCCs_h
      8 #define gc_FindSCCs_h
      9 
     10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     11 
     12 #include <algorithm>  // std::min
     13 
     14 #include "js/AllocPolicy.h"         // js::SystemAllocPolicy
     15 #include "js/friend/StackLimits.h"  // js::AutoCheckRecursionLimit
     16 #include "js/HashTable.h"           // js::HashSet, js::DefaultHasher
     17 
     18 namespace js {
     19 namespace gc {
     20 
     21 template <typename Node>
     22 struct GraphNodeBase {
     23  using NodeSet =
     24      js::HashSet<Node*, js::DefaultHasher<Node*>, js::SystemAllocPolicy>;
     25 
     26  NodeSet gcGraphEdges;
     27  Node* gcNextGraphNode = nullptr;
     28  Node* gcNextGraphComponent = nullptr;
     29  unsigned gcDiscoveryTime = 0;
     30  unsigned gcLowLink = 0;
     31 
     32  Node* nextNodeInGroup() const {
     33    if (gcNextGraphNode &&
     34        gcNextGraphNode->gcNextGraphComponent == gcNextGraphComponent) {
     35      return gcNextGraphNode;
     36    }
     37    return nullptr;
     38  }
     39 
     40  Node* nextGroup() const { return gcNextGraphComponent; }
     41 };
     42 
     43 /*
     44 * Find the strongly connected components of a graph using Tarjan's algorithm,
     45 * and return them in topological order.
     46 *
     47 * Nodes derive from GraphNodeBase and add target edge pointers to
     48 * sourceNode.gcGraphEdges to describe the graph:
     49 *
     50 * struct MyGraphNode : public GraphNodeBase<MyGraphNode>
     51 * {
     52 *   ...
     53 * }
     54 *
     55 * MyGraphNode node1, node2, node3;
     56 * node1.gcGraphEdges.put(node2); // Error checking elided.
     57 * node2.gcGraphEdges.put(node3);
     58 * node3.gcGraphEdges.put(node2);
     59 *
     60 * ComponentFinder<MyGraphNode> finder;
     61 * finder.addNode(node1);
     62 * finder.addNode(node2);
     63 * finder.addNode(node3);
     64 * MyGraphNode* result = finder.getResultsList();
     65 */
     66 
     67 template <typename Node>
     68 class ComponentFinder {
     69 public:
     70  explicit ComponentFinder(JSContext* cx) : cx(cx) {}
     71 
     72  ~ComponentFinder() {
     73    MOZ_ASSERT(!stack);
     74    MOZ_ASSERT(!firstComponent);
     75  }
     76 
     77  /* Forces all nodes to be added to a single component. */
     78  void useOneComponent() { stackFull = true; }
     79 
     80  void addNode(Node* v) {
     81    if (v->gcDiscoveryTime == Undefined) {
     82      MOZ_ASSERT(v->gcLowLink == Undefined);
     83      processNode(v);
     84    }
     85  }
     86 
     87  Node* getResultsList() {
     88    if (stackFull) {
     89      /*
     90       * All nodes after the stack overflow are in |stack|. Put them all in
     91       * one big component of their own.
     92       */
     93      Node* firstGoodComponent = firstComponent;
     94      for (Node* v = stack; v; v = stack) {
     95        stack = v->gcNextGraphNode;
     96        v->gcNextGraphComponent = firstGoodComponent;
     97        v->gcNextGraphNode = firstComponent;
     98        firstComponent = v;
     99      }
    100      stackFull = false;
    101    }
    102 
    103    MOZ_ASSERT(!stack);
    104 
    105    Node* result = firstComponent;
    106    firstComponent = nullptr;
    107 
    108    for (Node* v = result; v; v = v->gcNextGraphNode) {
    109      v->gcDiscoveryTime = Undefined;
    110      v->gcLowLink = Undefined;
    111    }
    112 
    113    return result;
    114  }
    115 
    116  static void mergeGroups(Node* first) {
    117    for (Node* v = first; v; v = v->gcNextGraphNode) {
    118      v->gcNextGraphComponent = nullptr;
    119    }
    120  }
    121 
    122 private:
    123  // Constant used to indicate an unprocessed vertex.
    124  static const unsigned Undefined = 0;
    125 
    126  // Constant used to indicate a processed vertex that is no longer on the
    127  // stack.
    128  static const unsigned Finished = (unsigned)-1;
    129 
    130  void addEdgeTo(Node* w) {
    131    if (w->gcDiscoveryTime == Undefined) {
    132      processNode(w);
    133      cur->gcLowLink = std::min(cur->gcLowLink, w->gcLowLink);
    134    } else if (w->gcDiscoveryTime != Finished) {
    135      cur->gcLowLink = std::min(cur->gcLowLink, w->gcDiscoveryTime);
    136    }
    137  }
    138 
    139  void processNode(Node* v) {
    140    v->gcDiscoveryTime = clock;
    141    v->gcLowLink = clock;
    142    ++clock;
    143 
    144    v->gcNextGraphNode = stack;
    145    stack = v;
    146 
    147    if (stackFull) {
    148      return;
    149    }
    150 
    151    AutoCheckRecursionLimit recursion(cx);
    152    if (!recursion.checkSystemDontReport(cx)) {
    153      stackFull = true;
    154      return;
    155    }
    156 
    157    Node* old = cur;
    158    cur = v;
    159    for (auto r = cur->gcGraphEdges.all(); !r.empty(); r.popFront()) {
    160      addEdgeTo(r.front());
    161    }
    162    cur = old;
    163 
    164    if (stackFull) {
    165      return;
    166    }
    167 
    168    if (v->gcLowLink == v->gcDiscoveryTime) {
    169      Node* nextComponent = firstComponent;
    170      Node* w;
    171      do {
    172        MOZ_ASSERT(stack);
    173        w = stack;
    174        stack = w->gcNextGraphNode;
    175 
    176        /*
    177         * Record that the element is no longer on the stack by setting the
    178         * discovery time to a special value that's not Undefined.
    179         */
    180        w->gcDiscoveryTime = Finished;
    181 
    182        /* Figure out which group we're in. */
    183        w->gcNextGraphComponent = nextComponent;
    184 
    185        /*
    186         * Prepend the component to the beginning of the output list to
    187         * reverse the list and achieve the desired order.
    188         */
    189        w->gcNextGraphNode = firstComponent;
    190        firstComponent = w;
    191      } while (w != v);
    192    }
    193  }
    194 
    195 private:
    196  unsigned clock = 1;
    197  Node* stack = nullptr;
    198  Node* firstComponent = nullptr;
    199  Node* cur = nullptr;
    200  JSContext* cx;
    201  bool stackFull = false;
    202 };
    203 
    204 } /* namespace gc */
    205 } /* namespace js */
    206 
    207 #endif /* gc_FindSCCs_h */