// $Id: Val.cc,v 1.89 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include <sys/types.h>

#include <netinet/in.h>

#include <stdio.h>
#include <stdlib.h>

#include "Val.h"
#include "Net.h"
#include "File.h"
#include "Func.h"
#include "RE.h"
#include "Scope.h"
#include "NetVar.h"
#include "Expr.h"
#include "Serializer.h"
#include "PrefixTable.h"

Val::Val(Func* f)
	{
	val.func_val = f;
	type = f->FType()->Ref();
	attribs = 0;
	}

Val::Val(BroFile* f)
	{
	val.file_val = f;
	type = new FileType(f->FType()->Ref());
	attribs = 0;
	}

Val::~Val()
	{
	if ( type->InternalType() == TYPE_INTERNAL_STRING )
		delete val.string_val;

	Unref(type);
	}

bool Val::Serialize(Serializer* s) const
  	{
	// Save type information.
	s->Write((char)type->InternalType());
	s->Write((char)type->Tag());

	// L<location>|-
	if ( location )
		{
		s->Write('L');
		if ( ! location->Serialize(s) )
			return false;
		}
	else
		s->Write('-');

	switch ( type->InternalType() ) {
	case TYPE_INTERNAL_VOID:
		s->Error("type is void");
		return false;

	case TYPE_INTERNAL_INT:
		s->Write(val.int_val, "int");
		break;

	case TYPE_INTERNAL_UNSIGNED:
		s->Write(val.uint_val, "uint");
		break;

	case TYPE_INTERNAL_DOUBLE:
		s->Write(val.double_val, "double");
		break;

	case TYPE_INTERNAL_STRING:
		s->Write((const char*) val.string_val->Bytes(),
				val.string_val->Len(), "string");
		break;

	case TYPE_INTERNAL_ADDR:
		// FIXME: No IPV6 address serializing yet.
		s->Write((uint32)ntohl(val.addr_val), "addr");
		break;

	case TYPE_INTERNAL_SUBNET:
		s->WriteOpenTag("subnet");
		s->Write(val.subnet_val.net);
		s->Write(val.subnet_val.width);
		s->WriteCloseTag("subnet");
		break;

	case TYPE_INTERNAL_OTHER:
		// Derived classes are responsible for this.
		break;

	case TYPE_INTERNAL_ERROR:
		s->Error("type is error");
		return false;

	default:
		s->Error("type is out of range");
		return false;
	}

	return DoSerialize(s);
	}

Val* Val::Unserialize(Serializer* s, BroType* t)
  	{
	// Read type.
	char internal_type;
	char tag;

	if ( ! s->Read(&internal_type) )
		return 0;

	if ( ! s->Read(&tag) )
		return 0;

	// Check Type.
	if ( t )
		{
		if ( t->InternalType() != (InternalTypeTag)internal_type ||
		     t->Tag() != (TypeTag)tag )
			{
			s->Error(fmt("type error, got %s, but expected %s",
						 type_name((TypeTag)tag),
						 type_name(t->Tag())));
			return 0;
			}
		}

	// Create value of correct subclass.
	Val* v = 0;
	switch ( TypeTag(tag) ) {
	case TYPE_VOID:
	case TYPE_BOOL:
	case TYPE_INT:
	case TYPE_COUNT:
	case TYPE_COUNTER:
	case TYPE_DOUBLE:
	case TYPE_TIME:
	case TYPE_INTERVAL:
		v = new Val((TypeTag)tag);
		break;

	case TYPE_PORT:
		v  = new PortVal(0);
		break;

	case TYPE_ADDR:
		// FIXME - no IPv6 support
		v  = new AddrVal(uint32(0));
		break;

	case TYPE_NET:
		v  = new NetVal(uint32(0));
		break;

	case TYPE_SUBNET:
		v  = new SubNetVal(uint32(0), 0);
		break;

	case TYPE_LIST:
		v  = new ListVal(TYPE_VOID);
		break;

	case TYPE_STRING:
		v  = new StringVal("");
		break;

	case TYPE_ENUM:
		v  = new EnumVal(0, 0);
		break;

	case TYPE_SET:
	case TYPE_TABLE:
		{
		TypeList* dummy = new TypeList();
		v = new TableVal(new TableType(dummy, base_type(TYPE_VOID)));
		break;
		}

	case TYPE_RECORD:
		{
		type_decl_list* dummy = new type_decl_list();
		v = new RecordVal(new RecordType(dummy));
		break;
		}

	case TYPE_UNION:
	case TYPE_ANY:
	case TYPE_TIMER:
	case TYPE_FUNC:
	case TYPE_FILE:
	case TYPE_ERROR:
	case TYPE_PATTERN:
		s->Error(fmt("not implemented for type %s",
				type_name((TypeTag)tag)));
		return 0;

	default:
		s->Error(fmt("unknown type tag %d", int(tag)));
		return 0;
	}

	// Set type.
	if ( t )
		{
		Unref(v->type);
		v->type = t;
		t->Ref();
		}
	// else keep default

	// Read location.
	v->location = 0;

	char flag;
	if ( ! s->Read(&flag) )
	  	return 0;

	switch ( flag ) {
	case '-':
		break;

	case 'L':
		v->location = new Location;
		if ( ! v->location->Unserialize(s) )
			return 0;
		break;

	default:
		s->Error("corrupt data");
		return 0;
	}

	// Read generic value data.
	switch ( InternalTypeTag(internal_type) ) {
	case TYPE_INTERNAL_VOID:
		s->Error("type is void");
		return 0;

	case TYPE_INTERNAL_INT:
		if ( ! s->Read(&v->val.int_val) )
			return 0;
		break;

	case TYPE_INTERNAL_UNSIGNED:
		if ( ! s->Read(&v->val.uint_val) )
			return 0;
		break;

	case TYPE_INTERNAL_DOUBLE:
		if ( ! s->Read(&v->val.double_val) )
			return 0;
		break;

	case TYPE_INTERNAL_STRING:
		char* str;
		int len;

		if ( ! s->Read(&str, &len) )
			return 0;

		delete v->val.string_val;
		v->val.string_val = new BroString((u_char*) str, len, 1);
		break;

	case TYPE_INTERNAL_ADDR:
		// FIXME: No IPV6 address serializing yet.
		if ( ! s->Read(&v->val.addr_val) )
			return 0;

		v->val.addr_val = htonl(v->val.addr_val);
		break;

	case TYPE_INTERNAL_SUBNET:
		if ( ! s->Read(&v->val.subnet_val.net) ||
		     ! s->Read(&v->val.subnet_val.width) )
			return 0;
		break;

	case TYPE_INTERNAL_OTHER:
		// Derived classes are responsible for this.
		break;

	case TYPE_INTERNAL_ERROR:
		s->Error("type is error");
		return 0;

	default:
		s->Error("type out of range");
		return 0;
	}

	// Read type specific data.
	if ( ! v->DoUnserialize(s) )
		return 0;

	return v;
	}

