home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume12
/
pathalias9
/
part02
/
mapit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1987-10-08
|
14KB
|
619 lines
/* pathalias -- by steve bellovin, as told to peter honeyman */
#ifndef lint
static char *sccsid = "@(#)mapit.c 9.1 87/10/04";
#endif
#include "def.h"
#define chkheap(i) /* void */
/* exports */
/* invariant while mapping: Nheap < Hashpart */
long Hashpart; /* start of unreached nodes */
long Nheap; /* end of heap */
void mapit();
/* imports */
extern long Nheap, Hashpart, Tabsize;
extern int Tflag, Vflag;
extern node **Table, *Home;
extern char *Linkout, *Graphout;
extern void freelink(), resetnodes(), printit(), dumpgraph();
extern void showlinks(), die();
extern long pack(), allocation();
extern link *newlink(), *addlink();
extern int maptrace();
extern node *ncopy();
/* privates */
static long Heaphighwater;
static link **Heap;
STATIC void insert(), heapup(), heapdown(), heapswap(), backlinks();
STATIC void setheapbits(), mtracereport(), heapchildren();
STATIC link *min_node();
STATIC int dehash(), skiplink(), skipterminalalias();
STATIC Cost costof();
/* transform the graph to a shortest-path tree by marking tree edges */
void
mapit()
{ register node *n;
register link *l;
link *lparent;
static int firsttime = 0;
vprintf(stderr, "*** mapping\n");
Tflag = Tflag && Vflag; /* tracing here only if verbose */
/* re-use the hash table space for the heap */
Heap = (link **) Table;
Hashpart = pack(0L, Tabsize - 1);
/* expunge penalties from -a option and make circular copy lists */
resetnodes();
if (firsttime++) {
if (Linkout && *Linkout) /* dump cheapest links */
showlinks();
if (Graphout && *Graphout) /* dump the edge list */
dumpgraph();
}
/* insert Home to get things started */
l = newlink(); /* link to get things started */
l->l_to = Home;
(void) dehash(Home);
insert(l);
/* main mapping loop */
remap:
Heaphighwater = Nheap;
while ((lparent = min_node()) != 0) {
chkheap(1);
lparent->l_flag |= LTREE;
n = lparent->l_to;
if (Tflag && maptrace(n, n))
fprintf(stderr, "%s -> %s mapped\n", n->n_parent->n_name, n->n_name);
if (n->n_flag & MAPPED)
die("mapped node in heap");
n->n_flag |= MAPPED;
/* add children to heap */
heapchildren(n);
}
vprintf(stderr, "heap high water mark was %d\n", Heaphighwater);
/* sanity check on implementation */
if (Nheap != 0)
die("null entry in heap");
if (Hashpart < Tabsize) {
/*
* add back links from unreachable hosts to
* reachable neighbors, then remap.
*
* asymptotically, this is quadratic; in
* practice, this is done once or twice.
*/
backlinks();
if (Nheap)
goto remap;
}
if (Hashpart < Tabsize) {
fputs("You can't get there from here:\n", stderr);
for ( ; Hashpart < Tabsize; Hashpart++) {
fprintf(stderr, "\t%s", Table[Hashpart]->n_name);
if (Table[Hashpart]->n_flag & ISPRIVATE)
fputs(" (private)", stderr);
putc('\n', stderr);
}
}
}
STATIC void
heapchildren(n)
register node *n;
{ register link *l;
register node *next;
register int mtrace;
register Cost cost;
for (l = n->n_link; l; l = l->l_next) {
next = l->l_to; /* neighboring node */
mtrace = Tflag && maptrace(n, next);
if (next->n_flag & MAPPED) {
if (mtrace)
mtracereport(n, l, "-\talready mapped");
continue;
}
if (l->l_flag & LTREE)
continue;
if (l->l_flag & LTERMINAL)
next = l->l_to = ncopy(next);
if ((n->n_flag & NTERMINAL) && (l->l_flag & LALIAS))
if (skipterminalalias(n, next))
continue;
else
next = l->l_to = ncopy(next);
cost = costof(n, l);
if (skiplink(l, n, cost, mtrace))
continue;
/*
* put this link in the heap and restore the
* heap property.
*/
if (mtrace) {
if (next->n_parent)
mtracereport(next->n_parent, l, "*\tdrop");
mtracereport(n, l, "+\tadd");
}
next->n_parent = n;
if (dehash(next) == 0) { /* first time */
next->n_cost = cost;
insert(l); /* insert at end */
heapup(l);
} else {
/* replace inferior path */
Heap[next->n_tloc] = l;
if (cost > next->n_cost) {
/* increase cost (gateway) */
next->n_cost = cost;
heapdown(l);
} else if (cost < next->n_cost) {
/* cheaper route */
next->n_cost = cost;
heapup(l);
}
}
setheapbits(l);
chkheap(1);
}
}
/* bizarro special case */
STATIC
skipterminalalias(n, next)
node *n, *next;
{ node *ncp;
while (n->n_flag & NALIAS) {
n = n->n_parent;
for (ncp = n->n_copy; ncp != n; ncp = ncp->n_copy)
if (next == ncp)
return 1;
}
return 0;
}
/*
* return 1 if we definitely don't want want this link in the
* shortest path tree, 0 if we might want it, i.e., best so far.
*
* if tracing is turned on, report only if this node is being skipped.
*/
STATIC int
skiplink(l, parent, cost, trace)
link *l; /* new link to this node */
node *parent; /* (potential) new parent of this node */
register Cost cost; /* new cost to this node */
int trace; /* trace this link? */
{ register node *n; /* this node */
register link *lheap; /* old link to this node */
n = l->l_to;
/* first time we've reached this node? */
if (n->n_tloc >= Hashpart)
return 0;
lheap = Heap[n->n_tloc];
/* examine links to nets that require gateways */
if (GATEWAYED(n)) {
/* if exactly one is a gateway, use it */
if ((lheap->l_flag & LGATEWAY) && !(l->l_flag & LGATEWAY)) {
if (trace)
mtracereport(parent, l, "-\told gateway");
return 1; /* old is gateway */
}
if (!(lheap->l_flag & LGATEWAY) && (l->l_flag & LGATEWAY))
return 0; /* new is gateway */
/* no gateway or both gateways; resolve in standard way ... */
}
/* examine dup link (sanity check) */
if (n->n_parent == parent && ((lheap->l_flag & LDEAD) || (l->l_flag & LDEAD)))
die("dup dead link");
/* examine cost */
if (cost < n->n_cost)
return 0;
if (cost > n->n_cost) {
if (trace)
mtracereport(parent, l, "-\tcheaper");
return 1;
}
/* all other things being equal, ask the oracle */
if (tiebreaker(n, parent)) {
if (trace)
mtracereport(parent, l, "-\ttiebreaker");
return 1;
}
return 0;
}
STATIC Cost
costof(prev, l)
register node *prev;
register link *l;
{ register node *next;
register Cost cost;
if (l->l_flag & LALIAS)
return prev->n_cost; /* by definition */
next = l->l_to;
cost = prev->n_cost + l->l_cost; /* basic cost */
/*
* heuristics:
* charge for a dead link.
* charge for getting past a terminal edge.
* charge for getting out of a dead host.
* charge for getting into a gatewayed net (except at a gateway).
* discourage mixing of left and right syntax when prev is a host.
*
* life was simpler when pathalias computed true shortest paths.
*/
if (l->l_flag & LDEAD) /* dead link */
cost += INF;
if (prev->n_flag & NTERMINAL) /* terminal parent */
cost += INF;
if (DEADHOST(prev)) /* dead host */
cost += INF;
if (GATEWAYED(next) && !(l->l_flag & LGATEWAY)) /* not gateway */
cost += INF;
if (!ISANET(prev)) { /* mixed syntax? */
if ((NETDIR(l) == LLEFT && (prev->n_flag & HASRIGHT))
|| (NETDIR(l) == LRIGHT && (prev->n_flag & HASLEFT)))
cost += INF;
}
return cost;
}
/* binary heap implementation of priority queue */
STATIC void
insert(l)
link *l;
{ register node *n;
n = l->l_to;
if (n->n_flag & MAPPED)
die("insert mapped node");
Heap[n->n_tloc] = 0;
if (Heap[Nheap+1] != 0)
die("heap error in insert");
if (Nheap++ == 0) {
Heap[1] = l;
n->n_tloc = 1;
return;
}
if (Vflag && Nheap > Heaphighwater)
Heaphighwater = Nheap; /* diagnostics */
/* insert at the end. caller must heapup(l). */
Heap[Nheap] = l;
n->n_tloc = Nheap;
}
/*
* "percolate" up the heap by exchanging with the parent. as in
* min_node(), give tiebreaker() a chance to produce better, stable
* routes by moving nets and domains close to the root, nets closer
* than domains.
*
* i know this seems obscure, but it's harmless and cheap. trust me.
*/
STATIC void
heapup(l)
link *l;
{ register long cindx, pindx; /* child, parent indices */
register Cost cost;
register node *child, *parent;
child = l->l_to;
cost = child->n_cost;
for (cindx = child->n_tloc; cindx > 1; cindx = pindx) {
pindx = cindx / 2;
if (Heap[pindx] == 0) /* sanity check */
die("impossible error in heapup");
parent = Heap[pindx]->l_to;
if (cost > parent->n_cost)
return;
/* net/domain heuristic */
if (cost == parent->n_cost) {
if (!ISANET(child))
return;
if (!ISADOMAIN(parent))
return;
if (ISADOMAIN(child))
return;
}
heapswap(cindx, pindx);
}
}
/* extract min (== Heap[1]) from heap */
STATIC link *
min_node()
{ link *rval, *lastlink;
register link **rheap;
if (Nheap == 0)
return 0;
rheap = Heap; /* in register -- heavily used */
rval = rheap[1]; /* return this one */
/* move last entry into root and reheap */
lastlink = rheap[Nheap];
rheap[Nheap] = 0;
if (--Nheap) {
rheap[1] = lastlink;
lastlink->l_to->n_tloc = 1;
heapdown(lastlink); /* restore heap property */
}
return rval;
}
/*
* swap Heap[i] with smaller child, iteratively down the tree.
*
* given the opportunity, attempt to place nets and domains
* near the root. this helps tiebreaker() shun domain routes.
*/
STATIC void
heapdown(l)
link *l;
{ register long pindx, cindx;
register link **rheap = Heap; /* in register -- heavily used */
node *child, *rchild, *parent;
pindx = l->l_to->n_tloc;
parent = rheap[pindx]->l_to; /* invariant */
for ( ; (cindx = pindx * 2) <= Nheap; pindx = cindx) {
/* pick lhs or rhs child */
child = rheap[cindx]->l_to;
if (cindx < Nheap) {
/* compare with rhs child */
rchild = rheap[cindx+1]->l_to;
/*
* use rhs child if smaller than lhs child.
* if equal, use rhs if net or domain.
*/
if (child->n_cost > rchild->n_cost) {
child = rchild;
cindx++;
} else if (child->n_cost == rchild->n_cost)
if (ISANET(rchild)) {
child = rchild;
cindx++;
}
}
/* child is the candidate for swapping */
if (parent->n_cost < child->n_cost)
break;
/*
* heuristics:
* move nets/domains up
* move nets above domains
*/
if (parent->n_cost == child->n_cost) {
if (!ISANET(child))
break;
if (ISANET(parent) && ISADOMAIN(child))
break;
}
heapswap(pindx, cindx);
}
}
/* exchange Heap[i] and Heap[j] pointers */
STATIC void
heapswap(i, j)
long i, j;
{ register link *temp, **rheap;
rheap = Heap; /* heavily used -- put in register */
temp = rheap[i];
rheap[i] = rheap[j];
rheap[j] = temp;
rheap[j]->l_to->n_tloc = j;
rheap[i]->l_to->n_tloc = i;
}
/* return 1 if n is already de-hashed (n_tloc < Hashpart), 0 o.w. */
STATIC int
dehash(n)
register node *n;
{
if (n->n_tloc < Hashpart)
return 1;
/* swap with entry in Table[Hashpart] */
Table[Hashpart]->n_tloc = n->n_tloc;
Table[n->n_tloc] = Table[Hashpart];
Table[Hashpart] = n;
n->n_tloc = Hashpart++;
return 0;
}
STATIC void
backlinks()
{ register link *l;
register node *n, *parent;
node *nomap, *ncp;
long i;
for (i = Hashpart; i < Tabsize; i++) {
nomap = Table[i];
for (ncp = nomap->n_copy; ncp != nomap; ncp = ncp->n_copy) {
if (ncp->n_flag & MAPPED) {
dehash(nomap);
Table[nomap->n_tloc] = 0;
nomap->n_tloc = 0;
goto nexti;
}
}
/* TODO: simplify this */
parent = 0;
for (l = nomap->n_link; l; l = l->l_next) {
n = l->l_to;
if (!(n->n_flag & MAPPED))
continue;
if (parent == 0) {
parent = n;
continue;
}
if (n->n_cost > parent->n_cost)
continue;
if (n->n_cost == parent->n_cost) {
nomap->n_parent = parent;
if (tiebreaker(nomap, n))
continue;
}
parent = n;
}
if (parent == 0)
continue;
(void) dehash(nomap);
l = addlink(parent, nomap, INF, DEFNET, DEFDIR);
nomap->n_parent = parent;
nomap->n_cost = costof(parent, l);
insert(l);
heapup(l);
nexti:
;
}
vprintf(stderr, "%d backlinks\n", Nheap);
}
STATIC void
setheapbits(l)
register link *l;
{ register node *n;
register node *parent;
n = l->l_to;
parent = n->n_parent;
n->n_flag &= ~(NALIAS|HASLEFT|HASRIGHT); /* reset */
/* record whether link is an alias */
if (l->l_flag & LALIAS) {
n->n_flag |= NALIAS;
if (parent->n_flag & NTERMINAL)
n->n_flag |= NTERMINAL;
}
/* set left/right bits */
if (NETDIR(l) == LLEFT || (parent->n_flag & HASLEFT))
n->n_flag |= HASLEFT;
if (NETDIR(l) == LRIGHT || (parent->n_flag & HASRIGHT))
n->n_flag |= HASRIGHT;
}
STATIC void
mtracereport(from, l, excuse)
node *from;
link *l;
char *excuse;
{ node *to = l->l_to;
fprintf(stderr, "%-16s ", excuse);
fputs(from->n_name, stderr);
if (from->n_flag & NTERMINAL)
putc('\'', stderr);
fputs(" -> ", stderr);
fputs(to->n_name, stderr);
if (to->n_flag & NTERMINAL)
putc('\'', stderr);
fprintf(stderr, " (%ld, %ld, %ld) ", from->n_cost, l->l_cost, to->n_cost);
if (to->n_parent) {
fputs(to->n_parent->n_name, stderr);
if (to->n_parent->n_flag & NTERMINAL)
putc('\'', stderr);
fprintf(stderr, " (%ld)", to->n_parent->n_cost);
}
putc('\n', stderr);
}
#if 0
chkheap(i)
{ int lhs, rhs;
lhs = i * 2;
rhs = lhs + 1;
if (lhs <= Nheap) {
if (Heap[i]->l_to->n_cost > Heap[lhs]->l_to->n_cost)
die("chkheap failure on lhs");
chkheap(lhs);
}
if (rhs <= Nheap) {
if (Heap[i]->l_to->n_cost > Heap[rhs]->l_to->n_cost)
die("chkheap failure on rhs");
chkheap(rhs);
}
#if 00
for (i = 1; i < Nheap; i++) {
link *l;
vprintf(stderr, "%5d %-16s", i, Heap[i]->l_to->n_name);
if ((l = Heap[i]->l_to->n_link) != 0) do {
vprintf(stderr, "%-16s", l->l_to->n_name);
} while ((l = l->l_next) != 0);
vprintf(stderr, "\n");
}
for (i = Hashpart; i < Tabsize; i++) {
link *l;
node *n;
vprintf(stderr, "%5d %-16s", i, Table[i]->n_name);
if ((l = Table[i]->n_link) != 0) do {
vprintf(stderr, "%-16s", l->l_to->n_name);
} while ((l = l->l_next) != 0);
vprintf(stderr, "\n");
}
#endif /*00*/
}
#endif /*0*/