/*
 * Copyright (c) 2003 Research Engineering Development, Inc.
 * Author: Alfred Perlstein <alfred@FreeBSD.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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 * $Id: mtrie.c,v 1.6 2003/07/01 08:01:17 bright Exp $
 *
 */
#include <stdlib.h>
#include <sys/types.h>
#if !defined(__FreeBSD__) || __FreeBSD_version >= 500000
#include <stdint.h>
#endif

#include "mtrie.h"

#ifdef DEBUG
#include <stdio.h>
#endif

#ifdef __unused
#define UNUSED(x) x __unused
#else
#define UNUSED(x) x
#endif

/*
 * Head object, just a reference to the first 'row'.
 */
struct mtrie {
	struct ment	*m_trie;
};

/*
 * These are allocated in chunks of 256.
 * The 'mask' portion is required because we need to only overwrite
 * entries that are less specific.  Meaning data for a /6 overrides data
 * for a /4.
 */
struct ment {
	struct ment	*me_next;	/* next octect or NULL. */
	void		*me_data;	/* data or NULL. */
	int		me_mask;	/* mask data was entered with. */
};

#define MTRIE_SIZE	256

static void	ment_free(struct ment *ment);
static int	poweroftwo(int x);
/*
 * std allocator.
 */
int
mtrie_alloc(mtrie_t *m, UNUSED(int flags))
{

	*m = calloc(1, sizeof(**m));
	return (*m == NULL ? -1 : 0);
}

void
mtrie_free(mtrie_t m)
{

	if (m->m_trie)
		ment_free(m->m_trie);
	free(m);
}

static void
ment_free(struct ment *ment)
{
	int i;

	for (i = 0; i < MTRIE_SIZE; i++)
		if (ment->me_next != NULL)
			ment_free(ment->me_next);
	free(ment);
}

/*
 * return 2^x
 */
static int
poweroftwo(int x)
{

	return (1 << x);
}

/*
 * std debug macro.
 */
#ifdef DEBUG
#define bug(fmt, args...)						\
	do {								\
		if (getenv("DEBUG"))					\
		fprintf(stderr, "%d: " fmt "\n", __LINE__, ##args);	\
	} while (0);
#else
#define bug(fmt, args...)
#endif

/*
 * insert into the mtrie
 */
int
mtrie_insert(mtrie_t m, uint32_t ip, uint8_t mask, void *datum)
{
	unsigned char *ov;
	struct ment **place, *prev;
	int idx, pow, accum, lowrange, rem, x;

	ov = (unsigned char *)&ip;
	place = &m->m_trie;
	prev = NULL;
	idx = 3;

	for ( ;; ) {
		/*
		 * allocate a new slot if needed
		 */
		bug("looping idx = %d", idx);
		if (*place == NULL) {
			*place = calloc(MTRIE_SIZE, sizeof(**place));
			if (*place == NULL)
				return (-1);
		}
		prev = &(*place)[ov[idx]];
		bug("mask = %d", mask);
		/*
		 * If we're still greater than 8, go down another row.
		 */
		if (mask > 8) {
			mask -= 8;
			place = &prev->me_next;
			idx--;
			continue;
		}
		/*
		 * If we're exactly at 8, just fill the data.
		 */
		if (mask == 8) {
			prev->me_data = datum;
			return (0);
		}
		accum = 0;
		rem = 8 - mask;
		/*
		 * Get the next highest multiple of 2^rem over
		 * the current cell, example:
		 * poweroftwo(rem) == 8, ov[idx] = 29
		 * then we want:
		 * accum = 32, lowrange = 24.
		 */
		pow = poweroftwo(rem);
		x = ov[idx];
		lowrange = x - (x % pow);
		accum = lowrange + pow;
		/*
		 * Now fill in the entries in the row.
		 */
		while (accum-- > lowrange) {
			struct ment *mp;

			mp = (*place) + accum;
			/*
			 * If there is no data or we're an equal or
			 * more specific mask then overwrite the data.
			 */
			if (mp->me_data == NULL || mp->me_mask >= rem) {
			 	/*
				 * If we wanted to be able to delete entries
				 * we'd probably stack them here instead,
				 * it would be a good idea to do the
				 * necessary allocation up front.
				 */
				mp->me_data = datum;
				mp->me_mask = rem;
				bug("accum: %d, datum %p, mp = %p, rem = %d",
				    accum, datum, mp, rem);
			} else {
				bug("SET!!! "
				    "accum: %d, datum %p, mp = %p, rem = %d",
				    accum, datum, mp, rem);
			}
		}
		return (0);
	}
}

/*
 * lookup in the mtrie.
 */
int
mtrie_lookup(mtrie_t m, uint32_t ip, void **datap)
{
	struct ment **place, *prev;
	unsigned char *ov;
	int idx;
	void *last;

	ov = (char *)&ip;
	place = &m->m_trie;
	prev = NULL;
	*datap = NULL;
	last = NULL;
	/*
	 * Walk the octets.
	 */
	for (idx = 3 ; idx >= 0; idx--) {
		bug("looping: idx = %d, *place = %p, prev = %p",
		    idx, *place, prev);
		bug("prev->me_data == %p",
		    prev != NULL ? prev->me_data : NULL);
		if (*place == NULL)
			break;
		/*
		 * if we're going deeper into the mtrie and
		 * leaving a node that has data then we need
		 * to remember that in case we fall out of the
		 * tree at a lower level where no specific route
		 * has been entered.
		 */
		if (prev != NULL && prev->me_data != NULL)
			last = prev->me_data;
		prev = &(*place)[ov[idx]];
		place = &prev->me_next;
	}
	bug("lookup: idx = %d, *place = %p, prev = %p",
	    idx, *place, prev);
	bug("prev->me_data == %p",
	    prev != NULL ? prev->me_data : NULL);
	/* we lost, nothing matches. */
	if (prev == NULL)
		return (-1);
	/*
	 * Get the data at the node we were at before
	 * we fell out.
	 */
	*datap = prev->me_data;
	/*
	 * If it was null then take a shot at the 'last'
	 * node that had data, that will be the data
	 * entered by a route higher up in the tree.
	 */
	if (*datap == NULL)
		*datap = last;
	/* we could have still lost. */
	return (*datap == NULL ? -1 : 0);
}


syntax highlighted by Code2HTML, v. 0.9.1