зеркало из https://github.com/mozilla/pjs.git
343 строки
12 KiB
C++
343 строки
12 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#ifndef GRAPHUTILS_H
|
|
#define GRAPHUTILS_H
|
|
|
|
#include "Fundamentals.h"
|
|
|
|
template<class Successor>
|
|
struct SearchStackEntry
|
|
{
|
|
Successor *next; // Next child to be searched at this level of the graph
|
|
Successor *limit; // Last child+1 to be searched at this level of the graph
|
|
};
|
|
|
|
|
|
//
|
|
// Search and mark all reachable nodes of the directed graph with the given root.
|
|
// The graph must have no more than maxNNodes nodes reachable from the root.
|
|
// The stack argument must be a preallocated temporary array of maxNNodes entries of
|
|
// type SearchStackEntry<Successor>.
|
|
//
|
|
// The NodeRef class is a way to refer to nodes in the graph (NodeRefs may be
|
|
// pointers to nodes, node indices, etc.).
|
|
// The SearchParams is a helper class that graphSimpleSearch uses to access the
|
|
// contents of graph nodes. SearchParams must support the following types and
|
|
// methods (static or dynamic):
|
|
//
|
|
// typedef Successor;
|
|
// typedef NodeRef;
|
|
//
|
|
// Successor *getSuccessorsBegin(NodeRef n);
|
|
// Successor *getSuccessorsEnd(NodeRef n);
|
|
// Return the bounds of an array of Successors of node n.
|
|
//
|
|
// NodeRef getNodeRef(Successor &s);
|
|
// Return the node to which the Successor s refers.
|
|
//
|
|
// bool isMarked(NodeRef n);
|
|
// void setMarked(NodeRef n);
|
|
// Each node in the graph is either marked or unmarked.
|
|
// All nodes should be in the unmarked state when graphSimpleSearch is called;
|
|
// graphSimpleSearch will not traverse any marked nodes it encounters. The
|
|
// setMarked method changes the state of a node to marked. Every node
|
|
// reachable from the root will be marked when graphSimpleSearch exits.
|
|
// setMarked is only called on unmarked nodes.
|
|
//
|
|
template<class SearchParams, class Successor>
|
|
void graphSimpleSearch(SearchParams &searchParams, Successor root, Uint32 DEBUG_ONLY(maxNNodes), SearchStackEntry<Successor> *stack)
|
|
{
|
|
#ifdef DEBUG
|
|
SearchStackEntry<Successor> *stackEnd = stack + maxNNodes;
|
|
#endif
|
|
|
|
SearchStackEntry<Successor> *sp = stack;
|
|
|
|
// Prepare to visit the root.
|
|
Successor *n = &root;
|
|
Successor *l = &root + 1;
|
|
|
|
while (true) {
|
|
if (n == l) {
|
|
// We're done with all successors between n and l, so pop up one level.
|
|
// Finish when we've marked the root.
|
|
if (sp == stack)
|
|
break;
|
|
--sp;
|
|
n = sp->next;
|
|
l = sp->limit;
|
|
|
|
} else {
|
|
// We still have to visit more successors between n and l. Mark the
|
|
// next successor and advance n.
|
|
typename SearchParams::NodeRef node = searchParams.getNodeRef(*n++);
|
|
if (!searchParams.isMarked(node)) {
|
|
// Mark the successor, saving the current place on the stack.
|
|
searchParams.setMarked(node);
|
|
assert(sp < stackEnd);
|
|
sp->next = n;
|
|
sp->limit = l;
|
|
sp++;
|
|
n = searchParams.getSuccessorsBegin(node);
|
|
l = searchParams.getSuccessorsEnd(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Search, mark, and count all reachable nodes of the directed graph with
|
|
// the given root (which may be null). The graph must have no more than
|
|
// maxNNodes nodes reachable from the root. The stack argument must be a
|
|
// preallocated temporary array of maxNNodes entries of type
|
|
// SearchStackEntry<Successor>.
|
|
//
|
|
// This function returns the number of nodes reached, including the root.
|
|
//
|
|
// The NodeRef class is a way to refer to nodes in the graph (NodeRefs may be
|
|
// pointers to nodes, node indices, etc.).
|
|
// The SearchParams is a helper class that graphSearch uses to access the
|
|
// contents of graph nodes. SearchParams must support the following types and
|
|
// methods (static or dynamic):
|
|
//
|
|
// typedef Successor;
|
|
// typedef NodeRef;
|
|
//
|
|
// Successor *getSuccessorsBegin(NodeRef n);
|
|
// Successor *getSuccessorsEnd(NodeRef n);
|
|
// Return the bounds of an array of Successors of node n.
|
|
//
|
|
// bool isNull(Successor &s);
|
|
// Returns true if n is null.
|
|
//
|
|
// NodeRef getNodeRef(Successor &s);
|
|
// Return the node to which the Successor s refers.
|
|
//
|
|
// bool isMarked(NodeRef n);
|
|
// void setMarked(NodeRef n);
|
|
// Each node in the graph is either marked or unmarked.
|
|
// All nodes should be in the unmarked state when graphSearch is called;
|
|
// graphSearch will not traverse any marked nodes it encounters. The
|
|
// setMarked method changes the state of a node to marked. Every node
|
|
// reachable from the root will be marked when graphSearch exits.
|
|
// setMarked is only called on unmarked nodes.
|
|
//
|
|
// void notePredecessor(NodeRef n);
|
|
// This method is called on node n once for each time n is a successor
|
|
// of any node or the root. The SearchParams class may use this method to
|
|
// count predecessors of node n.
|
|
//
|
|
template<class SearchParams, class Successor>
|
|
Uint32 graphSearch(SearchParams &searchParams, Successor &root, Uint32 DEBUG_ONLY(maxNNodes), SearchStackEntry<Successor> *stack)
|
|
{
|
|
#ifdef DEBUG
|
|
SearchStackEntry<Successor> *stackEnd = stack + maxNNodes;
|
|
#endif
|
|
|
|
Uint32 nNodes = 0;
|
|
if (!searchParams.isNull(root)) {
|
|
SearchStackEntry<Successor> *sp = stack;
|
|
|
|
// Prepare to visit the root.
|
|
Successor *n = &root;
|
|
Successor *l = &root + 1;
|
|
|
|
while (true) {
|
|
if (n == l) {
|
|
// We're done with all successors between n and l, so pop up one level.
|
|
// Finish when we've marked the root.
|
|
if (sp == stack)
|
|
break;
|
|
--sp;
|
|
n = sp->next;
|
|
l = sp->limit;
|
|
|
|
} else {
|
|
// We still have to visit more successors between n and l. Mark the
|
|
// next successor and advance n.
|
|
typename SearchParams::NodeRef node = searchParams.getNodeRef(*n++);
|
|
searchParams.notePredecessor(node);
|
|
if (!searchParams.isMarked(node)) {
|
|
// Mark the successor, saving the current place on the stack.
|
|
nNodes++;
|
|
searchParams.setMarked(node);
|
|
assert(sp < stackEnd);
|
|
sp->next = n;
|
|
sp->limit = l;
|
|
sp++;
|
|
n = searchParams.getSuccessorsBegin(node);
|
|
l = searchParams.getSuccessorsEnd(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert(nNodes <= maxNNodes);
|
|
return nNodes;
|
|
}
|
|
|
|
|
|
//
|
|
// A specialized version of graphSearch for graphs with a designated end node.
|
|
// The end node must have no successors. There may be other nodes with no successors.
|
|
// Unlike all other nodes, which must be reachable from the root, the end node does
|
|
// not have to be reachable from the root; if it's not reachable, it will have no
|
|
// predecessors.
|
|
//
|
|
// The end node may be the root node, in which case the graph consists of exactly
|
|
// one node.
|
|
//
|
|
// graphSearchWithEnd guarantees that the end node will be marked and counted,
|
|
// even if it is not reachable from the root.
|
|
//
|
|
template<class SearchParams, class Successor>
|
|
Uint32 graphSearchWithEnd(SearchParams &searchParams, Successor &root, Successor &end,
|
|
Uint32 maxNNodes, SearchStackEntry<Successor> *stack)
|
|
{
|
|
assert(!searchParams.isNull(end));
|
|
typename SearchParams::NodeRef endNode = searchParams.getNodeRef(end);
|
|
assert(searchParams.getSuccessorsBegin(endNode) == searchParams.getSuccessorsEnd(endNode));
|
|
searchParams.setMarked(endNode);
|
|
return 1 + graphSearch(searchParams, root, maxNNodes, stack);
|
|
}
|
|
|
|
|
|
//
|
|
// Perform a depth-first search of the directed graph with the given root
|
|
// (which may be null). This search will assign a unique integer between 0
|
|
// and nNodes-1 to each node in the graph according to the graph's depth-
|
|
// first ordering (see [ASU86], page 661). The graph must have exactly nNodes
|
|
// nodes reachable from the root. The stack argument must be a preallocated
|
|
// temporary array of nNodes entries of type SearchStackEntry<Successor>.
|
|
//
|
|
// The NodeRef class is a way to refer to nodes in the graph (NodeRefs may be
|
|
// pointers to nodes, node indices, etc.).
|
|
// The DFSParams is a helper class that depthFirstSearch uses to access the
|
|
// contents of graph nodes. DFSParams must support the following types and
|
|
// methods (static or dynamic):
|
|
//
|
|
// typedef Successor;
|
|
// typedef NodeRef;
|
|
//
|
|
// Successor *getSuccessorsBegin(NodeRef n);
|
|
// Successor *getSuccessorsEnd(NodeRef n);
|
|
// Return the bounds of an array of Successors of node n.
|
|
//
|
|
// bool isNull(Successor &s);
|
|
// Returns true if n is null.
|
|
//
|
|
// NodeRef getNodeRef(Successor &s);
|
|
// Return the node to which the Successor s refers.
|
|
//
|
|
// bool isUnvisited(NodeRef n);
|
|
// bool isNumbered(NodeRef n);
|
|
// void setVisited(NodeRef n);
|
|
// void setNumbered(NodeRef n, Int32 i);
|
|
// Each node in the graph is in one of three states:
|
|
// unvisited, visited, or numbered.
|
|
// All nodes should be in the unvisited state when depthFirstSearch is
|
|
// called. The setVisited and setNumbered methods change the state of
|
|
// a node to visited or numbered; i is the node's depth-first ordering
|
|
// index. Every node will be numbered when depthFirstSearch exits.
|
|
// setVisited is only called on an unvisited node. setNumbered is only
|
|
// called on an unvisited or visited node.
|
|
// isUnvisited and isNumbered query the current state of a node.
|
|
//
|
|
// void noteIncomingBackwardEdge(NodeRef n);
|
|
// This method is called on node n once for each time n is the target
|
|
// of any backward edge. The DFSParams class may use this method to
|
|
// determine if the graph contains cycles and which nodes are cycle
|
|
// headers.
|
|
//
|
|
template<class DFSParams, class Successor>
|
|
void depthFirstSearch(DFSParams &dfsParams, Successor &root, Uint32 nNodes, SearchStackEntry<Successor> *stack)
|
|
{
|
|
#ifdef DEBUG
|
|
SearchStackEntry<Successor> *stackEnd = stack + nNodes;
|
|
#endif
|
|
|
|
if (!dfsParams.isNull(root)) {
|
|
SearchStackEntry<Successor> *sp = stack;
|
|
|
|
// Prepare to visit the root.
|
|
Successor *n = &root;
|
|
Successor *l = &root + 1;
|
|
|
|
while (true) {
|
|
if (n == l) {
|
|
// We're done with all successors between n and l, so number the
|
|
// source node (which is on the stack) and pop up one level.
|
|
// Finish when we've marked the root.
|
|
if (sp == stack)
|
|
break;
|
|
--sp;
|
|
n = sp->next;
|
|
l = sp->limit;
|
|
dfsParams.setNumbered(dfsParams.getNodeRef(n[-1]), --nNodes);
|
|
|
|
} else {
|
|
// We still have to visit more successors between n and l. Visit the
|
|
// next successor and advance n.
|
|
typename DFSParams::NodeRef node = dfsParams.getNodeRef(*n++);
|
|
if (dfsParams.isUnvisited(node)) {
|
|
// Visit the successor, saving the current place on the stack.
|
|
dfsParams.setVisited(node);
|
|
assert(sp < stackEnd);
|
|
sp->next = n;
|
|
sp->limit = l;
|
|
sp++;
|
|
n = dfsParams.getSuccessorsBegin(node);
|
|
l = dfsParams.getSuccessorsEnd(node);
|
|
} else
|
|
// We have a cycle if we ever encounter a successor that has been visited
|
|
// but not yet numbered.
|
|
if (!dfsParams.isNumbered(node))
|
|
dfsParams.noteIncomingBackwardEdge(node);
|
|
}
|
|
}
|
|
}
|
|
assert(nNodes == 0);
|
|
}
|
|
|
|
|
|
//
|
|
// A specialized version of depthFirstSearch for graphs with a designated end node.
|
|
// The end node must have no successors. There may be other nodes with no successors.
|
|
// Unlike all other nodes, which must be reachable from the root, the end node does
|
|
// not have to be reachable from the root; if it's not reachable, it will have no
|
|
// predecessors.
|
|
//
|
|
// The end node may be the root node, in which case the graph consists of exactly
|
|
// one node.
|
|
//
|
|
// depthFirstSearchWithEnd guarantees that the end node will be assigned the number nNodes-1.
|
|
//
|
|
template<class DFSParams, class Successor>
|
|
void depthFirstSearchWithEnd(DFSParams &dfsParams, Successor &root, Successor &end,
|
|
Uint32 nNodes, SearchStackEntry<Successor> *stack)
|
|
{
|
|
assert(!dfsParams.isNull(end));
|
|
typename DFSParams::NodeRef endNode = dfsParams.getNodeRef(end);
|
|
assert(dfsParams.getSuccessorsBegin(endNode) == dfsParams.getSuccessorsEnd(endNode));
|
|
dfsParams.setNumbered(endNode, --nNodes);
|
|
depthFirstSearch(dfsParams, root, nNodes, stack);
|
|
}
|
|
|
|
#endif
|