RecordVal* Val::GetAttribs(bool instantiate)
	{
	if ( ! instantiate || attribs )
		return attribs;

	attribs = new RecordVal(type->AttributesType());
	return attribs;
	}

int Val::IsZero() const
	{
	switch ( type->InternalType() ) {
	case TYPE_INTERNAL_INT:		return val.int_val == 0;
	case TYPE_INTERNAL_UNSIGNED:	return val.uint_val == 0;
	case TYPE_INTERNAL_DOUBLE:	return val.double_val == 0.0;

	default:			return 0;
	}
	}

int Val::IsOne() const
	{
	switch ( type->InternalType() ) {
	case TYPE_INTERNAL_INT:		return val.int_val == 1;
	case TYPE_INTERNAL_UNSIGNED:	return val.uint_val == 1;
	case TYPE_INTERNAL_DOUBLE:	return val.double_val == 1.0;

	default:			return 0;
	}
	}

int Val::InternalInt() const
	{
	if ( type->InternalType() == TYPE_INTERNAL_INT )
		return val.int_val;
	else if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
		// ### should check here for overflow
		return int(val.uint_val);
	else
		Internal("bad request for InternalInt");

	return 0;
	}

uint32 Val::InternalUnsigned() const
	{
	if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
		return val.uint_val;
	else
		Internal("bad request for InternalUnsigned");

	return 0;
	}

double Val::InternalDouble() const
	{
	if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
		return val.double_val;
	else
		Internal("bad request for InternalDouble");

	return 0.0;
	}

int Val::CoerceToInt() const
	{
	if ( type->InternalType() == TYPE_INTERNAL_INT )
		return val.int_val;
	else if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
		return int(val.uint_val);
	else if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
		return int(val.double_val);
	else
		Internal("bad request for CoerceToInt");

	return 0;
	}

uint32 Val::CoerceToUnsigned() const
	{
	if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
		return val.uint_val;
	else if ( type->InternalType() == TYPE_INTERNAL_INT )
		return uint32(val.int_val);
	else if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
		return uint32(val.double_val);
	else
		Internal("bad request for CoerceToUnsigned");

	return 0;
	}

double Val::CoerceToDouble() const
	{
	if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
		return val.double_val;
	else if ( type->InternalType() == TYPE_INTERNAL_INT )
		return double(val.int_val);
	else if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
		return double(val.uint_val);
	else
		Internal("bad request for CoerceToDouble");

	return 0.0;
	}

int Val::Size() const
	{
	int size = sizeof(*this);

	switch ( type->InternalType() ) {
	case TYPE_INTERNAL_INT:
	case TYPE_INTERNAL_UNSIGNED:
	case TYPE_INTERNAL_DOUBLE:
	case TYPE_INTERNAL_ERROR:
		return size;

	case TYPE_INTERNAL_ADDR:
#ifdef BROv6
		return size + 4 * sizeof(uint32);
#else
		return size;
#endif

	case TYPE_INTERNAL_SUBNET:
		return sizeof(subnet_type);

	case TYPE_INTERNAL_STRING:
		return size + AsString()->Len();

	default:
		break;
	}

	switch ( type->Tag() ) {
	case TYPE_PATTERN:
	case TYPE_FUNC:
	case TYPE_FILE:
		return size;	// not clear what's the right answer

	case TYPE_SET:
	case TYPE_TABLE:
		{
		PDict(TableEntryVal)* v = val.table_val;
		IterCookie* c = v->InitForIteration();

		TableEntryVal* tv;
		HashKey* h;
		while ( (tv = v->NextEntry(h, c)) )
			{
			size += sizeof(*h) + h->Size();
			delete h;

			size += tv->Value()->Size();
			size += sizeof(TableEntryVal);
			}

		return size;
		}

	case TYPE_RECORD:
		{
		RecordType* rt = type->AsRecordType();
		const val_list* vl = val.val_list_val;
		int n = rt->NumFields();

		for ( int i = 0; i < n; ++i )
			{
			Val* v = (*vl)[i];

			// v might be nil for records that don't wind
			// up being set to a value.
			if ( v )
				size += (*vl)[i]->Size();
			}

		return size;
		}

	case TYPE_LIST:
		{
		const val_list* vl = AsListVal()->Vals();
		loop_over_list(*vl, i)
			size += (*vl)[i]->Size();

		return size;
		}

	default:
		break;
	}

	return size;
	}

int Val::AddTo(Val* /* v */, int /* is_first_init */) const
	{
	Error("can't use += with this type of value");
	return 0;
	}

int Val::RemoveFrom(Val* /* v */) const
	{
	Error("can't use -= with this type of value");
	return 0;
	}

void Val::Describe(ODesc* d) const
	{
	if ( d->IsBinary() || d->IsPortable() )
		{
		type->Describe(d);
		d->SP();
		}

	if ( d->IsReadable() )
		ValDescribe(d);
	else
		Val::ValDescribe(d);
	}

void Val::ValDescribe(ODesc* d) const
	{
	if ( d->IsReadable() && type->Tag() == TYPE_BOOL )
		{
		d->Add(CoerceToInt() ? "T" : "F");
		return;
		}

	switch ( type->InternalType() ) {
	case TYPE_INTERNAL_INT:		d->Add(val.int_val); break;
	case TYPE_INTERNAL_UNSIGNED:	d->Add(val.uint_val); break;
	case TYPE_INTERNAL_DOUBLE:	d->Add(val.double_val); break;
	case TYPE_INTERNAL_STRING:	d->AddBytes(val.string_val); break;
	case TYPE_INTERNAL_ADDR:	d->Add(dotted_addr(val.addr_val)); break;

	case TYPE_INTERNAL_SUBNET:
		d->Add(dotted_addr(val.subnet_val.net));
		d->Add("/");
		d->Add(val.subnet_val.width);
		break;

	case TYPE_INTERNAL_ERROR:	d->AddCS("error"); break;
	case TYPE_INTERNAL_OTHER:	d->Add("<no value description>"); break;

	default:
		if ( type->Tag() == TYPE_FUNC )
			{
			AsFunc()->Describe(d);
			break;
			}

		// Don't call Internal(), that'll loop!
		internal_error("Val description unavailable");
	}
	}

IntervalVal::IntervalVal(double quantity, double units) :
	Val(quantity * units, TYPE_INTERVAL)
	{
	}

