File:  [ELWIX - Embedded LightWeight unIX -] / libelwix / src / patricia.c
Revision 1.6: download - view: text, annotated - select for diffs - revision graph
Mon Jan 22 15:24:28 2024 UTC (10 months ago) by misho
Branches: MAIN
CVS tags: elwix6_6, elwix6_5, elwix6_4, elwix6_3, elwix6_2, elwix6_1, elwix5_9, elwix5_12, elwix5_11, elwix5_10, HEAD, ELWIX6_5, ELWIX6_4, ELWIX6_2, ELWIX6_1, ELWIX6_0, ELWIX5_9, ELWIX5_8, ELWIX5_11, ELWIX5_10
Version 5.8

/*************************************************************************
* (C) 2007 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
*  by Michael Pounov <misho@elwix.org>
*
* $Author: misho $
* $Id: patricia.c,v 1.6 2024/01/22 15:24:28 misho Exp $
*
**************************************************************************
The ELWIX and AITNET software is distributed under the following
terms:

All of the documentation and software included in the ELWIX and AITNET
Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>

Copyright 2004 - 2024
	by Michael Pounov <misho@elwix.org>.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
   must display the following acknowledgement:
This product includes software developed by Michael Pounov <misho@elwix.org>
ELWIX - Embedded LightWeight unIX and its contributors.
4. Neither the name of AITNET nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/
/*
 * This product includes software developed by the University of Michigan,
 * Merit Network, Inc., and their contributors. 
 *
 * This file had been called "radix.c" in the MRT sources.
 *
 * I renamed it to "patricia.c" since it's not an implementation of a general
 * radix trie.  Also I pulled in various requirements from "prefix.c" and
 * "demo.c" so that it could be used as a standalone API.
 *
 * Added and patched existing code by AITNET - Michael Pounov <misho@aitbg.com>
 */
#include "global.h"

#ifdef PATRICIA_SUPPORT
static int num_active_patricia;


/* { from prefix.c */
static inline int comp_with_mask(void *addr, void *dest, u_int mask)
{
	int n, m;

	if (/* mask/8 == 0 || */ !memcmp(addr, dest, mask / 8)) {
		n = mask / 8;
		m = ((-1) << (8 - (mask % 8)));

		if (!(mask % 8) || (((u_char*) addr)[n] & m) == (((u_char*) dest)[n] & m))
			return 1;
	}

	return 0;
}

/* this allows imcomplete prefix */
static int my_inet_pton(int af, const char *src, void *dst)
{
	int i, c, val;
	u_char xp[4] = { 0, 0, 0, 0 };

	if (af == AF_INET) {
		for (i = 0;; i++) {
			c = *src++;
			if (!isdigit(c))
				return -1;
			val = 0;
			do {
				val = val * 10 + c - '0';
				if (val > 255)
					return 0;
				c = *src++;
			} while (c && isdigit(c));

			xp[i] = val;
			if (!c)
				break;
			if (c != '.' || i >= 3)
				return 0;
		}

		memcpy(dst, xp, 4);
		return 1;
#ifdef HAVE_IPV6
	} else
		if (af == AF_INET6) {
			return inet_pton(af, src, dst);
#endif /* HAVE_IPV6 */
		} else {
			errno = EAFNOSUPPORT;
			return -1;
		}
}

/* 
 * convert prefix information to ascii string with length
 * thread safe and (almost) re-entrant implementation
 */
