4.3BSD/usr/contrib/pathalias/mapit.c

Compare this file to the similar file:
Show the results in this format:

/* pathalias -- by steve bellovin, as told to peter honeyman */
#ifndef lint
static char	*sccsid = "@(#)mapit.c	8.1 (down!honey) 86/01/19";
#endif

#include "def.h"

/* privates */
extern void	reheap(), insert(), heapswap();
extern link	*min_node(), *rmlink();
extern Cost	costof();

static long	Nheap;
static long	Heaphighwater;
static link	**Heap;


/* transform the graph to a shortest-path tree by marking tree edges */

mapit()
{
	register node *n, *next;
	register link *l;
	link	*lprev, *lnext;
	Cost cost;

	/*
	 * re-use the hash table space for the heap.
	 */
	Heap = (link **) Table;

	pack();		/* remove holes in the Table */
	if (Linkout && *Linkout)	/* dump cheapest links */
		showlinks();
	if (Graphout && *Graphout)	/* dump the edge list */
		dumpgraph();

	/* invent and insert a link for Home to get things started */
	l = newlink();
	l->l_to = Home;
	(void) dehash(Home);
	insert(l);

	/* main mapping loop */
remap:
	Heaphighwater = Nheap;
	while ((l = min_node()) != 0) {
		l->l_flag |= LTREE;
		n = l->l_to;
		n->n_flag |= MAPPED;
 
		/* add children to heap */
		lprev = 0;
		for (l = n->n_link; l != 0; l = lnext) {

			next = l->l_to;		/* neighboring node */
			if (next->n_flag & MAPPED) {
				lnext = rmlink(l, lprev, n);
				continue;
			}
			cost = costof(n, l);

			if (skiplink(l, n, cost)) {
				lnext = rmlink(l, lprev, n);
				continue;
			}

			/*
			 * put this link in the heap, in a place where it may
			 * percolate up, but not down.  if new, or if cost is
			 * being increased, move to end.  otherwise, cost is
			 * same or less, so leave it where it is.  unfortunately,
			 * freeing a link already in the heap is too costly at
			 * this point.
			 *
			 * TODO: avoid heaping aliases and network members.
			 */
			if (dehash(next) == 0)	/* first time in heap */
				insert(l);	/* insert at end */
			else {
				/* replace heaped link by this one */
				if (cost > next->n_cost) {	/* gateway */
					/* move old link to end of heap */
					heapswap((long) (next->n_tloc), Nheap);
					next->n_tloc = Nheap;
				}
				Heap[next->n_tloc] = l;
			}
				
			next->n_cost = cost;
			next->n_parent = n;
			reheap(l);		/* restore heap property */

			/*
			 * note whether we got here via a gatewayed net.
			 * domains are presumed to require gateways.
			 * aliases inherit parent's gateway status.
			 */
			next->n_flag &= ~GATEWAYIN;
			if (l->l_flag & LALIAS)
				next->n_flag |= (n->n_flag & GATEWAYIN);
			else if (GATEWAYED(n))
				next->n_flag |= GATEWAYIN;
			lprev = l;	/* this link's a keeper */
			lnext = l->l_next;
		}

	}
	vprintf(stderr, "heap high water mark was %d\n", Heaphighwater);

	/* sanity check on implementation */
	if (Nheap != 0) {
		fprintf(stderr, "%s: null entry found in heap\n", ProgName);
		badmagic(1);
	}

	if (Hashpart < Tabsize) {
		/*
		 * add back links from unreachable hosts to reachable
		 * neighbors, then remap.  asymptotically, this is
		 * quadratic.  in practice, this is done exactly once.
		 */
		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|COLLISION))
				fputs(" (private)", stderr);
			putc('\n', stderr);
		}
	}
}

/*
 * can this link be ignored?  if yes, return 1, o.w. 0.
 * a link can be skipped if it is not in the shortest path tree.
 */
STATIC int
skiplink(l, parent, cost)
link	*l;			/* new link to this node */
node	*parent;		/* new parent of this node */
Cost	cost;			/* new cost to this node */
{
	node	*n;		/* this node */
	link	*lheap;		/* existing 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))
			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))) {
		fprintf(stderr, "%s: dup dead link not eliminated: %s -> %s\n",
			ProgName, parent->n_name, n->n_name);
		badmagic(1);
	}


	/*  examine cost */
	if (cost < n->n_cost)
		return(0);
	if (cost > n->n_cost)
		return(1);

	/* all other things being equal, consult the oracle */
	return(tiebreaker(n, parent));
}