void IntervalVal::ValDescribe(ODesc* d) const
	{
	double v = val.double_val;

	if ( v == 0.0 )
		{
		d->Add("0 secs");
		return;
		}

	int did_one = 0;

#define DO_UNIT(unit, name) \
	if ( v >= unit || v <= -unit ) \
		{ \
		double num = double(int(v / unit)); \
		if ( num != 0.0 ) \
			{ \
			if ( did_one++ ) \
				d->SP(); \
			d->Add(num); \
			d->SP(); \
			d->Add(name); \
			if ( num != 1.0 && num != -1.0 ) \
				d->Add("s"); \
			v -= num * unit; \
			} \
		}

	DO_UNIT(Days, "day")
	DO_UNIT(Hours, "hr")
	DO_UNIT(Minutes, "min")
	DO_UNIT(Seconds, "sec")
	DO_UNIT(Milliseconds, "msec")
	DO_UNIT(Microseconds, "usec")
	}

#define UDP_PORT_MASK 0x10000
PortVal::PortVal(uint32 p, int is_tcp) : Val(TYPE_PORT)
	{
	if ( ! is_tcp )
		p |= UDP_PORT_MASK;	// set bit 32 to indicate UDP

	if ( p >= 65536*2 )
		Internal("bad port number");

	val.uint_val = p;
	}

PortVal::PortVal(uint32 p) : Val(TYPE_PORT)
	{
	if ( p >= 65536*2 )
		Internal("bad port number");
	val.uint_val = p;
	}

uint32 PortVal::Port() const
	{
	uint32 p = val.uint_val;
	p &= ~UDP_PORT_MASK;

	return p;
	}

int PortVal::IsTCP() const
	{
	return ! (val.uint_val & UDP_PORT_MASK);
	}

void PortVal::ValDescribe(ODesc* d) const
	{
	uint32 p = val.uint_val;
	if ( p & UDP_PORT_MASK )
		{
		d->Add(p & ~UDP_PORT_MASK);
		d->Add("/udp");
		}
	else
		{
		d->Add(p);
		d->Add("/tcp");
		}
	}

AddrVal::AddrVal(const char* text) : Val(TYPE_ADDR)
	{
	const char* colon = strchr(text, ':');

	if ( colon )
		{
#ifdef BROv6
		Init(dotted_to_addr6(text));
#else
		error("bro wasn't compiled with IPv6 support");
		Init(uint32(0));
#endif
		}

	else
		Init(dotted_to_addr(text));
	}

AddrVal::AddrVal(uint32 addr) : Val(TYPE_ADDR)
	{
	// ### perhaps do gethostbyaddr here?
	Init(addr);
	}

AddrVal::AddrVal(const uint32* addr) : Val(TYPE_ADDR)
	{
	Init(addr);
	}

AddrVal::~AddrVal()
	{
#ifdef BROv6
	delete val.addr_val;
#endif
	}

void AddrVal::Init(uint32 addr)
	{
#ifdef BROv6
	val.addr_val = new uint32[4];
	val.addr_val[0] = val.addr_val[1] = val.addr_val[2] = 0;
	val.addr_val[3] = addr;
#else
	val.addr_val = addr;
#endif
	}

void AddrVal::Init(const uint32* addr)
	{
#ifdef BROv6
	val.addr_val = new uint32[4];
	val.addr_val[0] = addr[0];
	val.addr_val[1] = addr[1];
	val.addr_val[2] = addr[2];
	val.addr_val[3] = addr[3];
#else
	val.addr_val = addr[0];
#endif
	}

static uint32 parse_dotted(const char* text, int& dots)
	{
	int addr[4];
	uint32 a = 0;
	dots = 0;

	if ( sscanf(text, "%d.%d.%d.%d", addr+0, addr+1, addr+2, addr+3) == 4 )
		{
		a = (addr[0] << 24) | (addr[1] << 16) |
			(addr[2] << 8) | addr[3];
		dots = 3;
		}

	else if ( sscanf(text, "%d.%d.%d", addr+0, addr+1, addr+2) == 3 )
		{
		a = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8);
		dots = 2;
		}

	else if ( sscanf(text, "%d.%d", addr+0, addr+1) == 2 )
		{
		a = (addr[0] << 24) | (addr[1] << 16);
		dots = 1;
		}

	else
		internal_error("scanf failed in parse_dotted()");

	for ( int i = 0; i <= dots; ++i )
		{
		if ( addr[i] < 0 || addr[i] > 255 )
			{
			error("bad dotted address", text);
			break;
			}
		}

	return a;
	}

NetVal::NetVal(const char* text) : AddrVal(TYPE_NET)
	{
	int dots;
	uint32 a = parse_dotted(text, dots);

	if ( addr_to_net(a) != a )
		error("bad net address", text);

	Init(uint32(htonl(a)));
	}

NetVal::NetVal(uint32 addr) : AddrVal(TYPE_NET)
	{
	Init(addr);
	}

#ifdef BROv6
NetVal::NetVal(const uint32* addr) : AddrVal(TYPE_NET)
	{
	Init(addr);
	}
#endif

void NetVal::ValDescribe(ODesc* d) const
	{
#ifdef BROv6
	d->Add(dotted_net6(val.addr_val));
#else
	d->Add(dotted_net(val.addr_val));
#endif
	}


SubNetVal::SubNetVal(const char* text) : Val(TYPE_SUBNET)
	{
	const char* sep = strchr(text, '/');
	if ( ! sep )
		Internal("separator missing in SubNetVal::SubNetVal");

	Init(text, atoi(sep+1));
	}

SubNetVal::SubNetVal(const char* text, int width) : Val(TYPE_SUBNET)
	{
	Init(text, width);
	}

SubNetVal::SubNetVal(uint32 addr, int width) : Val(TYPE_SUBNET)
	{
	Init(addr, width);
	}

#ifdef BROv6
SubNetVal::SubNetVal(const uint32* addr, int width) : Val(TYPE_SUBNET)
	{
	Init(addr, width);
	}
#endif

void SubNetVal::Init(const char* text, int width)
	{
#ifdef BROv6
	if ( width <= 0 || width > 128 )
#else
	if ( width <= 0 || width > 32 )
#endif
		Error("bad subnet width");

	int dots;
	uint32 a = parse_dotted(text, dots);

	Init(uint32(htonl(a)), width);
	}


void SubNetVal::Init(uint32 addr, int width)
	{
#ifdef BROv6
	Internal("SubNetVal::Init called on 4-byte address w/ BROv6");
#else
	val.subnet_val.net = mask_addr(addr, uint32(width));
	val.subnet_val.width = width;
#endif
	}