static char *prefix_toa2x(prefix_t *prefix, char *buff, int with_len)
{
	struct buffer {
		char buffs[16][48 + 5];
		u_int i;
	} *buffp;
	u_char *a;

	if (!prefix)
		return "(Null)";
	assert(prefix->ref_count >= 0);
	if (!buff) {
#if 0
		THREAD_SPECIFIC_DATA(struct buffer, buffp, 1);
#else
		{ /* for scope only */
			static struct buffer local_buff;
			buffp = &local_buff;
		}
#endif
		if (!buffp) {
			/* XXX should we report an error? */
			return NULL;
		}

		buff = buffp->buffs[buffp->i++ % 16];
	}
	if (prefix->family == AF_INET) {
		assert (prefix->bitlen <= 32);
		a = prefix_touchar(prefix);
		if (with_len)
			snprintf(buff, with_len, "%d.%d.%d.%d/%d", a[0], a[1], a[2], 
					a[3], prefix->bitlen);
		else
			snprintf(buff, 16, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
		return buff;
	}
#ifdef HAVE_IPV6
	else
		if (prefix->family == AF_INET6) {
			a = (char*) inet_ntop(AF_INET6, &prefix->add.sin6, buff, 48 /* a guess value */);
			if (a && with_len) {
				assert(prefix->bitlen <= 128);
				snprintf(buff + strlen(buff), with_len - strlen(buff), 
						"/%d", prefix->bitlen);
			}
			return buff;
		}
#endif /* HAVE_IPV6 */
		else
			return NULL;
}

static prefix_t *New_Prefix2(int family, void *dest, int bitlen, prefix_t *prefix)
{
	int dynamic_allocated = 0;
	int default_bitlen = 32;

#ifdef HAVE_IPV6
	if (family == AF_INET6) {
		default_bitlen = 128;
		if (!prefix) {
			prefix = e_calloc(1, sizeof(prefix6_t));
			dynamic_allocated++;
		}
		memcpy(&prefix->add.sin6, dest, 16);
	} else
#endif /* HAVE_IPV6 */
		if (family == AF_INET) {
			if (!prefix) {
				prefix = e_calloc(1, sizeof(prefix4_t));
				dynamic_allocated++;
			}
			memcpy(&prefix->add.sin, dest, 4);
		} else
			return NULL;

	prefix->bitlen = (bitlen >= 0) ? bitlen : default_bitlen;
	prefix->family = family;
	prefix->ref_count = 0;
	if (dynamic_allocated)
		prefix->ref_count++;

	return prefix;
}

static inline prefix_t *New_Prefix(int family, void *dest, int bitlen)
{
    return New_Prefix2(family, dest, bitlen, NULL);
}

// ------------------------------------------------------------------------------------

char *prefix_toa(prefix_t * prefix)
{
    return prefix_toa2x(prefix, NULL, 0);
}

prefix_t *ascii2prefix(int family, char *string)
{
	u_long bitlen, maxbitlen = 0;
	char *cp;
	struct in_addr sin;
#ifdef HAVE_IPV6
	struct in6_addr sin6;
#endif /* HAVE_IPV6 */
	int result;
	char save[MAXLINE];

	if (!string)
		return (NULL);

	/* easy way to handle both families */
	if (!family) {
		family = AF_INET;
#ifdef HAVE_IPV6
		if (strchr(string, ':'))
			family = AF_INET6;
#endif /* HAVE_IPV6 */
	}

	if (family == AF_INET)
		maxbitlen = 32;
#ifdef HAVE_IPV6
	else
		if (family == AF_INET6)
			maxbitlen = 128;
#endif /* HAVE_IPV6 */

	if ((cp = strchr(string, '/'))) {
		bitlen = atol(cp + 1);
		/* *cp = '\0'; */
		/* copy the string to save. Avoid destroying the string */
		assert(cp - string < MAXLINE);
		memcpy(save, string, cp - string);
		save[cp - string] = 0;
		string = save;
		if (bitlen > maxbitlen)
			bitlen = maxbitlen;
	} else
		bitlen = maxbitlen;

	if (family == AF_INET) {
		if ((result = my_inet_pton(AF_INET, string, &sin)) <= 0)
			return NULL;
		return New_Prefix(AF_INET, &sin, bitlen);
	}
#ifdef HAVE_IPV6
	else
		if (family == AF_INET6) {
			if ((result = inet_pton(AF_INET6, string, &sin6)) <= 0)
				return NULL;
			return New_Prefix(AF_INET6, &sin6, bitlen);
		}
#endif /* HAVE_IPV6 */
		else
			return NULL;
}

prefix_t *Ref_Prefix(prefix_t *prefix)
{
	if (!prefix)
		return NULL;

	if (!prefix->ref_count) {
		/* make a copy in case of a static prefix */
		return New_Prefix2(prefix->family, &prefix->add, prefix->bitlen, NULL);
	}
	prefix->ref_count++;

	return prefix;
}

void Deref_Prefix(prefix_t *prefix)
{
	if (!prefix)
		return;

	/* for secure programming, raise an assert. no static prefix can call this */
	assert(prefix->ref_count > 0);
	prefix->ref_count--;
	assert(prefix->ref_count >= 0);
	if (prefix->ref_count <= 0)
		Delete(prefix);
}

/* } */

/* these routines support continuous mask only */
patricia_tree_t *New_Patricia(int maxbits)
{
	patricia_tree_t *patricia;

	patricia = e_calloc(1, sizeof *patricia);

	patricia->maxbits = maxbits;
	patricia->head = NULL;
	patricia->num_active_node = 0;

	assert(maxbits <= PATRICIA_MAXBITS); /* XXX */
	num_active_patricia++;

	return patricia;
}


/*
 * if func is supplied, it will be called as func(node->data)
 * before deleting the node
 */
void Clear_Patricia(patricia_tree_t *patricia, void_fn_t func)
{
	assert(patricia);
	if (patricia->head) {
		patricia_node_t *Xstack[PATRICIA_MAXBITS+1];
		patricia_node_t **Xsp = Xstack;
		patricia_node_t *Xrn = patricia->head;

		while (Xrn) {
			patricia_node_t *l = Xrn->l;
			patricia_node_t *r = Xrn->r;

			if (Xrn->prefix) {
				Deref_Prefix(Xrn->prefix);
				if (Xrn->data && func)
					func(Xrn->data);
			} else
				assert(!Xrn->data);
			Delete(Xrn);
			patricia->num_active_node--;

			if (l) {
				if (r)
					*Xsp++ = r;
				Xrn = l;
			} else {
				if (r)
					Xrn = r;
				else
					Xrn = (Xsp != Xstack) ? *(--Xsp) : NULL;
			}
		}
	}

	assert (!patricia->num_active_node);
	/* Delete(patricia); */
}

void Destroy_Patricia(patricia_tree_t *patricia, void_fn_t func)
{
	Clear_Patricia(patricia, func);
	Delete(patricia);
	num_active_patricia--;
}


/*
 * if func is supplied, it will be called as func(node->prefix, node->data)
 */
void patricia_process(patricia_tree_t *patricia, void_fn_t func)
{
	patricia_node_t *node;

	assert(func);

	PATRICIA_WALK(patricia->head, node) {
		func(node->prefix, node->data);
	} PATRICIA_WALK_END;
}


patricia_node_t *patricia_search_exact(patricia_tree_t *patricia, prefix_t *prefix)
{
	patricia_node_t *node;
	u_char *addr;
	u_int bitlen;

	assert(patricia);
	assert(prefix);
	assert(prefix->bitlen <= patricia->maxbits);

	if (!patricia->head)
		return (NULL);

	node = patricia->head;
	addr = prefix_touchar(prefix);
	bitlen = prefix->bitlen;

	while (node->bit < bitlen) {
		if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) {
#ifdef PATRICIA_DEBUG
			if (node->prefix)
				fprintf(stderr, "patricia_search_exact: take right %s/%d\n", 
						prefix_toa(node->prefix), node->prefix->bitlen);
			else
				fprintf(stderr, "patricia_search_exact: take right at %d\n", 
						node->bit);
#endif /* PATRICIA_DEBUG */
			node = node->r;
		} else {
#ifdef PATRICIA_DEBUG
			if (node->prefix)
				fprintf(stderr, "patricia_search_exact: take left %s/%d\n", 
						prefix_toa(node->prefix), node->prefix->bitlen);
			else
				fprintf(stderr, "patricia_search_exact: take left at %d\n", 
						node->bit);
#endif /* PATRICIA_DEBUG */
			node = node->l;
		}

		if (!node)
			return NULL;
	}

#ifdef PATRICIA_DEBUG
	if (node->prefix)
		fprintf(stderr, "patricia_search_exact: stop at %s/%d\n", 
				prefix_toa(node->prefix), node->prefix->bitlen);
	else
		fprintf(stderr, "patricia_search_exact: stop at %d\n", node->bit);
#endif /* PATRICIA_DEBUG */
	if (node->bit > bitlen || !node->prefix)
		return NULL;
	assert(node->bit == bitlen);
	assert(node->bit == node->prefix->bitlen);
	if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), bitlen)) {
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_search_exact: found %s/%d\n", 
				prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
		return node;
	}

	return NULL;
}