STATIC Cost
costof(prev, l)
register node	*prev;
register link	*l;
{
	register node	*next;
	register Cost	cost;

	next = l->l_to;

	if (l->l_flag & LALIAS) {
		/* copy left/right bits */
		next->n_flag &= ~(HASLEFT|HASRIGHT);
		next->n_flag |= (prev->n_flag & (HASLEFT|HASRIGHT));
		return(prev->n_cost);	/* by definition */
	}

		
	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 next is a host.
	 *    charge for leaving a gatewayed net.
	 *
	 * life was simpler when pathalias computed true shortest paths.
	 */
	if (l->l_flag & LDEAD)		/* dead link */
		cost += INF/2;
	if (DEADHOST(prev))		/* dead host */
		cost += INF/2;
	if (GATEWAYED(next) && !(l->l_flag & LGATEWAY))	/* not gateway */
		cost += INF/2;
	if (!ISANET(next)) {
		/* charge for mixed syntax here */
		if ((NETDIR(l) == LLEFT && (prev->n_flag & HASRIGHT))
		 || (NETDIR(l) == LRIGHT && (prev->n_flag & HASLEFT)))
			cost += DEFCOST;
	}
	/*
	 * if reached by a gatewayed net, discourage further links.
	 * this has some relevance to common carriers and the FCC ...
	 * the penalty inheres to hosts, not aliases, nets, or domains.
	 */
	if ((prev->n_flag & GATEWAYIN) && !ISADOMAIN(prev) && !(prev->n_flag & NNET))
		cost += INF/2;	/* heavyweight, but appropriate */

	/* set left/right bits */
	next->n_flag &= ~(HASLEFT|HASRIGHT);
	if (NETDIR(l) == LLEFT || (prev->n_flag & HASLEFT))
		next->n_flag |= HASLEFT;
	if (NETDIR(l) == LRIGHT || (prev->n_flag & HASRIGHT))
		next->n_flag |= HASRIGHT;

	return(cost);
}

STATIC link *
rmlink(l, lprev, n)
link	*l, *lprev;
node	*n;
{
	link	*lnext;

	lnext = l->l_next;
	if (lprev)
		lprev->l_next = l->l_next;
	else
		n->n_link = l->l_next;
	free((char *) l);
	return(lnext);
}

/*
 * binary heap implementation of priority queue.
 * TODO: make the heap smaller by giving inserting a placeholder
 * for net members when the net is extracted.  this requires storing the
 * cost of a net in the net node itself -- yuck.  is it worth it?
 */

STATIC void
insert(l)
link	*l;
{
	register node	*n;

	n = l->l_to;
	Heap[n->n_tloc] = 0;
	if (Heap[Nheap+1] != 0) {
		fprintf(stderr, "%s: heap error in insert\n", ProgName);
		badmagic(1);
	}
	if (Nheap++ == 0) {
		Heap[1] = l;
		n->n_tloc = 1;
		return;
	}
	if (Vflag && Nheap > Heaphighwater)
		Heaphighwater = Nheap;	/* diagnostics */

	/* insert at the end.  caller must reheap(). */
	Heap[Nheap] = l;
	n->n_tloc = Nheap;
}

/*
 * replace existing link in heap by this one, then
 * "percolate" up the heap by exchanging with the parent
 */
STATIC void
reheap(l)
link	*l;
{
	register long	loc, parent;
	register Cost	cost;
	register node	*n, *np;

	n = l->l_to;

	cost = n->n_cost;
	for (loc = n->n_tloc; loc > 1; loc = parent) {
		parent = loc / 2;
		/* sanity check on implementation */
		if (Heap[parent] == 0) {
			fprintf(stderr, "%s: heap error in insert\n", ProgName);
			badmagic(1);
		}
		np = Heap[parent]->l_to;
		if (cost > np->n_cost)
			return;
		/* move nets below hosts for output stability */
		if (cost == np->n_cost && ((n->n_flag & NNET) || !(np->n_flag & NNET)))
			return;
		heapswap(loc, parent);
	}
}

/* extract min (== Heap[1]) from heap */
STATIC link	*
min_node()
{
	link *rval;
	register link **regheap;
	register long	i, child;
	
	if (Nheap == 0)
		return(0);

	regheap = Heap;		/* in register -- heavily used */
	rval = regheap[1];	/* return this one */
			
	/* move last entry into root, percolate down */
	regheap[1] = regheap[Nheap];
	regheap[1]->l_to->n_tloc = 1;
	regheap[Nheap] = 0;
	if (--Nheap == 0)
		return(rval);

	i = 1;
	for (;;) {
		/* swap with smaller child down the tree */
		child = i * 2;	/* lhs child is 2i, rhs is 2i+1. */
		if (child >= Nheap)
			return(rval);
		/* use rhs child if smaller than lhs child */
		if (regheap[child]->l_to->n_cost > regheap[child+1]->l_to->n_cost
		 || (regheap[child]->l_to->n_cost == regheap[child+1]->l_to->n_cost
		  && !ISANET(regheap[child+1]->l_to)))
			child++;
			
		if (regheap[i]->l_to->n_cost < regheap[child]->l_to->n_cost)
			return(rval);
		/* move nets below hosts for output stability */
		if (regheap[i]->l_to->n_cost == regheap[child]->l_to->n_cost
		 && (!ISANET(regheap[i]->l_to) || ISANET(regheap[child]->l_to)))
			return(rval);
		heapswap(i, child);
		i = child;
	}
	/*NOTREACHED*/
}

/* exchange Heap[i] and Heap[j] pointers */
STATIC void
heapswap(i, j)
long	i, j;
{
	register link	*temp, **regheap;

	regheap = Heap;	/* heavily used -- put in register */
	temp = regheap[i];
	regheap[i] = regheap[j];
	regheap[j] = temp;
	regheap[j]->l_to->n_tloc = j;
	regheap[i]->l_to->n_tloc = i;
}

/* return 1 if n is already de-hashed (n_tloc < Hashpart), 0 o.w. */
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);
}

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);
		reheap(l);
	}
	vprintf(stderr, "%d backlinks\n", Nheap);
}