void SubNetVal::Init(const uint32* addr, int width)
	{
#ifdef BROv6
	const uint32* a = mask_addr(addr, uint32(width));

	val.subnet_val.net[0] = a[0];
	val.subnet_val.net[1] = a[1];
	val.subnet_val.net[2] = a[2];
	val.subnet_val.net[3] = a[3];

	if ( is_v4_addr(addr) )
		val.subnet_val.width = width + 96;
	else
		val.subnet_val.width = width;
#else
	Internal("SubNetVal::Init called on 16-byte address w/o BROv6");
#endif
	}

void SubNetVal::ValDescribe(ODesc* d) const
	{
	d->Add(dotted_addr(val.subnet_val.net, d->Style() == ALTERNATIVE_STYLE));
	d->Add("/");
	d->Add(val.subnet_val.width);
	}

addr_type SubNetVal::Mask() const
	{
#ifdef BROv6
	uint32* m = new uint32[4];
	uint32* mp = m;

	uint32 w;
	for ( w = val.subnet_val.width; w >= 32; w -= 32 )
		*(mp++) = 0xffffffff;

	*mp = ~((1 << (32 - w)) - 1);

	while ( ++mp < m + 4 )
		*mp = 0;

	return m;

#else
	return ~((1 << (32 - val.subnet_val.width)) - 1);
#endif
	}

bool SubNetVal::Contains(const uint32 addr) const
	{
#ifdef BROv6
	Internal("SubNetVal::Contains called on 4-byte address w/ BROv6");
	return false;
#else
	return ntohl(val.subnet_val.net) == (ntohl(addr) & Mask());
#endif
	}

bool SubNetVal::Contains(const uint32* addr) const
	{
#ifdef BROv6
	const uint32* net = val.subnet_val.net;
	const uint32* a = addr;
	uint32 m;

	for ( m = val.subnet_val.width; m >= 32; m -= 32 )
		{
		if ( *net != *a )
			return false;

		++net;
		++a;
		}

	uint32 mask = ~((1 << (32 - m)) - 1);
	return ntohl(*net) == (ntohl(*a) & mask);
#else
	return Contains(addr[3]);
#endif
	}

bool SubNetVal::DoSerialize(Serializer* s) const
	{
	s->Write(mask);
	return true;
	}

bool SubNetVal::DoUnserialize(Serializer* s)
	{
	return s->Read(&mask);
	}

StringVal::StringVal(BroString* s) : Val(TYPE_STRING)
	{
	val.string_val = s;
	}

StringVal::StringVal(int length, const char* s) : Val(TYPE_STRING)
	{
	// The following adds a NUL at the end.
	val.string_val = new BroString((const u_char *) s, length, 1);
	}

StringVal::StringVal(const char* s) : Val(TYPE_STRING)
	{
	val.string_val = new BroString(s);
	}

StringVal* StringVal::ToUpper()
	{
	val.string_val->ToUpper();
	return this;
	}

void StringVal::ValDescribe(ODesc* d) const
	{
	// Should reintroduce escapes ? ###
	// d->Add("\"");
	d->AddBytes(val.string_val);
	// d->Add("\"");
	}

PatternVal::PatternVal(RE_Matcher* re) : Val(base_type(TYPE_PATTERN))
	{
	val.re_val = re;
	}

PatternVal::~PatternVal()
	{
	delete AsPattern();
	Unref(type);	// base_type() ref'd it, so did our base constructor
	}

int PatternVal::AddTo(Val* v, int /* is_first_init */) const
	{
	if ( v->Type()->Tag() != TYPE_PATTERN )
		{
		v->Error("not a pattern");
		return 0;
		}

	PatternVal* pv = v->AsPatternVal();

	RE_Matcher* re = new RE_Matcher(AsPattern()->PatternText());
	re->AddPat(pv->AsPattern()->PatternText());
	re->Compile();

	pv->SetMatcher(re);

	return 1;
	}

void PatternVal::SetMatcher(RE_Matcher* re)
	{
	delete AsPattern();
	val.re_val = re;
	}

void PatternVal::ValDescribe(ODesc* d) const
	{
	d->Add("/");
	d->Add(AsPattern()->PatternText());
	d->Add("/");
	}

ListVal::ListVal(TypeTag t)
: Val(new TypeList(t == TYPE_ANY ? 0 : base_type(t)))
	{
	tag = t;
	}

ListVal::~ListVal()
	{
	loop_over_list(vals, i)
		Unref(vals[i]);
	Unref(type);
	}

const char* ListVal::IncludedInString(const char* str) const
	{
	if ( tag != TYPE_STRING )
		Internal("non-string list in ListVal::IncludedInString");

	loop_over_list(vals, i)
		{
		const char* vs = (const char*) (vals[i]->AsString()->Bytes());

		const char* embedded = strstr(str, vs);
		if ( embedded )
			return embedded;
		}

	return 0;
	}

RE_Matcher* ListVal::BuildRE() const
	{
	if ( tag != TYPE_STRING )
		Internal("non-string list in ListVal::IncludedInString");

	RE_Matcher* re = new RE_Matcher();
	loop_over_list(vals, i)
		{
		const char* vs = (const char*) (vals[i]->AsString()->Bytes());
		re->AddPat(vs);
		}

	return re;
	}

void ListVal::Append(Val* v)
	{
	if ( type->AsTypeList()->IsPure() )
		{
		if ( v->Type()->Tag() != tag )
			Internal("heterogeneous list in ListVal::Append");
		}

	vals.append(v);
	type->AsTypeList()->Append(v->Type()->Ref());
	}

TableVal* ListVal::ConvertToSet() const
	{
	if ( tag == TYPE_ANY )
		Internal("conversion of heterogeneous list to set");

	TypeList* set_index = new TypeList(type->AsTypeList()->PureType());
	set_index->Append(base_type(tag));
	SetType* s = new SetType(set_index, 0);
	TableVal* t = new TableVal(s);

	loop_over_list(vals, i)
		t->Assign(vals[i], 0);

	return t;
	}

void ListVal::Describe(ODesc* d) const
	{
	if ( d->IsBinary() || d->IsPortable() )
		{
		type->Describe(d);
		d->SP();
		d->Add(vals.length());
		d->SP();
		}

	loop_over_list(vals, i)
		{
		if ( i > 0 )
			{
			if ( d->IsReadable() || d->IsPortable() )
				{
				d->Add(",");
				d->SP();
				}
			}

		vals[i]->Describe(d);
		}
	}

bool ListVal::DoUnserialize(Serializer* s)
	{
	// We cannot implement this here because, in general, we do not know the
	// types of the vals. But this doesn't really matter because
	// ListVal is only an internal class and classes such as TableVal
	// do know the types, so they can implement the serialization.
	s->Error( "ListVal not implemented");
	return false;
	}

bool ListVal::DoSerialize(Serializer* s) const
	{
	s->Error( "ListVal not implemented");
	return false;
	}