/* if inclusive != 0, "best" may be the given prefix itself */
patricia_node_t *patricia_search_best2(patricia_tree_t *patricia, prefix_t *prefix, int inclusive)
{
	patricia_node_t *node;
	patricia_node_t *stack[PATRICIA_MAXBITS + 1];
	u_char *addr;
	u_int bitlen;
	int cnt = 0;

	assert(patricia);
	assert(prefix);
	assert(prefix->bitlen <= patricia->maxbits);

	if (!patricia->head)
		return NULL;

	node = patricia->head;
	addr = prefix_touchar(prefix);
	bitlen = prefix->bitlen;

	while (node->bit < bitlen) {
		if (node->prefix) {
#ifdef PATRICIA_DEBUG
			fprintf(stderr, "patricia_search_best: push %s/%d\n", 
					prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
			stack[cnt++] = node;
		}

		if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) {
#ifdef PATRICIA_DEBUG
			if (node->prefix)
				fprintf(stderr, "patricia_search_best: take right %s/%d\n", 
						prefix_toa(node->prefix), node->prefix->bitlen);
			else
				fprintf(stderr, "patricia_search_best: take right at %d\n", 
						node->bit);
#endif /* PATRICIA_DEBUG */
			node = node->r;
		} else {
#ifdef PATRICIA_DEBUG
			if (node->prefix)
				fprintf(stderr, "patricia_search_best: take left %s/%d\n", 
						prefix_toa(node->prefix), node->prefix->bitlen);
			else
				fprintf(stderr, "patricia_search_best: take left at %d\n", 
						node->bit);
#endif /* PATRICIA_DEBUG */
			node = node->l;
		}

		if (!node)
			break;
	}

	if (inclusive && node && node->prefix)
		stack[cnt++] = node;

#ifdef PATRICIA_DEBUG
	if (!node)
		fprintf(stderr, "patricia_search_best: stop at null\n");
	else
		if (node->prefix)
			fprintf(stderr, "patricia_search_best: stop at %s/%d\n", 
					prefix_toa(node->prefix), node->prefix->bitlen);
		else
			fprintf(stderr, "patricia_search_best: stop at %d\n", node->bit);
#endif /* PATRICIA_DEBUG */

	if (cnt <= 0)
		return NULL;

	while (--cnt >= 0) {
		node = stack[cnt];
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_search_best: pop %s/%d\n", 
				prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
		if (comp_with_mask(prefix_touchar(node->prefix), prefix_touchar(prefix), 
					node->prefix->bitlen)) {
#ifdef PATRICIA_DEBUG
			fprintf(stderr, "patricia_search_best: found %s/%d\n", 
					prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
			return node;
		}
	}

	return NULL;
}

patricia_node_t *patricia_search_best(patricia_tree_t *patricia, prefix_t *prefix)
{
    return patricia_search_best2(patricia, prefix, 1);
}


patricia_node_t *patricia_lookup(patricia_tree_t *patricia, prefix_t *prefix)
{
	patricia_node_t *node, *new_node, *parent, *glue;
	u_char *addr, *test_addr;
	u_int bitlen, check_bit, differ_bit;
	int i, j, r;

	assert(patricia);
	assert(prefix);
	assert(prefix->bitlen <= patricia->maxbits);

	if (!patricia->head) {
		node = e_calloc(1, sizeof *node);
		node->bit = prefix->bitlen;
		node->prefix = Ref_Prefix(prefix);
		node->parent = NULL;
		node->l = node->r = NULL;
		node->data = NULL;
		patricia->head = node;
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_lookup: new_node #0 %s/%d (head)\n", 
				prefix_toa(prefix), prefix->bitlen);
#endif /* PATRICIA_DEBUG */
		patricia->num_active_node++;
		return node;
	}

	addr = prefix_touchar(prefix);
	bitlen = prefix->bitlen;
	node = patricia->head;

	while (node->bit < bitlen || !node->prefix) {
		if (node->bit < patricia->maxbits &&
				BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) {
			if (!node->r)
				break;
#ifdef PATRICIA_DEBUG
			if (node->prefix)
				fprintf(stderr, "patricia_lookup: take right %s/%d\n", 
						prefix_toa(node->prefix), node->prefix->bitlen);
			else
				fprintf(stderr, "patricia_lookup: take right at %d\n", node->bit);
#endif /* PATRICIA_DEBUG */
			node = node->r;
		} else {
			if (!node->l)
				break;
#ifdef PATRICIA_DEBUG
			if (node->prefix)
				fprintf(stderr, "patricia_lookup: take left %s/%d\n", 
						prefix_toa(node->prefix), node->prefix->bitlen);
			else
				fprintf(stderr, "patricia_lookup: take left at %d\n", node->bit);
#endif /* PATRICIA_DEBUG */
			node = node->l;
		}

		assert(node);
	}

	assert(node->prefix);
#ifdef PATRICIA_DEBUG
	fprintf(stderr, "patricia_lookup: stop at %s/%d\n", 
			prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */

	test_addr = prefix_touchar(node->prefix);
	/* find the first bit different */
	check_bit = (node->bit < bitlen) ? node->bit : bitlen;
	differ_bit = 0;
	for (i = 0; i * 8 < check_bit; i++) {
		if (!(r = (addr[i] ^ test_addr[i]))) {
			differ_bit = (i + 1) * 8;
			continue;
		}
		/* I know the better way, but for now */
		for (j = 0; j < 8; j++) {
			if (BIT_TEST(r, (0x80 >> j)))
				break;
		}
		/* must be found */
		assert(j < 8);
		differ_bit = i * 8 + j;
		break;
	}

	if (differ_bit > check_bit)
		differ_bit = check_bit;
#ifdef PATRICIA_DEBUG
	fprintf(stderr, "patricia_lookup: differ_bit %d\n", differ_bit);
#endif /* PATRICIA_DEBUG */

	parent = node->parent;
	while (parent && parent->bit >= differ_bit) {
		node = parent;
		parent = node->parent;
#ifdef PATRICIA_DEBUG
		if (node->prefix)
			fprintf(stderr, "patricia_lookup: up to %s/%d\n", 
					prefix_toa(node->prefix), node->prefix->bitlen);
		else
			fprintf(stderr, "patricia_lookup: up to %d\n", node->bit);
#endif /* PATRICIA_DEBUG */
	}

	if (differ_bit == bitlen && node->bit == bitlen) {
		if (node->prefix) {
#ifdef PATRICIA_DEBUG 
			fprintf(stderr, "patricia_lookup: found %s/%d\n", 
					prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
			return node;
		}
		node->prefix = Ref_Prefix(prefix);
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_lookup: new node #1 %s/%d (glue mod)\n",
				prefix_toa(prefix), prefix->bitlen);
#endif /* PATRICIA_DEBUG */
		assert(!node->data);
		return node;
	}

	new_node = e_calloc(1, sizeof *new_node);
	new_node->bit = prefix->bitlen;
	new_node->prefix = Ref_Prefix (prefix);
	new_node->parent = NULL;
	new_node->l = new_node->r = NULL;
	new_node->data = NULL;
	patricia->num_active_node++;

	if (node->bit == differ_bit) {
		new_node->parent = node;
		if (node->bit < patricia->maxbits &&
				BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) {
			assert(!node->r);
			node->r = new_node;
		} else {
			assert(!node->l);
			node->l = new_node;
		}
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_lookup: new_node #2 %s/%d (child)\n", 
				prefix_toa(prefix), prefix->bitlen);
#endif /* PATRICIA_DEBUG */
		return new_node;
	}

	if (bitlen == differ_bit) {
		if (bitlen < patricia->maxbits &&
				BIT_TEST(test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07)))
			new_node->r = node;
		else
			new_node->l = node;
		new_node->parent = node->parent;
		if (!node->parent) {
			assert(patricia->head == node);
			patricia->head = new_node;
		} else
			if (node->parent->r == node)
				node->parent->r = new_node;
			else
				node->parent->l = new_node;
		node->parent = new_node;
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_lookup: new_node #3 %s/%d (parent)\n", 
				prefix_toa(prefix), prefix->bitlen);
