|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T m
Length: 12827 (0x321b)
Types: TextFile
Names: »mapit.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec8/pathalias/mapit.c«
/* pathalias -- by steve bellovin, as told to peter honeyman */
#ifndef lint
static char *sccsid = "@(#)mapit.c 8.16 (down!honey) 86/09/19";
#endif
#include "def.h"
#ifndef DEBUG
#define chkheap(i) /* void */
#endif
/* exports */
extern void mapit();
/* invariant while mapping: Nheap < Slop < Hashpart */
long Hashpart; /* start of unreached nodes */
long Slop; /* start of mapped nodes */
long Nheap; /* end of heap */
/* imports */
extern void freelink(), resetnodes(), putaside(), printit(), dumpgraph();
extern void showlinks(), die();
extern long pack(), allocation();
extern link *newlink(), *addlink();
extern int maptrace();
extern long Nheap, Slop, Hashpart, Tabsize;
extern int Dflag, Tflag, Vflag;
extern node **Table, *Home;
extern char *Linkout, *Graphout;
/* privates */
STATIC void insert(), heapup(), heapdown(), heapswap(), backlinks();
STATIC void setheapbits(), mtracereport();
STATIC link *min_node();
STATIC int dehash(), skiplink();
STATIC Cost costof();
static long Heaphighwater;
static link **Heap;
/* hook for mtracereport() */
static char *Add = "add";
/* transform the graph to a shortest-path tree by marking tree edges */
void
mapit(phase)
{ register node *n, *next;
register link *l;
link *lparent;
Cost cost;
static int firsttime = 0;
int mtrace = 0;
vprintf(stderr, "*** PHASE %d mapping\n", phase);
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);
Slop = Hashpart - 1;
/* expunge penalties from -a option and costs from earlier phases */
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_name, n->n_parent->n_name);
if (n->n_flag & MAPPED)
die("mapped node in heap");
n->n_flag |= MAPPED;
/* add children to heap */
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, next, "already mapped");
continue;
}
cost = costof(n, l);
if (phase == NODOMAINS && ISADOMAIN(next))
cost += INF;
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, next, "drop");
mtracereport(n, next, Add);
}
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(n, l);
chkheap(1);
}
putaside(n); /* put it aside for the next phase */
}
vprintf(stderr, "heap high water mark was %d\n", Heaphighwater);
/* sanity check on implementation */
if (Nheap != 0)
die("null entry in heap");
if (Dflag || phase == NODOMAINS) {
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);
}
}
}
vprintf(stderr, "allocation is %ldk after mapping\n", allocation());
printit(phase); /* traverse tree and print paths */
vprintf(stderr, "allocation is %ldk after printing\n", allocation());
}
/*
* 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; /* new parent of this node */
Cost cost; /* new cost to this node */
int trace; /* link is being traced? */
{ register node *n; /* this node */
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, n, "old 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, n, "cheaper");
return(1);
}
/* all other things being equal, ask the oracle */
if (tiebreaker(n, parent)) {
if (trace)
mtracereport(parent, n, "tiebreaker");
return(1);
}
return(0);
}
STATIC Cost
costof(prev, l)
register node *prev;
register link *l;
{ register node *next;
register Cost cost;
node *alias;
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 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.
* charge for leaving a domain.
*
* life was simpler when pathalias computed true shortest paths.
*/
if (l->l_flag & LDEAD) /* dead link */
cost += INF;
if (DEADHOST(prev)) /* dead host */
cost += INF;
if (GATEWAYED(next) && !(l->l_flag & LGATEWAY)) /* not gateway */
cost += INF;
if (!ISANET(prev)) {
/* charge for mixed syntax here */
if ((NETDIR(l) == LLEFT && (prev->n_flag & HASRIGHT))
|| (NETDIR(l) == LRIGHT && (prev->n_flag & HASLEFT)))
cost += INF;
}
/*
* discourage links out of domains (except subdomains).
*/
for (alias = prev; alias->n_flag & NALIAS; alias = alias->n_parent)
;
if (alias->n_parent && ISADOMAIN(alias->n_parent) && !ISADOMAIN(alias))
cost += INF; /* heavyweight, but appropriate */
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 (Nheap+1 == Slop)
Slop = pack(Slop, Hashpart-1);
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()
{ link *l;
node *n, *parent, *nomap;
long i;
for (i = Hashpart; i < Tabsize; i++) {
nomap = Table[i];
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);
}
vprintf(stderr, "%d backlinks\n", Nheap);
}
STATIC void
setheapbits(prev, l)
register node *prev;
register link *l;
{ register node *next;
next = l->l_to;
next->n_flag &= ~(NALIAS|HASLEFT|HASRIGHT); /* reset */
/* record whether link is an alias */
if (l->l_flag & LALIAS)
next->n_flag |= NALIAS;
/* set left/right bits */
if (NETDIR(l) == LLEFT || (prev->n_flag & HASLEFT))
next->n_flag |= HASLEFT;
if (NETDIR(l) == LRIGHT || (prev->n_flag & HASRIGHT))
next->n_flag |= HASRIGHT;
}
STATIC void
mtracereport(from, to, excuse)
node *from, *to;
char *excuse;
{
if (excuse == Add) {
fprintf(stderr, "+ %s -> %s\n", from->n_name, to->n_name);
return;
}
fprintf(stderr, "- %s -> %s (%s)\n", from->n_name, to->n_name, excuse);
}
#ifdef DEBUG
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);
}
}
#endif