TableValTimer::TableValTimer(TableVal* val, double t) : Timer(t)
	{
	table = val;
	}

void TableValTimer::Dispatch(double t, int is_expire)
	{
	// Just ignore it if we've been cancelled or Bro is terminating.
	if ( active && ! is_expire )
		{
		table->DoExpire(t);
		table->InitTimer();
		}
	}

static void table_entry_val_delete_func(void* val)
	{
	TableEntryVal* tv = (TableEntryVal*) val;
	tv->Unref();
	delete tv;
	}

TableVal::TableVal(TableType* t, Attributes* a) : Val(t)
	{
	table_type = t;
	expire_func = 0;
	timer = 0;

	if ( t->IsSubNetIndex() )
		subnets = new PrefixTable;
	else	
		subnets = 0;
		
	SetAttrs(a);

	table_hash = new CompositeHash(table_type->Indices());
	val.table_val = new PDict(TableEntryVal);
	val.table_val->SetDeleteFunc(table_entry_val_delete_func);
	}

TableVal::~TableVal()
	{
	if ( timer )
		timer->Cancel();

	delete table_hash;
	delete AsTable();
	delete subnets;
	Unref(attrs);
	}

void TableVal::RemoveAll()
	{
	// Here we take the brute force approach.
	delete AsTable();
	val.table_val = new PDict(TableEntryVal);
	val.table_val->SetDeleteFunc(table_entry_val_delete_func);
	}

void TableVal::SetAttrs(Attributes* a)
	{
	attrs = a;

	if ( ! a )
		return;

	::Ref(attrs);

	CheckExpireAttr(ATTR_EXPIRE_READ);
	CheckExpireAttr(ATTR_EXPIRE_WRITE);
	CheckExpireAttr(ATTR_EXPIRE_CREATE);

	Attr* ef = attrs->FindAttr(ATTR_EXPIRE_FUNC);
	if ( ef )
		expire_func = ef->AttrExpr()->Eval(0)->AsFunc();
	}

void TableVal::CheckExpireAttr(attr_tag at)
	{
	Attr* a = attrs->FindAttr(at);

	if ( a )
		{
		expire_time = a->AttrExpr()->ExprVal()->AsInterval();

		// As network_time is not necessarily initialized yet,
		// we set a timer which fires immediately.
		timer = new TableValTimer(this, 1);
		timer_mgr->Add(timer);
		}
	}

int TableVal::Assign(Val* index, Val* new_val)
	{
	HashKey* k = ComputeHash(index);
	if ( ! k )
		{
		index->Error("index type doesn't match table", table_type->Indices());
		return 0;
		}

	return Assign(index, k, new_val);
	}

int TableVal::Assign(Val* index, HashKey* k, Val* new_val)
	{
	int is_set = table_type->IsSet();

	if ( (is_set && new_val) || (! is_set && ! new_val) )
		Internal("bad set/table in TableVal::Assign");

	if ( ! new_val )
		// A set, not a table.  Use index as the value.
		new_val = index->Ref();

	TableEntryVal* new_entry_val = new TableEntryVal(new_val);
	TableEntryVal* old_entry_val = AsTable()->Insert(k, new_entry_val);

	if ( subnets )
		{
		if ( ! index )
			{
			Val* v = RecoverIndex(k);
			subnets->Insert(v, new_entry_val);
			Unref(v);
			}
		else
			subnets->Insert(index, new_entry_val);
		}
	
	// Keep old expiration time if necessary.
	if ( old_entry_val && attrs && attrs->FindAttr(ATTR_EXPIRE_CREATE) )
		new_entry_val->SetExpireAccess(old_entry_val->ExpireAccessTime());

	delete k;
	if ( old_entry_val )
		{
		old_entry_val->Unref();
		delete old_entry_val;
		}

	return 1;
	}

int TableVal::AddTo(Val* val, int is_first_init) const
	{
	if ( val->Type()->Tag() != TYPE_TABLE )
		{
		val->Error("not a table");
		return 0;
		}

	TableVal* t = val->AsTableVal();

	if ( ! same_type(type, t->Type()) )
		{
		type->Error("table type clash", t->Type());
		return 0;
		}

	const PDict(TableEntryVal)* tbl = AsTable();
	IterCookie* c = tbl->InitForIteration();

	HashKey* k;
	TableEntryVal* v;
	while ( (v = tbl->NextEntry(k, c)) )
		{
		if ( is_first_init && t->AsTable()->Lookup(k) )
			{
			Val* key = table_hash->RecoverVals(k);
			// ### Shouldn't complain if their values are equal.
			key->Warn("multiple initializations for index");
			Unref(key);
			continue;
			}

		if ( type->IsSet() )
			{
			if ( ! t->Assign(v->Value(), k, 0) )
				 return 0;
			}
		else
			{
			v->Ref();
			if ( ! t->Assign(0, k, v->Value()) )
				 return 0;
			}
		}

	return 1;
	}

int TableVal::RemoveFrom(Val* val) const
	{
	if ( val->Type()->Tag() != TYPE_TABLE )
		{
		val->Error("not a table");
		return 0;
		}

	TableVal* t = val->AsTableVal();

	if ( ! same_type(type, t->Type()) )
		{
		type->Error("table type clash", t->Type());
		return 0;
		}

	const PDict(TableEntryVal)* tbl = AsTable();
	IterCookie* c = tbl->InitForIteration();

	HashKey* k;
	TableEntryVal* v;
	while ( (v = tbl->NextEntry(k, c)) )
		{
		Unref(t->Delete(k));
		delete k;
		}

	return 1;
	}

int TableVal::ExpandAndInit(Val* index, Val* new_val)
	{
	BroType* index_type = index->Type();

	if ( index_type->IsSet() )
		{
		Val* new_index = index->AsTableVal()->ConvertToList();
		Unref(index);
		return ExpandAndInit(new_index, new_val);
		}

	if ( index_type->Tag() != TYPE_LIST )
		// Nothing to expand.
		return CheckAndAssign(index, new_val);

	ListVal* iv = index->AsListVal();
	if ( iv->BaseTag() != TYPE_ANY )
		{
		if ( table_type->Indices()->Types()->length() != 1 )
			internal_error("bad singleton list index");

		for ( int i = 0; i < iv->Length(); ++i )
			if ( ! ExpandAndInit(iv->Index(i), new_val ? new_val->Ref() : 0) )
				return 0;

		Unref(new_val);
		return 1;
		}

	else
		{ // Compound table.
		val_list* vl = iv->Vals();
		loop_over_list(*vl, i)
			{
			// ### if CompositeHash::ComputeHash did flattening
			// of 1-element lists (like ComputeSingletonHash does),
			// then we could optimize here.
			BroType* t = (*vl)[i]->Type();
			if ( t->IsSet() || t->Tag() == TYPE_LIST )
				break;
			}

		if ( i >= vl->length() )
			// Nothing to expand.
			return CheckAndAssign(index, new_val);
		else
			{
			int result = ExpandCompoundAndInit(vl, i, new_val);
			Unref(new_val);
			return result;
			}
		}
	}

