// $Id: PrefixTable.cc,v 1.3 2003/09/04 06:05:09 vern Exp $

#include "PrefixTable.h"

static void* NO_DATA = (void*) 1;

// IPv4 version.
inline static prefix_t* make_prefix(uint32 addr, int width)
	{
	prefix_t* prefix = new prefix_t;

	memcpy(&prefix->add.sin, &addr, sizeof(prefix->add.sin)) ;
	prefix->family = AF_INET;
	prefix->bitlen = width;
	prefix->ref_count = 1;

	return prefix;
	}

#ifdef BROv6
// IPv6 version. (FIXME: untested...)
inline static prefix_t make_prefix(uint32* addr, int width)
	{
	prefix_t* prefix = new prefix_t;

	memcpy(&prefix->add.sin6, addr, 4 * sizeof(uint32));
	prefix->family = AF_INET6;
	prefix->bitlen = width;
	prefix->ref_count = 1;

	return prefix;
	}
#endif

void* PrefixTable::Insert(addr_type addr, int width, void* data)
	{
	if ( ! data )
		data = NO_DATA;

	prefix_t* prefix = make_prefix(addr, width);
	patricia_node_t* node = patricia_lookup(tree, prefix);
	Deref_Prefix(prefix);

	if ( ! node )
		internal_error("Cannovoid create node in patrcia tree");

	void* old = node->data;
	node->data = data;
	return old;
	}

void* PrefixTable::Insert(const Val* value, void* data)
	{
	// [elem] -> elem
	if ( value->Type()->Tag() == TYPE_LIST &&
	     value->AsListVal()->Length() == 1 )
		value = value->AsListVal()->Index(0);

	if ( ! data )
		data = NO_DATA;

	switch ( value->Type()->Tag() ) {
	case TYPE_ADDR:
		return Insert(value->AsAddr(), 32, data);
		break;

	case TYPE_SUBNET:
		return Insert(value->AsSubNet()->net,
				value->AsSubNet()->width, data);
		break;

	default:
		internal_error("Wrong index type for PrefixTable");
		return 0;
	}
	}

void* PrefixTable::Lookup(addr_type addr, int width, bool exact) const
	{
	prefix_t* prefix = make_prefix(addr, width);
	patricia_node_t* node =
		exact ? patricia_search_exact(tree, prefix) :
			patricia_search_best(tree, prefix);

	Deref_Prefix(prefix);
	return node ? node->data : 0;
	}

void* PrefixTable::Lookup(const Val* value, bool exact) const
	{
	// [elem] -> elem
	if ( value->Type()->Tag() == TYPE_LIST &&
	     value->AsListVal()->Length() == 1 )
		value = value->AsListVal()->Index(0);

	switch ( value->Type()->Tag() ) {
	case TYPE_ADDR:
		return Lookup(value->AsAddr(), 32, exact);
		break;

	case TYPE_SUBNET:
		return Lookup(value->AsSubNet()->net,
				value->AsSubNet()->width, exact);
		break;

	default:
		internal_error(fmt("Wrong index type %d for PrefixTable",
					value->Type()->Tag()));
		return 0;
	}
	}

void* PrefixTable::Remove(addr_type addr, int width)
	{
	prefix_t* prefix = make_prefix(addr, width);
	patricia_node_t* node = patricia_search_exact(tree, prefix);
	Deref_Prefix(prefix);

	if ( ! node )
		return 0;

	void* old = node->data;
	patricia_remove(tree, node);

	return old;
	}

void* PrefixTable::Remove(const Val* value)
	{
	// [elem] -> elem
	if ( value->Type()->Tag() == TYPE_LIST &&
	     value->AsListVal()->Length() == 1 )
		value = value->AsListVal()->Index(0);

	switch ( value->Type()->Tag() ) {
	case TYPE_ADDR:
		return Remove(value->AsAddr(), 32);
		break;

	case TYPE_SUBNET:
		return Remove(value->AsSubNet()->net, value->AsSubNet()->width);
		break;

	default:
		internal_error("Wrong index type for PrefixTable");
		return 0;
	}
	}