#endif /* PATRICIA_DEBUG */
	} else {
		glue = e_calloc(1, sizeof *glue);
		glue->bit = differ_bit;
		glue->prefix = NULL;
		glue->parent = node->parent;
		glue->data = NULL;
		patricia->num_active_node++;

		if (differ_bit < patricia->maxbits &&
				BIT_TEST(addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) {
			glue->r = new_node;
			glue->l = node;
		} else {
			glue->r = node;
			glue->l = new_node;
		}
		new_node->parent = glue;

		if (!node->parent) {
			assert(patricia->head == node);
			patricia->head = glue;
		} else
			if (node->parent->r == node)
				node->parent->r = glue;
			else
				node->parent->l = glue;
		node->parent = glue;
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_lookup: new_node #4 %s/%d (glue+node)\n", 
				prefix_toa(prefix), prefix->bitlen);
#endif /* PATRICIA_DEBUG */
	}

	return new_node;
}


void patricia_remove(patricia_tree_t *patricia, patricia_node_t *node)
{
	patricia_node_t *parent, *child;

	assert(patricia);
	assert(node);

	if (node->r && node->l) {
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_remove: #0 %s/%d (r & l)\n", 
				prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
	
		/* this might be a placeholder node -- have to check and make sure
		 * there is a prefix aossciated with it ! */
		if (node->prefix) 
			Deref_Prefix(node->prefix);
		node->prefix = NULL;
		/* Also I needed to clear data pointer -- masaki */
		node->data = NULL;
		return;
	}

	if (!node->r && !node->l) {
#ifdef PATRICIA_DEBUG
		fprintf(stderr, "patricia_remove: #1 %s/%d (!r & !l)\n", 
				prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
		parent = node->parent;
		Deref_Prefix(node->prefix);
		Delete(node);
		patricia->num_active_node--;

		if (!parent) {
			assert(patricia->head == node);
			patricia->head = NULL;
			return;
		}

		if (parent->r == node) {
			parent->r = NULL;
			child = parent->l;
		} else {
			assert(parent->l == node);
			parent->l = NULL;
			child = parent->r;
		}

		if (parent->prefix)
			return;

		/* we need to remove parent too */
		if (!parent->parent) {
			assert(patricia->head == parent);
			patricia->head = child;
		} else
			if (parent->parent->r == parent)
				parent->parent->r = child;
			else {
				assert(parent->parent->l == parent);
				parent->parent->l = child;
			}
		child->parent = parent->parent;
		Delete(parent);
		patricia->num_active_node--;
		return;
	}

#ifdef PATRICIA_DEBUG
	fprintf(stderr, "patricia_remove: #2 %s/%d (r ^ l)\n", 
			prefix_toa(node->prefix), node->prefix->bitlen);
#endif /* PATRICIA_DEBUG */
	if (node->r)
		child = node->r;
	else {
		assert(node->l);
		child = node->l;
	}
	parent = node->parent;
	child->parent = parent;

	Deref_Prefix(node->prefix);
	Delete(node);
	patricia->num_active_node--;

	if (!parent) {
		assert(patricia->head == node);
		patricia->head = child;
		return;
	}

	if (parent->r == node)
		parent->r = child;
	else {
		assert(parent->l == node);
		parent->l = child;
	}
}

/* { from demo.c */

void lookup_then_remove(patricia_tree_t *tree, char *string)
{
	patricia_node_t *node;

	if ((node = try_search_exact(tree, string)))
		patricia_remove(tree, node);
}

patricia_node_t *make_and_lookup(patricia_tree_t *tree, char *string)
{
	prefix_t *prefix;
	patricia_node_t *node;

	prefix = ascii2prefix(AF_INET, string);
#ifdef PATRICIA_DEBUG
	printf("make_and_lookup: %s/%d\n", prefix_toa(prefix), prefix->bitlen);
#endif
	node = patricia_lookup(tree, prefix);
	Deref_Prefix(prefix);

	return node;
}

patricia_node_t *try_search_exact(patricia_tree_t *tree, char *string)
{
	prefix_t *prefix;
	patricia_node_t *node;

	prefix = ascii2prefix(AF_INET, string);
#ifdef PATRICIA_DEBUG
	printf("try_search_exact: %s/%d\n", prefix_toa(prefix), prefix->bitlen);
#endif
	node = patricia_search_exact(tree, prefix);
#ifdef PATRICIA_DEBUG
	if (!node)
		printf("try_search_exact: not found\n");
	else
		printf("try_search_exact: %s/%d found\n", prefix_toa(node->prefix), node->prefix->bitlen);
#endif
	Deref_Prefix(prefix);

	return node;
}

patricia_node_t *try_search_best(patricia_tree_t *tree, char *string)
{
	prefix_t *prefix;
	patricia_node_t *node;

	prefix = ascii2prefix(AF_INET, string);
#ifdef PATRICIA_DEBUG
	printf("try_search_best: %s/%d\n", prefix_toa(prefix), prefix->bitlen);
#endif
	node = patricia_search_best(tree, prefix);
#ifdef PATRICIA_DEBUG
	if (!node)
		printf("try_search_best: not found\n");
	else
		printf("try_search_best: %s/%d found\n", prefix_toa(node->prefix), node->prefix->bitlen);
#endif
	Deref_Prefix(prefix);

	return node;
}

/* } */
#endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>