Val* TableVal::Lookup(const Val* index) const
	{
	if ( subnets )
		{
		TableEntryVal* v = (TableEntryVal*) subnets->Lookup(index);
		return v ? v->Value() : 0;
		}
	
	const PDict(TableEntryVal)* tbl = AsTable();

	if ( tbl->Length() > 0 )
		{
		HashKey* k = ComputeHash(index);
		if ( k )
			{
			TableEntryVal* v = AsTable()->Lookup(k);
			delete k;

			if ( v )
				{
				if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
					v->SetExpireAccess(network_time);

				return v->Value();
				}
			}
		}

	return 0;
	}

ListVal* TableVal::RecoverIndex(const HashKey* k)
	{
	return table_hash->RecoverVals(k);
	}

Val* TableVal::Delete(const Val* index)
	{
	HashKey* k = ComputeHash(index);
	TableEntryVal* v = k ? AsTable()->RemoveEntry(k) : 0;
	Val* va = v ? v->Value() : 0;

	if ( subnets && ! subnets->Remove(index) )
		internal_error( "index not in prefix table" );
	
	delete k;
	delete v;

	return va;
	}

Val* TableVal::Delete(const HashKey* k)
	{
	TableEntryVal* v = AsTable()->RemoveEntry(k);
	Val* va = v ? v->Value() : 0;

	if ( subnets )
		{
		Val* index = table_hash->RecoverVals(k);
		if ( ! subnets->Remove(index) )
			internal_error( "index not in prefix table" );
		Unref(index);
		}
		
	delete v;

	return va;
	}

ListVal* TableVal::ConvertToList(TypeTag t) const
	{
	ListVal* l = new ListVal(t);

	const PDict(TableEntryVal)* tbl = AsTable();
	IterCookie* c = tbl->InitForIteration();

	HashKey* k;
	TableEntryVal* v;
	while ( (v = tbl->NextEntry(k, c)) )
		{
		ListVal* index = table_hash->RecoverVals(k);

		if ( t == TYPE_ANY )
			l->Append(index);
		else
			{
			// We're expecting a pure list, flatten the
			// ListVal.
			if ( index->Length() != 1 )
				Internal("bad index in TableVal::ConvertToList");
			Val* flat_v = index->Index(0)->Ref();
			Unref(index);
			l->Append(flat_v);
			}

		delete k;
		}

	return l;
	}

ListVal* TableVal::ConvertToPureList() const
	{
	type_list* tl = table_type->Indices()->Types();
	if ( tl->length() != 1 )
		Internal("bad index type in TableVal::ConvertToPureList");

	return ConvertToList((*tl)[0]->Tag());
	}

void TableVal::Describe(ODesc* d) const
	{
	const PDict(TableEntryVal)* tbl = AsTable();
	int n = tbl->Length();

	if ( d->IsBinary() || d->IsPortable() )
		{
		table_type->Describe(d);
		d->SP();
		d->Add(n);
		d->SP();
		}

	if ( d->IsPortable() || d->IsReadable() )
		{
		d->Add("{");
		if ( ! table_type->IsSet() )
			d->PushIndent();
		}

	IterCookie* c = tbl->InitForIteration();

	for ( int i = 0; i < n; ++i )
		{
		HashKey* k;
		TableEntryVal* v = tbl->NextEntry(k, c);

		if ( ! v )
			internal_error("hash table underflow in TableVal::Describe");

		ListVal* vl = table_hash->RecoverVals(k);
		int dim = vl->Length();

		if ( i > 0 )
			{
			if ( ! d->IsBinary() )
				d->Add(",");

			if ( ! table_type->IsSet() )
				d->NL();
			}

		if ( d->IsReadable() )
			{
			if ( dim != 1 || ! table_type->IsSet() )
				d->Add("[");
			}
		else
			{
			d->Add(dim);
			d->SP();
			}

		vl->Describe(d);

		delete k;
		Unref(vl);

		if ( table_type->IsSet() )
			{ // We're a set, not a table.
			if ( d->IsReadable() )
				if ( dim != 1 )
					d->AddSP("]");
			}
		else
			{
			if ( d->IsReadable() )
				d->AddSP("] =");
			v->Value()->Describe(d);
			}
		}

	if ( tbl->NextEntry(c) )
		internal_error("hash table overflow in TableVal::Describe");

	if ( d->IsPortable() || d->IsReadable() )
		{
		if ( ! table_type->IsSet() )
			d->PopIndent();
		d->Add("}");
		}
	}

int TableVal::ExpandCompoundAndInit(val_list* vl, int k, Val* new_val)
	{
	Val* ind_k_v = (*vl)[k];
	ListVal* ind_k = ind_k_v->Type()->IsSet() ?
				ind_k_v->AsTableVal()->ConvertToList() :
				ind_k_v->AsListVal();

	for ( int i = 0; i < ind_k->Length(); ++i )
		{
		Val* ind_k_i = ind_k->Index(i);
		ListVal* expd = new ListVal(TYPE_ANY);
		loop_over_list(*vl, j)
			{
			if ( j == k )
				expd->Append(ind_k_i->Ref());
			else
				expd->Append((*vl)[j]->Ref());
			}

		int success = ExpandAndInit(expd, new_val ? new_val->Ref() : 0);
		Unref(expd);

		if ( ! success )
			return 0;
		}

	if ( ind_k_v->Type()->IsSet() )
		Unref(ind_k);

	return 1;
	}

int TableVal::CheckAndAssign(Val* index, Val* new_val)
	{
	Val* v = 0;
	if ( subnets )
		// We need an exact match here.
		v = (Val*) subnets->Lookup(index, true);
	else
		v = Lookup(index);
	
	if ( v )
		index->Warn("multiple initializations for index");
	
	return Assign(index, new_val);
	}

void TableVal::InitTimer()
	{
	timer = new TableValTimer(this, network_time + table_expire_interval);
	timer_mgr->Add(timer);
	}

void TableVal::DoExpire(double t)
	{
	if ( ! type )
		return; // FIX ME ###

	PDict(TableEntryVal)* tbl = AsTable();
	IterCookie* c = tbl->InitForIteration();

	HashKey* k;
	TableEntryVal* v;

	// As we can't delete elements while iterating over the dictionary,
	// we first just collect the expired entries.

	hash_key_list expired;

	while ( (v = tbl->NextEntry(k, c)) )
		{
		if ( v->ExpireAccessTime() + expire_time < t )
			expired.append(k);
		else
			delete k;
		}

	loop_over_list(expired, i)
		{
		k = expired[i];
		TableEntryVal* v = AsTable()->Lookup(k);
		Val* val = v ? v->Value() : 0;

		if ( val && expire_func )
			{
			val_list* vl = new val_list;
			val->Ref();
			vl->append(val);

			double secs = expire_func->Call(vl)->AsInterval();

			if ( secs > 0 )
				{
				// User doesn't want us to expire this now.
				v->SetExpireAccess(network_time - expire_time + secs);
				continue;
				}
			}

		if ( subnets )
			{
			Val* index = RecoverIndex(k);
			if ( ! subnets->Remove(index) )
				internal_error( "index not in prefix table" );
			Unref(index);
			}
		
		tbl->RemoveEntry(k);
		delete k;
		delete v;

		Unref(val);
		}
	}

bool TableVal::DoSerialize(Serializer* s) const
	{
	const PDict(TableEntryVal)* tbl = AsTable();
	IterCookie* c = tbl->InitForIteration();

	HashKey* k;
	TableEntryVal* v;

	s->WriteOpenTag(table_type->IsSet() ? "set" : "table");
	s->Write(tbl->Length(), "len");

	// We have to store this explicitly as we may not know
	// the type during unserialization.
	s->Write(bool(table_type->IsSet()));

	while ( (v = tbl->NextEntry(k, c)) )
		{
		// Serialize index.
		ListVal* index = table_hash->RecoverVals(k)->AsListVal();
		delete k;

		s->WriteOpenTag("key");
		s->Write((char)index->BaseTag());

		const val_list* vals = index->Vals();
	  	s->Write(vals->length());

		loop_over_list(*vals, i)
			{
			if ( ! (*vals)[i]->Serialize(s) )
				return false;
			}

		Unref(index);

		s->WriteCloseTag("key");

		// Serialize data.

		s->WriteOpenTag("value");
		if ( ! type->IsSet() && ! v->val->Serialize(s) )
			 return false;

		s->Write(v->last_access_time, "lastaccess");
		s->Write(v->expire_access_time, "expireaccess");
		s->WriteCloseTag("value");
		}

	s->WriteCloseTag(table_type->IsSet() ? "set" : "table");

	return true;
	}

bool TableVal::DoUnserialize(Serializer* s)
	{
	// This is slightly more complex than one would expect due to
	// the case where we don't know the type of the index (i.e.,
	// we do not know the type declaration).
	const type_list* index_types = ((TableType*) type)->IndexTypes();
	bool have_type = index_types->length() != 0;

	TypeList* prov_types = 0;
	if ( have_type )
		{ // table_set is not set correctly yet
		delete table_type;
		delete table_hash;
		table_type = (TableType*) type;
		table_hash = new CompositeHash(table_type->Indices());
		}
	else
		{
		// We have to build a provisional type list for hashing.
		prov_types = new TypeList;
		table_hash = 0;
		}

	int count;
	if ( ! s->Read(&count) )
		return false;

	bool is_set;
	if ( ! s->Read(&is_set) )
		return false;

	while ( count-- )
		{
		char tag;
		if ( ! s->Read(&tag) )
			return false;

		// Unserialize index.
		ListVal* index = new ListVal((TypeTag)tag);

		int len;
		if ( ! s->Read(&len) )
			return false;

		if ( have_type && index_types->length() != len )
			{
			s->Error("type mismatch, wrong index size");
			return false;
			}

		for ( int i = 0; i < len; ++i )
			{
			Val* v = Val::Unserialize(s,
					have_type ? (*index_types)[i] : 0);
			if ( ! v )
				return false;

			index->Append(v);

			if ( ! table_hash )
				prov_types->Append(v->Type());
			}

		// Unserialize data.
		Val* entry;
		if ( ! is_set )
			{
			entry = Val::Unserialize(s,
					have_type ? type->YieldType() : 0);
			if ( ! entry )
				return false;
			}
		else
			entry = index->Ref();

		TableEntryVal* entry_val = new TableEntryVal(entry);
		if ( ! s->Read(&entry_val->last_access_time) ||
		     ! s->Read(&entry_val->expire_access_time) )
			return false;

		if ( ! table_hash )
			table_hash = new CompositeHash(prov_types);

		AsTable()->Insert(ComputeHash(index), entry_val);

		if ( subnets )
			subnets->Insert(index, entry_val);
		}

	return true;
	}

RecordVal::RecordVal(RecordType* t) : Val(t)
	{
	record_type = t;
	val_list* vl = val.val_list_val = new val_list;
	int n = record_type->NumFields();

	// Initialize to default values from RecordType (which are nil
	// by default).
	for ( int i = 0; i < n; ++i )
		{
		Attributes* a = record_type->FieldDecl(i)->attrs;
		Attr* def_attr = a ? a->FindAttr(ATTR_DEFAULT) : 0;
		Val* def = def_attr ? def_attr->AttrExpr()->Eval(0) : 0;

		if ( def )
			def->Ref();

		else if ( record_type->FieldDecl(i)->type->Tag() == TYPE_RECORD )
			def = new RecordVal(record_type->FieldDecl(i)->type->AsRecordType());

		vl->append(def ? def->Ref() : 0);

		Unref(def);
		}
	}

RecordVal::~RecordVal()
	{
	delete_vals(AsRecord());
	}

void RecordVal::Assign(int field, Val* new_val)
	{
	Val* old_val = AsRecord()->replace(field, new_val);
	Unref(old_val);
	}

Val* RecordVal::Lookup(int field) const
	{
	return (*AsRecord())[field];
	}

void RecordVal::Describe(ODesc* d) const
	{
	const val_list* vl = AsRecord();
	int n = vl->length();

	if ( d->IsBinary() || d->IsPortable() )
		{
		record_type->Describe(d);
		d->SP();
		d->Add(n);
		d->SP();
		}
	else
		d->Add("[");

	loop_over_list(*vl, i)
		{
		if ( ! d->IsBinary() && i > 0 )
			d->Add(", ");

		d->Add(record_type->FieldName(i));

		if ( ! d->IsBinary() )
			d->Add("=");

		Val* v = (*vl)[i];
		if ( v )
			v->Describe(d);
		else
			d->Add("<uninitialized>");
		}

	if ( d->IsReadable() )
		d->Add("]");
	}

bool RecordVal::DoSerialize(Serializer* s) const
	{
	// It would be nice to use the type name as a tag here.
	s->WriteOpenTag("record");
	s->Write(val.val_list_val->length());

	loop_over_list(*val.val_list_val, i)
		{
		const char* field = "unknown_field";

		if ( Type()->AsRecordType()->NumFields() )
			field = Type()->AsRecordType()->FieldName(i);

		s->WriteOpenTag(field);

		Val* v = (*val.val_list_val)[i];
		if ( ! v )
			// Optional record entries may be null.
			s->Write('0');
		else
			{
			s->Write('1');
			if ( ! (*val.val_list_val)[i]->Serialize(s) )
				return false;
			}

		s->WriteCloseTag(field);
		}

	s->WriteCloseTag("record");

	return true;
	}

bool RecordVal::DoUnserialize(Serializer* s)
	{
	bool have_type = ((RecordType*) type)->NumFields() != 0;

	if ( have_type )
		{
		delete record_type;
		record_type = (RecordType*) type;
		origin = 0;
		}

	int len;
	if ( ! s->Read(&len) )
		return false;

	if ( have_type && record_type->NumFields() != len )
		{
		s->Error("type mismatch, wrong record size");
		return false;
		}

	for ( int i = 0; i < len; ++i )
		{
		char nonzero;
		if ( ! s->Read(&nonzero) )
			return false;

		if ( nonzero == '1' )
			{
			Val* v = Val::Unserialize(s,
				have_type ? record_type->FieldType(i) : 0);

			if ( ! v )
				return false;
			else
				AsRecord()->append(v);
			}

		else
			AsRecord()->append(0);
		}

	return true;
	}

void EnumVal::ValDescribe(ODesc* d) const
	{
	d->Add(type->AsEnumType()->Lookup(val.int_val));
	}


Val* recover_val(void* ptr, const BroType* t, int& n)
	{
	TypeTag tag = t->Tag();
	InternalTypeTag it = t->InternalType();

	switch ( it ) {
	case TYPE_INTERNAL_INT:
		{
		int ival = *((int*) ptr);
		n = sizeof(int);
		return new Val(ival, tag);
		}

	case TYPE_INTERNAL_UNSIGNED:
		{
		unsigned int uval = *((unsigned int*) ptr);
		n = sizeof(unsigned int);

		switch ( tag ) {
		case TYPE_COUNT:
		case TYPE_COUNTER:
			return new Val(uval, tag);

		case TYPE_PORT:
			return new PortVal(uval);

		default:
			internal_error("bad internal unsigned int in recover_val()");
			return 0;
		}
		}

	case TYPE_INTERNAL_DOUBLE:
		{
		double dval = *((double*) ptr);
		n = sizeof(double);

		if ( tag == TYPE_INTERVAL )
			return new IntervalVal(dval, 1.0);
		else
			return new Val(dval, tag);
		}

	case TYPE_INTERNAL_ADDR:
		{
#ifdef BROv6
		uint32* uval = ((uint32*) ptr);
		n = sizeof(uint32) * 4;
#else
		unsigned int uval = *((unsigned int*) ptr);
		n = sizeof(unsigned int);
#endif
		switch ( tag ) {
		case TYPE_ADDR:
			return new AddrVal(uval);

		case TYPE_NET:
			return new NetVal(uval);

		default:
			internal_error("bad internal address in recover_val()");
			return 0;
		}
		}

	case TYPE_INTERNAL_SUBNET:
		{
		subnet_type uval = *((subnet_type*) ptr);
		n = sizeof(subnet_type);
		return new SubNetVal(uval.net, uval.width);
		}

	case TYPE_INTERNAL_OTHER:
		{
		Val* v = (Val*) ptr;
		n = sizeof(Val*);

		if ( ! v || ! v->Type() )
			internal_error("bad aggregate Val in recover_val()");

		if ( ! same_type(v->Type(), t) )
			internal_error("inconsistent aggregate Val in recover_val()");

		return v->Ref();
		}

	case TYPE_INTERNAL_STRING:
		return new StringVal(new BroString((const byte_vec) ptr, n, 1));

	case TYPE_INTERNAL_ERROR:
		n = sizeof(Val*);
		return new Val();

	default:
		internal_error("bad internal type in recover_val()");
		return 0;
	}
	}

Val* check_and_promote(Val* v, const BroType* t, int is_init)
	{
	BroType* vt = v->Type();

	vt = flatten_type(vt);
	t = flatten_type(t);

	TypeTag t_tag = t->Tag();
	TypeTag v_tag = vt->Tag();

	if ( EitherArithmetic(t_tag, v_tag) )
		{
		if ( ! BothArithmetic(t_tag, v_tag) &&
		     (! IsArithmetic(v_tag) || t_tag != TYPE_TIME || ! v->IsZero()) )
			{
			if ( t_tag == TYPE_LIST || v_tag == TYPE_LIST )
				t->Error("list mixed with scalar", v);
			else
				t->Error("arithmetic mixed with non-arithmetic", v);
			Unref(v);
			return 0;
			}

		if ( v_tag == t_tag )
			return v;

		if ( t_tag != TYPE_TIME )
			{
			TypeTag mt = max_type(t_tag, v_tag);
			if ( mt != t_tag )
				{
				t->Error("over-promotion of arithmetic value", v);
				Unref(v);
				return 0;
				}
			}

		// Need to promote v to type t.
		InternalTypeTag it = t->InternalType();
		InternalTypeTag vit = vt->InternalType();

		if ( it == vit )
			// Already has the right internal type.
			return v;

		Val* promoted_v;
		switch ( it ) {
		case TYPE_INTERNAL_INT:
			promoted_v = new Val(v->CoerceToInt(), t_tag);
			break;

		case TYPE_INTERNAL_UNSIGNED:
			promoted_v = new Val(v->CoerceToUnsigned(), t_tag);
			break;

		case TYPE_INTERNAL_DOUBLE:
			promoted_v = new Val(v->CoerceToDouble(), t_tag);
			break;

		default:
			internal_error("bad internal type in check_and_promote()");
			Unref(v);
			return 0;
		}

		Unref(v);
		return promoted_v;
		}

	else if ( ! same_type(t, vt, is_init) )
		{
		t->Error("type clash", v);
		Unref(v);
		return 0;
		}

	return v;
	}

int same_val(const Val* /* v1 */, const Val* /* v2 */)
	{
	return 0;
	}

void describe_vals(const val_list* vals, ODesc* d, int offset)
	{
	if ( ! d->IsReadable() )
		{
		d->Add(vals->length());
		d->SP();
		}

	for ( int i = offset; i < vals->length(); ++i )
		{
		if ( i > offset && d->IsReadable() )
			d->Add(", ");

		(*vals)[i]->Describe(d);
		}
	}

void delete_vals(val_list* vals)
	{
	if ( vals )
		{
		loop_over_list(*vals, i)
			Unref((*vals)[i]);
		delete vals;
		}
	}
