zeek/src/Val.cc
Bernhard Amann 417542f283 Merge branch 'topic/bernhard/log-threads' into topic/bernhard/input-threads
Seems to work -- all test pass.
But there are thread-safety issues at the moment, because the constructors of IPAddr and IPPrefix are not thread-safe, but needed by workers.

Conflicts:
	src/logging/Manager.cc
2012-02-27 22:59:08 -08:00

3262 lines
66 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.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 "RemoteSerializer.h"
#include "PrefixTable.h"
#include "Conn.h"
#include "Reporter.h"
#include "IPAddr.h"
Val::Val(Func* f)
{
val.func_val = f;
type = f->FType()->Ref();
attribs = 0;
#ifdef DEBUG
bound_id = 0;
#endif
}
Val::Val(BroFile* f)
{
static FileType* string_file_type = 0;
if ( ! string_file_type )
string_file_type = new FileType(base_type(TYPE_STRING));
val.file_val = f;
assert(f->FType()->Tag() == TYPE_STRING);
type = string_file_type->Ref();
attribs = 0;
#ifdef DEBUG
bound_id = 0;
#endif
}
Val::~Val()
{
if ( type->InternalType() == TYPE_INTERNAL_STRING )
delete val.string_val;
else if ( type->Tag() == TYPE_FILE )
Unref(val.file_val);
Unref(type);
#ifdef DEBUG
Unref(bound_id);
#endif
}
Val* Val::Clone() const
{
SerializationFormat* form = new BinarySerializationFormat();
form->StartWrite();
CloneSerializer ss(form);
SerialInfo sinfo(&ss);
sinfo.cache = false;
this->Serialize(&sinfo);
char* data;
uint32 len = form->EndWrite(&data);
form->StartRead(data, len);
UnserialInfo uinfo(&ss);
uinfo.cache = false;
Val* clone = Unserialize(&uinfo, type);
delete [] data;
return clone;
}
bool Val::Serialize(SerialInfo* info) const
{
return SerialObj::Serialize(info);
}
Val* Val::Unserialize(UnserialInfo* info, TypeTag type, const BroType* exact_type)
{
Val* v = (Val*) SerialObj::Unserialize(info, SER_VAL);
if ( ! v )
return 0;
if ( type != TYPE_ANY && (v->Type()->Tag() != type
|| (exact_type && ! same_type(exact_type, v->Type()))) )
{
info->s->Error("type mismatch for value");
Unref(v);
return 0;
}
// For MutableVals, we may get a value which, by considering the
// globally unique ID, we already know. To keep references correct,
// we have to bind to the local version. (FIXME: This is not the
// nicest solution. Ideally, DoUnserialize() should be able to pass
// us an alternative ptr to the correct object.)
if ( v->IsMutableVal() )
{
MutableVal* mv = v->AsMutableVal();
if ( mv->HasUniqueID() )
{
ID* current =
global_scope()->Lookup(mv->UniqueID()->Name());
if ( current && current != mv->UniqueID() )
{
DBG_LOG(DBG_STATE, "binding to already existing ID %s\n", current->Name());
assert(current->ID_Val());
// Need to unset the ID here. Otherwise,
// when the SerializationCache destroys
// the value, the global name will disappear.
mv->SetID(0);
Unref(v);
return current->ID_Val()->Ref();
}
}
}
// An enum may be bound to a different internal number remotely than we
// do for the same identifier. Check if this is the case, and, if yes,
// rebind to our value.
if ( v->Type()->Tag() == TYPE_ENUM )
{
int rv = v->AsEnum();
EnumType* rt = v->Type()->AsEnumType();
const char* name = rt->Lookup(rv);
if ( name )
{
// See if we know the enum locally.
ID* local = global_scope()->Lookup(name);
if ( local && local->IsEnumConst() )
{
EnumType* lt = local->Type()->AsEnumType();
int lv = lt->Lookup(local->ModuleName(),
local->Name());
// Compare.
if ( rv != lv )
{
// Different, so let's bind the val
// to the local type.
v->val.int_val = lv;
Unref(rt);
v->type = lt;
::Ref(lt);
}
}
}
}
return v;
}
IMPLEMENT_SERIAL(Val, SER_VAL);
bool Val::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_VAL, BroObj);
if ( ! type->Serialize(info) )
return false;
SERIALIZE_OPTIONAL(attribs);
switch ( type->InternalType() ) {
case TYPE_INTERNAL_VOID:
info->s->Error("type is void");
return false;
case TYPE_INTERNAL_INT:
return SERIALIZE(val.int_val);
case TYPE_INTERNAL_UNSIGNED:
return SERIALIZE(val.uint_val);
case TYPE_INTERNAL_DOUBLE:
return SERIALIZE(val.double_val);
case TYPE_INTERNAL_STRING:
return SERIALIZE_STR((const char*) val.string_val->Bytes(),
val.string_val->Len());
case TYPE_INTERNAL_ADDR:
return SERIALIZE(*val.addr_val);
case TYPE_INTERNAL_SUBNET:
return SERIALIZE(*val.subnet_val);
case TYPE_INTERNAL_OTHER:
// Derived classes are responsible for this.
// Exception: Functions and files. There aren't any derived
// classes.
if ( type->Tag() == TYPE_FUNC )
if ( ! AsFunc()->Serialize(info) )
return false;
if ( type->Tag() == TYPE_FILE )
if ( ! AsFile()->Serialize(info) )
return false;
return true;
case TYPE_INTERNAL_ERROR:
info->s->Error("type is error");
return false;
default:
info->s->Error("type is out of range");
return false;
}
reporter->InternalError("should not be reached");
return false;
}
bool Val::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(BroObj);
if ( type )
Unref(type);
if ( ! (type = BroType::Unserialize(info)) )
return false;
UNSERIALIZE_OPTIONAL(attribs,
(RecordVal*) Val::Unserialize(info, TYPE_RECORD));
switch ( type->InternalType() ) {
case TYPE_INTERNAL_VOID:
info->s->Error("type is void");
return false;
case TYPE_INTERNAL_INT:
return UNSERIALIZE(&val.int_val);
case TYPE_INTERNAL_UNSIGNED:
return UNSERIALIZE(&val.uint_val);
case TYPE_INTERNAL_DOUBLE:
return UNSERIALIZE(&val.double_val);
case TYPE_INTERNAL_STRING:
const char* str;
int len;
if ( ! UNSERIALIZE_STR(&str, &len) )
return false;
val.string_val = new BroString((u_char*) str, len, 1);
delete [] str;
return true;
case TYPE_INTERNAL_ADDR:
{
val.addr_val = new IPAddr();
return UNSERIALIZE(val.addr_val);
}
case TYPE_INTERNAL_SUBNET:
{
val.subnet_val = new IPPrefix();
return UNSERIALIZE(val.subnet_val);
}
case TYPE_INTERNAL_OTHER:
// Derived classes are responsible for this.
// Exception: Functions and files. There aren't any derived
// classes.
if ( type->Tag() == TYPE_FUNC )
{
val.func_val = Func::Unserialize(info);
return val.func_val != 0;
}
else if ( type->Tag() == TYPE_FILE )
{
val.file_val = BroFile::Unserialize(info);
return val.file_val != 0;
}
return true;
case TYPE_INTERNAL_ERROR:
info->s->Error("type is error");
return false;
default:
info->s->Error("type out of range");
return false;
}
reporter->InternalError("should not be reached");
return false;
}
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;
}
}
bro_int_t 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 static_cast<bro_int_t>(val.uint_val);
else
InternalWarning("bad request for InternalInt");
return 0;
}
bro_uint_t Val::InternalUnsigned() const
{
if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
return val.uint_val;
else
InternalWarning("bad request for InternalUnsigned");
return 0;
}
double Val::InternalDouble() const
{
if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
return val.double_val;
else
InternalWarning("bad request for InternalDouble");
return 0.0;
}
bro_int_t Val::CoerceToInt() const
{
if ( type->InternalType() == TYPE_INTERNAL_INT )
return val.int_val;
else if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
return static_cast<bro_int_t>(val.uint_val);
else if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
return static_cast<bro_int_t>(val.double_val);
else
InternalWarning("bad request for CoerceToInt");
return 0;
}
bro_uint_t Val::CoerceToUnsigned() const
{
if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
return val.uint_val;
else if ( type->InternalType() == TYPE_INTERNAL_INT )
return static_cast<bro_uint_t>(val.int_val);
else if ( type->InternalType() == TYPE_INTERNAL_DOUBLE )
return static_cast<bro_uint_t>(val.double_val);
else
InternalWarning("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 static_cast<double>(val.int_val);
else if ( type->InternalType() == TYPE_INTERNAL_UNSIGNED )
return static_cast<double>(val.uint_val);
else
InternalWarning("bad request for CoerceToDouble");
return 0.0;
}
Val* Val::SizeVal() const
{
switch ( type->InternalType() ) {
case TYPE_INTERNAL_INT:
// Return abs value. However abs() only works on ints and llabs
// doesn't work on Mac OS X 10.5. So we do it by hand
if ( val.int_val < 0 )
return new Val(-val.int_val, TYPE_COUNT);
else
return new Val(val.int_val, TYPE_COUNT);
case TYPE_INTERNAL_UNSIGNED:
return new Val(val.uint_val, TYPE_COUNT);
case TYPE_INTERNAL_DOUBLE:
return new Val(fabs(val.double_val), TYPE_DOUBLE);
case TYPE_INTERNAL_OTHER:
if ( type->Tag() == TYPE_FUNC )
return new Val(val.func_val->FType()->ArgTypes()->Types()->length(), TYPE_COUNT);
if ( type->Tag() == TYPE_FILE )
return new Val(val.file_val->Size(), TYPE_DOUBLE);
break;
default:
break;
}
return new Val(0, TYPE_COUNT);
}
unsigned int Val::MemoryAllocation() const
{
return padded_sizeof(*this);
}
int Val::AddTo(Val* v, int is_first_init) const
{
Error("+= initializer only applies to aggregate values");
return 0;
}
int Val::RemoveFrom(Val* v) const
{
Error("-= initializer only applies to aggregate values");
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::DescribeReST(ODesc* d) const
{
ValDescribeReST(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(val.addr_val->AsString().c_str()); break;
case TYPE_INTERNAL_SUBNET:
d->Add(val.subnet_val->AsString().c_str());
break;
case TYPE_INTERNAL_ERROR: d->AddCS("error"); break;
case TYPE_INTERNAL_OTHER:
if ( type->Tag() == TYPE_FUNC )
AsFunc()->Describe(d);
else if ( type->Tag() == TYPE_FILE )
AsFile()->Describe(d);
else
d->Add("<no value description>");
break;
case TYPE_INTERNAL_VOID:
d->Add("<void value description>");
break;
default:
// Don't call Internal(), that'll loop!
reporter->InternalError("Val description unavailable");
}
}
void Val::ValDescribeReST(ODesc* d) const
{
switch ( type->InternalType() ) {
case TYPE_INTERNAL_OTHER:
Describe(d);
break;
default:
d->Add("``");
ValDescribe(d);
d->Add("``");
}
}
MutableVal::~MutableVal()
{
for ( list<ID*>::iterator i = aliases.begin(); i != aliases.end(); ++i )
{
if ( global_scope() )
global_scope()->Remove((*i)->Name());
(*i)->ClearVal(); // just to make sure.
Unref((*i));
}
if ( id )
{
if ( global_scope() )
global_scope()->Remove(id->Name());
id->ClearVal(); // just to make sure.
Unref(id);
}
}
bool MutableVal::AddProperties(Properties arg_props)
{
if ( (props | arg_props) == props )
// No change.
return false;
props |= arg_props;
if ( ! id )
Bind();
return true;
}
bool MutableVal::RemoveProperties(Properties arg_props)
{
if ( (props & ~arg_props) == props )
// No change.
return false;
props &= ~arg_props;
return true;
}
ID* MutableVal::Bind() const
{
static bool initialized = false;
assert(!id);
static unsigned int id_counter = 0;
static const int MAX_NAME_SIZE = 128;
static char name[MAX_NAME_SIZE];
static char* end_of_static_str = 0;
if ( ! initialized )
{
// Get local IP.
char host[MAXHOSTNAMELEN];
strcpy(host, "localhost");
gethostname(host, MAXHOSTNAMELEN);
host[MAXHOSTNAMELEN-1] = '\0';
#if 0
// We ignore errors.
struct hostent* ent = gethostbyname(host);
uint32 ip;
if ( ent && ent->h_addr_list[0] )
ip = *(uint32*) ent->h_addr_list[0];
else
ip = htonl(0x7f000001); // 127.0.0.1
safe_snprintf(name, MAX_NAME_SIZE, "#%s#%d#",
IPAddr(IPAddr::IPv4, &ip, IPAddr::Network)->AsString().c_str(),
getpid());
#else
safe_snprintf(name, MAX_NAME_SIZE, "#%s#%d#", host, getpid());
#endif
end_of_static_str = name + strlen(name);
initialized = true;
}
safe_snprintf(end_of_static_str, MAX_NAME_SIZE - (end_of_static_str - name),
"%u", ++id_counter);
name[MAX_NAME_SIZE-1] = '\0';
// DBG_LOG(DBG_STATE, "new unique ID %s", name);
id = new ID(name, SCOPE_GLOBAL, true);
id->SetType(const_cast<MutableVal*>(this)->Type()->Ref());
global_scope()->Insert(name, id);
id->SetVal(const_cast<MutableVal*>(this), OP_NONE, true);
return id;
}
void MutableVal::TransferUniqueID(MutableVal* mv)
{
const char* new_name = mv->UniqueID()->Name();
if ( ! id )
Bind();
DBG_LOG(DBG_STATE, "transfering ID (new %s, old/alias %s)", new_name, id->Name());
// Keep old name as alias.
aliases.push_back(id);
id = new ID(new_name, SCOPE_GLOBAL, true);
id->SetType(const_cast<MutableVal*>(this)->Type()->Ref());
global_scope()->Insert(new_name, id);
id->SetVal(const_cast<MutableVal*>(this), OP_NONE, true);
Unref(mv->id);
mv->id = 0;
}
IMPLEMENT_SERIAL(MutableVal, SER_MUTABLE_VAL);
bool MutableVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_MUTABLE_VAL, Val);
if ( ! SERIALIZE(props) )
return false;
// Don't use ID::Serialize here, that would loop. All we
// need is the name, anyway.
const char* name = id ? id->Name() : "";
if ( ! SERIALIZE(name) )
return false;
return true;
}
bool MutableVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
if ( ! UNSERIALIZE(&props) )
return false;
id = 0;
const char* name;
if ( ! UNSERIALIZE_STR(&name, 0) )
return false;
if ( *name )
{
id = new ID(name, SCOPE_GLOBAL, true);
id->SetVal(this, OP_NONE, true);
ID* current = global_scope()->Lookup(name);
if ( ! current )
{
global_scope()->Insert(name, id);
DBG_LOG(DBG_STATE, "installed formerly unknown ID %s", id->Name());
}
else
{
DBG_LOG(DBG_STATE, "got already known ID %s", current->Name());
// This means that we already know the value and
// that in fact we should bind to the local value.
// Val::Unserialize() will take care of this.
}
}
delete [] name;
return true;
}
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")
}
IMPLEMENT_SERIAL(IntervalVal, SER_INTERVAL_VAL);
bool IntervalVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_INTERVAL_VAL, Val);
return true;
}
bool IntervalVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
PortVal::PortVal(uint32 p, TransportProto port_type) : Val(TYPE_PORT)
{
// Note, for ICMP one-way connections:
// src_port = icmp_type, dst_port = icmp_code.
if ( p >= 65536 )
{
InternalWarning("bad port number");
p = 0;
}
switch ( port_type ) {
case TRANSPORT_TCP:
p |= TCP_PORT_MASK;
break;
case TRANSPORT_UDP:
p |= UDP_PORT_MASK;
break;
case TRANSPORT_ICMP:
p |= ICMP_PORT_MASK;
break;
default:
break; // "other"
}
val.uint_val = static_cast<bro_uint_t>(p);
}
PortVal::PortVal(uint32 p) : Val(TYPE_PORT)
{
if ( p >= 65536 * NUM_PORT_SPACES )
{
InternalWarning("bad port number");
p = 0;
}
val.uint_val = static_cast<bro_uint_t>(p);
}
uint32 PortVal::Port() const
{
uint32 p = static_cast<uint32>(val.uint_val);
return p & ~PORT_SPACE_MASK;
}
int PortVal::IsTCP() const
{
return (val.uint_val & PORT_SPACE_MASK) == TCP_PORT_MASK;
}
int PortVal::IsUDP() const
{
return (val.uint_val & PORT_SPACE_MASK) == UDP_PORT_MASK;
}
int PortVal::IsICMP() const
{
return (val.uint_val & PORT_SPACE_MASK) == ICMP_PORT_MASK;
}
void PortVal::ValDescribe(ODesc* d) const
{
uint32 p = static_cast<uint32>(val.uint_val);
d->Add(p & ~PORT_SPACE_MASK);
if ( IsUDP() )
d->Add("/udp");
else if ( IsTCP() )
d->Add("/tcp");
else if ( IsICMP() )
d->Add("/icmp");
else
d->Add("/unknown");
}
IMPLEMENT_SERIAL(PortVal, SER_PORT_VAL);
bool PortVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_PORT_VAL, Val);
return true;
}
bool PortVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
AddrVal::AddrVal(const char* text) : Val(TYPE_ADDR)
{
val.addr_val = new IPAddr(text);
}
AddrVal::AddrVal(uint32 addr) : Val(TYPE_ADDR)
{
// ### perhaps do gethostbyaddr here?
val.addr_val = new IPAddr(IPAddr::IPv4, &addr, IPAddr::Network);
}
AddrVal::AddrVal(const uint32 addr[4]) : Val(TYPE_ADDR)
{
val.addr_val = new IPAddr(IPAddr::IPv6, addr, IPAddr::Network);
}
AddrVal::AddrVal(const IPAddr& addr) : Val(TYPE_ADDR)
{
val.addr_val = new IPAddr(addr);
}
AddrVal::~AddrVal()
{
delete val.addr_val;
}
unsigned int AddrVal::MemoryAllocation() const
{
return padded_sizeof(*this) + val.addr_val->MemoryAllocation();
}
Val* AddrVal::SizeVal() const
{
if ( val.addr_val->GetFamily() == IPAddr::IPv4 )
return new Val(32, TYPE_COUNT);
else
return new Val(128, TYPE_COUNT);
}
IMPLEMENT_SERIAL(AddrVal, SER_ADDR_VAL);
bool AddrVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_ADDR_VAL, Val);
return true;
}
bool AddrVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
SubNetVal::SubNetVal(const char* text) : Val(TYPE_SUBNET)
{
string s(text);
size_t slash_loc = s.find('/');
if ( slash_loc == string::npos )
{
reporter->Error("Bad string in SubNetVal ctor: %s", text);
val.subnet_val = new IPPrefix();
}
else
{
val.subnet_val = new IPPrefix(s.substr(0, slash_loc),
atoi(s.substr(slash_loc + 1).c_str()));
}
}
SubNetVal::SubNetVal(const char* text, int width) : Val(TYPE_SUBNET)
{
val.subnet_val = new IPPrefix(text, width);
}
SubNetVal::SubNetVal(uint32 addr, int width) : Val(TYPE_SUBNET)
{
IPAddr a(IPAddr::IPv4, &addr, IPAddr::Network);
val.subnet_val = new IPPrefix(a, width);
}
SubNetVal::SubNetVal(const uint32* addr, int width) : Val(TYPE_SUBNET)
{
IPAddr a(IPAddr::IPv6, addr, IPAddr::Network);
val.subnet_val = new IPPrefix(a, width);
}
SubNetVal::SubNetVal(const IPAddr& addr, int width) : Val(TYPE_SUBNET)
{
val.subnet_val = new IPPrefix(addr, width);
}
SubNetVal::SubNetVal(const IPPrefix& prefix) : Val(TYPE_SUBNET)
{
val.subnet_val = new IPPrefix(prefix);
}
SubNetVal::~SubNetVal()
{
delete val.subnet_val;
}
unsigned int SubNetVal::MemoryAllocation() const
{
return padded_sizeof(*this) + val.subnet_val->MemoryAllocation();
}
Val* SubNetVal::SizeVal() const
{
int retained = 128 - val.subnet_val->LengthIPv6();
return new Val(pow(2.0, double(retained)), TYPE_DOUBLE);
}
void SubNetVal::ValDescribe(ODesc* d) const
{
d->Add(string(*val.subnet_val).c_str());
}
IPAddr SubNetVal::Mask() const
{
if ( val.subnet_val->Length() == 0 )
{
// We need to special-case a mask width of zero, since
// the compiler doesn't guarantee that 1 << 32 yields 0.
uint32 m[4];
for ( unsigned int i = 0; i < 4; ++i )
m[i] = 0;
IPAddr rval(IPAddr::IPv6, m, IPAddr::Host);
return rval;
}
uint32 m[4];
uint32* mp = m;
uint32 w;
for ( w = val.subnet_val->Length(); w >= 32; w -= 32 )
*(mp++) = 0xffffffff;
*mp = ~((1 << (32 - w)) - 1);
while ( ++mp < m + 4 )
*mp = 0;
IPAddr rval(IPAddr::IPv6, m, IPAddr::Host);
return rval;
}
bool SubNetVal::Contains(const IPAddr& addr) const
{
IPAddr a(addr);
return val.subnet_val->Contains(a);
}
IMPLEMENT_SERIAL(SubNetVal, SER_SUBNET_VAL);
bool SubNetVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_SUBNET_VAL, Val);
return true;
}
bool SubNetVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
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 ? ###
if ( d->WantQuotes() )
d->Add("\"");
d->AddBytes(val.string_val);
if ( d->WantQuotes() )
d->Add("\"");
}
unsigned int StringVal::MemoryAllocation() const
{
return padded_sizeof(*this) + val.string_val->MemoryAllocation();
}
IMPLEMENT_SERIAL(StringVal, SER_STRING_VAL);
bool StringVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_STRING_VAL, Val);
return true;
}
bool StringVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
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("/");
}
unsigned int PatternVal::MemoryAllocation() const
{
return padded_sizeof(*this) + val.re_val->MemoryAllocation();
}
IMPLEMENT_SERIAL(PatternVal, SER_PATTERN_VAL);
bool PatternVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_PATTERN_VAL, Val);
return AsPattern()->Serialize(info);
}
bool PatternVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
val.re_val = RE_Matcher::Unserialize(info);
return val.re_val != 0;
}
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);
Unref(s);
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);
}
}
IMPLEMENT_SERIAL(ListVal, SER_LIST_VAL);
bool ListVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_LIST_VAL, Val);
if ( ! (SERIALIZE(char(tag)) && SERIALIZE(vals.length())) )
return false;
loop_over_list(vals, i)
{
if ( ! vals[i]->Serialize(info) )
return false;
}
return true;
}
bool ListVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
char t;
int len;
if ( ! (UNSERIALIZE(&t) && UNSERIALIZE(&len)) )
return false;
tag = TypeTag(t);
while ( len-- )
{
Val* v = Val::Unserialize(info, TYPE_ANY);
if ( ! v )
return false;
vals.append(v);
}
// Our dtor will do Unref(type) in addition to Val's dtor.
if ( type )
type->Ref();
return true;
}
unsigned int ListVal::MemoryAllocation() const
{
unsigned int size = 0;
loop_over_list(vals, i)
size += vals[i]->MemoryAllocation();
return size + padded_sizeof(*this) + vals.MemoryAllocation() - padded_sizeof(vals)
+ type->MemoryAllocation();
}
TableValTimer::TableValTimer(TableVal* val, double t) : Timer(t, TIMER_TABLE_VAL)
{
table = val;
}
TableValTimer::~TableValTimer()
{
table->ClearTimer(this);
}
void TableValTimer::Dispatch(double t, int is_expire)
{
if ( ! is_expire )
{
table->ClearTimer(this);
table->DoExpire(t);
}
}
static void table_entry_val_delete_func(void* val)
{
TableEntryVal* tv = (TableEntryVal*) val;
tv->Unref();
delete tv;
}
TableVal::TableVal(TableType* t, Attributes* a) : MutableVal(t)
{
Init(t);
SetAttrs(a);
}
void TableVal::Init(TableType* t)
{
::Ref(t);
table_type = t;
expire_expr = 0;
expire_time = 0;
expire_cookie = 0;
timer = 0;
def_val = 0;
if ( t->IsSubNetIndex() )
subnets = new PrefixTable;
else
subnets = 0;
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_mgr->Cancel(timer);
Unref(table_type);
delete table_hash;
delete AsTable();
delete subnets;
Unref(attrs);
Unref(def_val);
Unref(expire_expr);
}
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);
}
int TableVal::RecursiveSize() const
{
int n = AsTable()->Length();
if ( Type()->IsSet() ||
const_cast<TableType*>(Type()->AsTableType())->YieldType()->Tag()
!= TYPE_TABLE )
return n;
PDict(TableEntryVal)* v = val.table_val;
IterCookie* c = v->InitForIteration();
TableEntryVal* tv;
while ( (tv = v->NextEntry(c)) )
{
if ( tv->Value() )
n += tv->Value()->AsTableVal()->RecursiveSize();
}
return n;
}
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_expr = ef->AttrExpr();
expire_expr->Ref();
}
}
void TableVal::CheckExpireAttr(attr_tag at)
{
Attr* a = attrs->FindAttr(at);
if ( a )
{
Val* timeout = a->AttrExpr()->Eval(0);
if ( ! timeout )
{
a->AttrExpr()->Error("value of timeout not fixed");
return;
}
expire_time = timeout->AsInterval();
if ( timer )
timer_mgr->Cancel(timer);
// 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, Opcode op)
{
HashKey* k = ComputeHash(index);
if ( ! k )
{
Unref(new_val);
index->Error("index type doesn't match table", table_type->Indices());
return 0;
}
return Assign(index, k, new_val, op);
}
int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
{
int is_set = table_type->IsSet();
if ( (is_set && new_val) || (! is_set && ! new_val) )
InternalWarning("bad set/table in TableVal::Assign");
BroType* yt = Type()->AsTableType()->YieldType();
if ( yt && yt->Tag() == TYPE_TABLE &&
new_val->AsTableVal()->FindAttr(ATTR_MERGEABLE) )
{
// Join two mergeable sets.
Val* old = Lookup(index, false);
if ( old && old->AsTableVal()->FindAttr(ATTR_MERGEABLE) )
{
if ( LoggingAccess() && op != OP_NONE )
StateAccess::Log(new StateAccess(OP_ASSIGN_IDX,
this, index, new_val, old));
new_val->AsTableVal()->AddTo(old->AsTableVal(), 0, false);
Unref(new_val);
return 1;
}
}
TableEntryVal* new_entry_val = new TableEntryVal(new_val);
TableEntryVal* old_entry_val = AsNonConstTable()->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);
}
if ( LoggingAccess() && op != OP_NONE )
{
Val* rec_index = 0;
if ( ! index )
index = rec_index = RecoverIndex(k);
if ( new_val )
{
// A table.
if ( new_val->IsMutableVal() )
new_val->AsMutableVal()->AddProperties(GetProperties());
bool unref_old_val = false;
Val* old_val = old_entry_val ?
old_entry_val->Value() : 0;
if ( op == OP_INCR && ! old_val )
// If it's an increment, somebody has already
// checked that the index is there. If it's
// not, that can only be due to using the
// default.
{
old_val = Default(index);
unref_old_val = true;
}
assert(op != OP_INCR || old_val);
StateAccess::Log(
new StateAccess(
op == OP_INCR ?
OP_INCR_IDX : OP_ASSIGN_IDX,
this, index, new_val, old_val));
if ( unref_old_val )
Unref(old_val);
}
else
{
// A set.
if ( old_entry_val && remote_check_sync_consistency )
{
Val* has_old_val = new Val(1, TYPE_INT);
StateAccess::Log(
new StateAccess(OP_ADD, this, index,
has_old_val));
Unref(has_old_val);
}
else
StateAccess::Log(
new StateAccess(OP_ADD, this,
index, 0, 0));
}
if ( rec_index )
Unref(rec_index);
}
// 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;
}
Modified();
return 1;
}
int TableVal::AddTo(Val* val, int is_first_init) const
{
return AddTo(val, is_first_init, true);
}
int TableVal::AddTo(Val* val, int is_first_init, bool propagate_ops) 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,
propagate_ops ? OP_ASSIGN : OP_NONE) )
return 0;
}
else
{
v->Ref();
if ( ! t->Assign(0, k, v->Value(),
propagate_ops ? OP_ASSIGN : OP_NONE) )
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)) )
{
Val* index = RecoverIndex(k);
Unref(index);
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 )
reporter->InternalError("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::Default(Val* index)
{
Attr* def_attr = FindAttr(ATTR_DEFAULT);
if ( ! def_attr )
return 0;
if ( ! def_val )
{
BroType* ytype = Type()->YieldType();
BroType* dtype = def_attr->AttrExpr()->Type();
if ( dtype->Tag() == TYPE_RECORD && ytype->Tag() == TYPE_RECORD &&
! same_type(dtype, ytype) &&
record_promotion_compatible(dtype->AsRecordType(),
ytype->AsRecordType()) )
{
Expr* coerce = new RecordCoerceExpr(def_attr->AttrExpr(), ytype->AsRecordType());
def_val = coerce->Eval(0);
Unref(coerce);
}
else
def_val = def_attr->AttrExpr()->Eval(0);
}
if ( ! def_val )
{
Error("non-constant default attribute");
return 0;
}
if ( def_val->Type()->Tag() != TYPE_FUNC ||
same_type(def_val->Type(), Type()->YieldType()) )
return def_val->Ref();
const Func* f = def_val->AsFunc();
val_list* vl = new val_list();
if ( index->Type()->Tag() == TYPE_LIST )
{
const val_list* vl0 = index->AsListVal()->Vals();
loop_over_list(*vl0, i)
vl->append((*vl0)[i]->Ref());
}
else
vl->append(index->Ref());
Val* result = 0;
try
{
result = f->Call(vl);
}
catch ( InterpreterException& e )
{ /* Already reported. */ }
delete vl;
if ( ! result )
{
Error("no value returned from &default function");
return 0;
}
return result;
}
Val* TableVal::Lookup(Val* index, bool use_default_val)
{
static Val* last_default = 0;
if ( last_default )
{
Unref(last_default);
last_default = 0;
}
if ( subnets )
{
TableEntryVal* v = (TableEntryVal*) subnets->Lookup(index);
if ( v )
return v->Value() ? v->Value() : this;
if ( ! use_default_val )
return 0;
Val* def = Default(index);
last_default = def;
return def;
}
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_WRITE) ||
attrs->FindAttr(ATTR_EXPIRE_CREATE)) )
{
v->SetExpireAccess(network_time);
if ( LoggingAccess() && expire_time )
ReadOperation(index, v);
}
return v->Value() ? v->Value() : this;
}
}
}
if ( ! use_default_val )
return 0;
Val* def = Default(index);
last_default = def;
return def;
}
bool TableVal::UpdateTimestamp(Val* index)
{
TableEntryVal* v;
if ( subnets )
v = (TableEntryVal*) subnets->Lookup(index);
else
{
HashKey* k = ComputeHash(index);
if ( ! k )
return false;
v = AsTable()->Lookup(k);
delete k;
}
if ( ! v )
return false;
v->SetExpireAccess(network_time);
if ( attrs->FindAttr(ATTR_EXPIRE_READ) )
ReadOperation(index, v);
return true;
}
ListVal* TableVal::RecoverIndex(const HashKey* k) const
{
return table_hash->RecoverVals(k);
}
Val* TableVal::Delete(const Val* index)
{
HashKey* k = ComputeHash(index);
TableEntryVal* v = k ? AsNonConstTable()->RemoveEntry(k) : 0;
Val* va = v ? (v->Value() ? v->Value() : this->Ref()) : 0;
if ( subnets && ! subnets->Remove(index) )
reporter->InternalError("index not in prefix table");
if ( LoggingAccess() )
{
if ( v )
{
if ( v->Value() && remote_check_sync_consistency )
// A table.
StateAccess::Log(
new StateAccess(OP_DEL, this,
index, v->Value()));
else
{
// A set.
Val* has_old_val = new Val(1, TYPE_INT);
StateAccess::Log(
new StateAccess(OP_DEL, this, index,
has_old_val));
Unref(has_old_val);
}
}
else
StateAccess::Log(
new StateAccess(OP_DEL, this, index, 0));
}
delete k;
delete v;
Modified();
return va;
}
Val* TableVal::Delete(const HashKey* k)
{
TableEntryVal* v = AsNonConstTable()->RemoveEntry(k);
Val* va = v ? (v->Value() ? v->Value() : this->Ref()) : 0;
if ( subnets )
{
Val* index = table_hash->RecoverVals(k);
if ( ! subnets->Remove(index) )
reporter->InternalError("index not in prefix table");
Unref(index);
}
delete v;
if ( LoggingAccess() )
StateAccess::Log(new StateAccess(OP_DEL, this, k));
Modified();
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 )
InternalWarning("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 )
{
InternalWarning("bad index type in TableVal::ConvertToPureList");
return 0;
}
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("{");
d->PushIndent();
}
IterCookie* c = tbl->InitForIteration();
for ( int i = 0; i < n; ++i )
{
HashKey* k;
TableEntryVal* v = tbl->NextEntry(k, c);
if ( ! v )
reporter->InternalError("hash table underflow in TableVal::Describe");
ListVal* vl = table_hash->RecoverVals(k);
int dim = vl->Length();
if ( i > 0 )
{
if ( ! d->IsBinary() )
d->Add(",");
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("] =");
if ( v->Value() )
v->Value()->Describe(d);
}
if ( d->IsReadable() && ! d->IsShort() && d->IncludeStats() )
{
d->Add(" @");
d->Add(fmt_access_time(v->ExpireAccessTime()));
}
}
if ( tbl->NextEntry(c) )
reporter->InternalError("hash table overflow in TableVal::Describe");
if ( d->IsPortable() || d->IsReadable() )
{
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, Opcode op)
{
Val* v = 0;
if ( subnets )
// We need an exact match here.
v = (Val*) subnets->Lookup(index, true);
else
v = Lookup(index, false);
if ( v )
index->Warn("multiple initializations for index");
return Assign(index, new_val, op);
}
void TableVal::InitTimer(double delay)
{
timer = new TableValTimer(this, network_time + delay);
timer_mgr->Add(timer);
}
void TableVal::DoExpire(double t)
{
if ( ! type )
return; // FIX ME ###
PDict(TableEntryVal)* tbl = AsNonConstTable();
if ( ! expire_cookie )
{
expire_cookie = tbl->InitForIteration();
tbl->MakeRobustCookie(expire_cookie);
}
HashKey* k = 0;
TableEntryVal* v = 0;
for ( int i = 0; i < table_incremental_step &&
(v = tbl->NextEntry(k, expire_cookie)); ++i )
{
if ( v->ExpireAccessTime() == 0 )
// This happens when we insert val while network_time
// hasn't been initialized yet (e.g. in bro_init()).
// We correct the timestamp now.
v->SetExpireAccess(network_time);
else if ( v->ExpireAccessTime() + expire_time < t )
{
Val* val = v ? v->Value() : 0;
if ( expire_expr )
{
Val* idx = RecoverIndex(k);
double secs = CallExpireFunc(idx);
// It's possible that the user-provided
// function modified or deleted the table
// value, so look it up again.
v = tbl->Lookup(k);
if ( ! v )
{ // user-provided function deleted it
delete k;
continue;
}
if ( secs > 0 )
{
// User doesn't want us to expire
// this now.
v->SetExpireAccess(network_time - expire_time + secs);
delete k;
continue;
}
}
if ( subnets )
{
Val* index = RecoverIndex(k);
if ( ! subnets->Remove(index) )
reporter->InternalError("index not in prefix table");
Unref(index);
}
if ( LoggingAccess() )
StateAccess::Log(
new StateAccess(OP_EXPIRE, this, k));
tbl->RemoveEntry(k);
delete v;
Unref(val);
Modified();
}
delete k;
}
if ( ! v )
{
expire_cookie = 0;
InitTimer(table_expire_interval);
}
else
InitTimer(table_expire_delay);
}
double TableVal::CallExpireFunc(Val* idx)
{
if ( ! expire_expr )
{
Unref(idx);
return 0;
}
val_list* vl = new val_list;
vl->append(Ref());
// Flatten lists of a single element.
if ( idx->Type()->Tag() == TYPE_LIST &&
idx->AsListVal()->Length() == 1 )
{
Val* old = idx;
idx = idx->AsListVal()->Index(0);
idx->Ref();
Unref(old);
}
vl->append(idx);
double secs;
try
{
Val* vs = expire_expr->Eval(0)->AsFunc()->Call(vl);
secs = vs->AsInterval();
Unref(vs);
delete vl;
}
catch ( InterpreterException& e )
{
secs = 0;
}
return secs;
}
void TableVal::ReadOperation(Val* index, TableEntryVal* v)
{
// In theory we need to only propagate one update per &read_expire
// interval to prevent peers from expiring intervals. To account for
// practical issues such as latency, we send one update every half
// &read_expire.
if ( network_time - v->LastReadUpdate() > expire_time / 2 )
{
StateAccess::Log(new StateAccess(OP_READ_IDX, this, index));
v->SetLastReadUpdate(network_time);
}
}
IMPLEMENT_SERIAL(TableVal, SER_TABLE_VAL);
// This is getting rather complex due to the ability to suspend even within
// deeply-nested values.
bool TableVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE_WITH_SUSPEND(SER_TABLE_VAL, MutableVal);
// The current state of the serialization.
struct State {
IterCookie* c;
TableEntryVal* v; // current value
bool did_index; // already wrote the val's index
}* state = 0;
PDict(TableEntryVal)* tbl =
const_cast<TableVal*>(this)->AsNonConstTable();
if ( info->cont.NewInstance() )
{
// For simplicity, we disable suspension for the objects
// serialized here. (In fact we know that *currently*
// they won't even try).
DisableSuspend suspend(info);
state = new State;
state->c = tbl->InitForIteration();
tbl->MakeRobustCookie(state->c);
state->v = 0;
state->did_index = false;
info->s->WriteOpenTag(table_type->IsSet() ? "set" : "table");
if ( ! SERIALIZE(expire_time) )
return false;
SERIALIZE_OPTIONAL(attrs);
SERIALIZE_OPTIONAL(expire_expr);
// Make sure nobody kills us in between.
const_cast<TableVal*>(this)->Ref();
}
else if ( info->cont.ChildSuspended() )
state = (State*) info->cont.RestoreState();
else if ( info->cont.Resuming() )
{
info->cont.Resume();
state = (State*) info->cont.RestoreState();
}
else
reporter->InternalError("unknown continuation state");
HashKey* k;
int count = 0;
assert((!info->cont.ChildSuspended()) || state->v);
while ( true )
{
if ( ! state->v )
{
state->v = tbl->NextEntry(k, state->c);
if ( ! state->c )
{
// No next one.
SERIALIZE(false);
break;
}
// There's a value coming.
SERIALIZE(true);
if ( state->v->Value() )
state->v->Ref();
state->did_index = false;
}
// Serialize index.
if ( ! state->did_index )
{
// Indices are rather small, so we disable suspension
// here again.
DisableSuspend suspend(info);
info->s->WriteOpenTag("key");
ListVal* index = table_hash->RecoverVals(k)->AsListVal();
delete k;
if ( ! index->Serialize(info) )
return false;
Unref(index);
info->s->WriteCloseTag("key");
state->did_index = true;
// Start serializing data.
if ( ! type->IsSet() )
info->s->WriteOpenTag("value");
}
if ( ! type->IsSet() )
{
info->cont.SaveState(state);
info->cont.SaveContext();
bool result = state->v->val->Serialize(info);
info->cont.RestoreContext();
if ( ! result )
return false;
if ( info->cont.ChildSuspended() )
return true;
}
double eat = state->v->ExpireAccessTime();
if ( ! (SERIALIZE(state->v->last_access_time) &&
SERIALIZE(eat)) )
return false;
info->s->WriteCloseTag("value");
if ( state->v->Value() )
state->v->Unref();
state->v = 0; // Next value.
// Suspend if we've done enough for now (which means we
// have serialized more than table_incremental_step entries
// in a row; if an entry has suspended itself in between,
// we start counting from 0).
if ( info->may_suspend && ++count > table_incremental_step)
{
info->cont.SaveState(state);
info->cont.Suspend();
reporter->Info("TableVals serialization suspended right in the middle.");
return true;
}
}
info->s->WriteCloseTag(table_type->IsSet() ? "set" : "table");
delete state;
Unref(const_cast<TableVal*>(this));
return true;
}
bool TableVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(MutableVal);
if ( ! UNSERIALIZE(&expire_time) )
return false;
Init((TableType*) type);
UNSERIALIZE_OPTIONAL(attrs, Attributes::Unserialize(info));
UNSERIALIZE_OPTIONAL(expire_expr, Expr::Unserialize(info));
while ( true )
{
// Anymore?
bool next;
if ( ! UNSERIALIZE(&next) )
return false;
if ( ! next )
break;
// Unserialize index.
ListVal* index =
(ListVal*) Val::Unserialize(info, table_type->Indices());
if ( ! index )
return false;
// Unserialize data.
Val* entry;
if ( ! table_type->IsSet() )
{
entry = Val::Unserialize(info, type->YieldType());
if ( ! entry )
return false;
}
else
entry = 0;
TableEntryVal* entry_val = new TableEntryVal(entry);
double eat;
if ( ! UNSERIALIZE(&entry_val->last_access_time) ||
! UNSERIALIZE(&eat) )
return false;
entry_val->SetExpireAccess(eat);
HashKey* key = ComputeHash(index);
TableEntryVal* old_entry_val =
AsNonConstTable()->Insert(key, entry_val);
assert(! old_entry_val);
delete key;
if ( subnets )
subnets->Insert(index, entry_val);
Unref(index);
}
// If necessary, activate the expire timer.
if ( attrs)
{
CheckExpireAttr(ATTR_EXPIRE_READ);
CheckExpireAttr(ATTR_EXPIRE_WRITE);
CheckExpireAttr(ATTR_EXPIRE_CREATE);
}
return true;
}
bool TableVal::AddProperties(Properties arg_props)
{
if ( ! MutableVal::AddProperties(arg_props) )
return false;
if ( Type()->IsSet() || ! RecursiveProps(arg_props) )
return true;
// For a large table, this could get expensive. So, let's hope
// that nobody creates such a table *before* making it persistent
// (for example by inserting it into another table).
TableEntryVal* v;
PDict(TableEntryVal)* tbl = val.table_val;
IterCookie* c = tbl->InitForIteration();
while ( (v = tbl->NextEntry(c)) )
if ( v->Value()->IsMutableVal() )
v->Value()->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
return true;
}
bool TableVal::RemoveProperties(Properties arg_props)
{
if ( ! MutableVal::RemoveProperties(arg_props) )
return false;
if ( Type()->IsSet() || ! RecursiveProps(arg_props) )
return true;
// For a large table, this could get expensive. So, let's hope
// that nobody creates such a table *before* making it persistent
// (for example by inserting it into another table).
TableEntryVal* v;
PDict(TableEntryVal)* tbl = val.table_val;
IterCookie* c = tbl->InitForIteration();
while ( (v = tbl->NextEntry(c)) )
if ( v->Value()->IsMutableVal() )
v->Value()->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
return true;
}
unsigned int TableVal::MemoryAllocation() const
{
unsigned int size = 0;
PDict(TableEntryVal)* v = val.table_val;
IterCookie* c = v->InitForIteration();
TableEntryVal* tv;
while ( (tv = v->NextEntry(c)) )
{
if ( tv->Value() )
size += tv->Value()->MemoryAllocation();
size += padded_sizeof(TableEntryVal);
}
return size + padded_sizeof(*this) + val.table_val->MemoryAllocation()
+ table_hash->MemoryAllocation();
}
RecordVal::RecordVal(RecordType* t) : MutableVal(t)
{
record_type = t;
int n = record_type->NumFields();
val_list* vl = val.val_list_val = new val_list(n);
// 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 && ! (a && a->FindAttr(ATTR_OPTIONAL)) )
{
BroType* type = record_type->FieldDecl(i)->type;
TypeTag tag = type->Tag();
if ( tag == TYPE_RECORD )
def = new RecordVal(type->AsRecordType());
else if ( tag == TYPE_TABLE )
def = new TableVal(type->AsTableType(), a);
else if ( tag == TYPE_VECTOR )
def = new VectorVal(type->AsVectorType());
}
vl->append(def ? def->Ref() : 0);
Unref(def);
}
}
RecordVal::~RecordVal()
{
delete_vals(AsNonConstRecord());
}
void RecordVal::Assign(int field, Val* new_val, Opcode op)
{
if ( new_val && Lookup(field) &&
record_type->FieldType(field)->Tag() == TYPE_TABLE &&
new_val->AsTableVal()->FindAttr(ATTR_MERGEABLE) )
{
// Join two mergeable sets.
Val* old = Lookup(field);
if ( old->AsTableVal()->FindAttr(ATTR_MERGEABLE) )
{
if ( LoggingAccess() && op != OP_NONE )
{
StringVal* index = new StringVal(Type()->AsRecordType()->FieldName(field));
StateAccess::Log(new StateAccess(OP_ASSIGN_IDX, this, index, new_val, old));
Unref(index);
}
new_val->AsTableVal()->AddTo(old->AsTableVal(), 0, false);
Unref(new_val);
return;
}
}
Val* old_val = AsNonConstRecord()->replace(field, new_val);
if ( LoggingAccess() && op != OP_NONE )
{
if ( new_val && new_val->IsMutableVal() )
new_val->AsMutableVal()->AddProperties(GetProperties());
StringVal* index = new StringVal(Type()->AsRecordType()->FieldName(field));
StateAccess::Log(
new StateAccess(
op == OP_INCR ? OP_INCR_IDX : OP_ASSIGN_IDX,
this, index, new_val, old_val));
Unref(index); // The logging may keep a cached copy.
}
Unref(old_val);
Modified();
}
Val* RecordVal::Lookup(int field) const
{
return (*AsRecord())[field];
}
Val* RecordVal::LookupWithDefault(int field) const
{
Val* val = (*AsRecord())[field];
if ( val )
return val->Ref();
return record_type->FieldDefault(field);
}
RecordVal* RecordVal::CoerceTo(const RecordType* t, Val* aggr, bool allow_orphaning) const
{
if ( ! record_promotion_compatible(t->AsRecordType(), Type()->AsRecordType()) )
return 0;
if ( ! aggr )
aggr = new RecordVal(const_cast<RecordType*>(t->AsRecordType()));
RecordVal* ar = aggr->AsRecordVal();
RecordType* ar_t = aggr->Type()->AsRecordType();
const RecordType* rv_t = Type()->AsRecordType();
int i;
for ( i = 0; i < rv_t->NumFields(); ++i )
{
int t_i = ar_t->FieldOffset(rv_t->FieldName(i));
if ( t_i < 0 )
{
if ( allow_orphaning )
continue;
char buf[512];
safe_snprintf(buf, sizeof(buf),
"orphan field \"%s\" in initialization",
rv_t->FieldName(i));
Error(buf);
break;
}
if ( ar_t->FieldType(t_i)->Tag() == TYPE_RECORD
&& ! same_type(ar_t->FieldType(t_i), Lookup(i)->Type()) )
{
Expr* rhs = new ConstExpr(Lookup(i)->Ref());
Expr* e = new RecordCoerceExpr(rhs, ar_t->FieldType(t_i)->AsRecordType());
ar->Assign(t_i, e->Eval(0));
continue;
}
ar->Assign(t_i, Lookup(i)->Ref());
}
for ( i = 0; i < ar_t->NumFields(); ++i )
if ( ! ar->Lookup(i) &&
! ar_t->FieldDecl(i)->FindAttr(ATTR_OPTIONAL) )
{
char buf[512];
safe_snprintf(buf, sizeof(buf),
"non-optional field \"%s\" missing in initialization", ar_t->FieldName(i));
Error(buf);
}
return ar;
}
RecordVal* RecordVal::CoerceTo(RecordType* t, bool allow_orphaning)
{
if ( same_type(Type(), t) )
{
this->Ref();
return this;
}
return CoerceTo(t, 0, allow_orphaning);
}
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("]");
}
void RecordVal::DescribeReST(ODesc* d) const
{
const val_list* vl = AsRecord();
int n = vl->length();
d->Add("{");
d->PushIndent();
loop_over_list(*vl, i)
{
if ( i > 0 )
d->NL();
d->Add(record_type->FieldName(i));
d->Add("=");
Val* v = (*vl)[i];
if ( v )
v->Describe(d);
else
d->Add("<uninitialized>");
}
d->PopIndent();
d->Add("}");
}
IMPLEMENT_SERIAL(RecordVal, SER_RECORD_VAL);
bool RecordVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_RECORD_VAL, MutableVal);
// We could use the type name as a tag here.
info->s->WriteOpenTag("record");
// We don't need to serialize record_type as it's simply the
// casted table_type.
// FIXME: What about origin?
if ( ! SERIALIZE(val.val_list_val->length()) )
return false;
loop_over_list(*val.val_list_val, i)
{
info->s->WriteOpenTag(record_type->FieldName(i));
Val* v = (*val.val_list_val)[i];
SERIALIZE_OPTIONAL(v);
info->s->WriteCloseTag(record_type->FieldName(i));
}
info->s->WriteCloseTag("record");
return true;
}
bool RecordVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(MutableVal);
record_type = (RecordType*) type;
origin = 0;
int len;
if ( ! UNSERIALIZE(&len) )
{
val.val_list_val = new val_list;
return false;
}
val.val_list_val = new val_list(len);
for ( int i = 0; i < len; ++i )
{
Val* v;
UNSERIALIZE_OPTIONAL(v, Val::Unserialize(info));
AsNonConstRecord()->append(v); // correct for v==0, too.
}
return true;
}
bool RecordVal::AddProperties(Properties arg_props)
{
if ( ! MutableVal::AddProperties(arg_props) )
return false;
if ( ! RecursiveProps(arg_props) )
return true;
loop_over_list(*val.val_list_val, i)
{
Val* v = (*val.val_list_val)[i];
if ( v && v->IsMutableVal() )
v->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
}
return true;
}
bool RecordVal::RemoveProperties(Properties arg_props)
{
if ( ! MutableVal::RemoveProperties(arg_props) )
return false;
if ( ! RecursiveProps(arg_props) )
return true;
loop_over_list(*val.val_list_val, i)
{
Val* v = (*val.val_list_val)[i];
if ( v && v->IsMutableVal() )
v->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
}
return true;
}
unsigned int RecordVal::MemoryAllocation() const
{
unsigned int size = 0;
const val_list* vl = AsRecord();
loop_over_list(*vl, i)
{
Val* v = (*vl)[i];
if ( v )
size += v->MemoryAllocation();
}
return size + padded_sizeof(*this) + val.val_list_val->MemoryAllocation();
}
void EnumVal::ValDescribe(ODesc* d) const
{
const char* ename = type->AsEnumType()->Lookup(val.int_val);
if ( ! ename )
ename = "<undefined>";
d->Add(ename);
}
IMPLEMENT_SERIAL(EnumVal, SER_ENUM_VAL);
bool EnumVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_ENUM_VAL, Val);
return true;
}
bool EnumVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(Val);
return true;
}
VectorVal::VectorVal(VectorType* t) : MutableVal(t)
{
vector_type = t->Ref()->AsVectorType();
val.vector_val = new vector<Val*>();
}
VectorVal::~VectorVal()
{
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
Unref((*val.vector_val)[i]);
Unref(vector_type);
delete val.vector_val;
}
bool VectorVal::Assign(unsigned int index, Val* element, const Expr* assigner,
Opcode op)
{
if ( element &&
! same_type(element->Type(), vector_type->YieldType(), 0) )
{
Unref(element);
return false;
}
BroType* yt = Type()->AsVectorType()->YieldType();
if ( yt && yt->Tag() == TYPE_TABLE &&
element->AsTableVal()->FindAttr(ATTR_MERGEABLE) )
{
// Join two mergeable sets.
Val* old = Lookup(index);
if ( old && old->AsTableVal()->FindAttr(ATTR_MERGEABLE) )
{
if ( LoggingAccess() && op != OP_NONE )
{
Val* ival = new Val(index, TYPE_COUNT);
StateAccess::Log(new StateAccess(OP_ASSIGN_IDX,
this, ival, element,
(*val.vector_val)[index]));
Unref(ival);
}
element->AsTableVal()->AddTo(old->AsTableVal(), 0, false);
Unref(element);
return true;
}
}
if ( index < val.vector_val->size() )
Unref((*val.vector_val)[index]);
else
val.vector_val->resize(index + 1);
if ( LoggingAccess() && op != OP_NONE )
{
if ( element->IsMutableVal() )
element->AsMutableVal()->AddProperties(GetProperties());
Val* ival = new Val(index, TYPE_COUNT);
StateAccess::Log(new StateAccess(op == OP_INCR ?
OP_INCR_IDX : OP_ASSIGN_IDX,
this, ival, element, (*val.vector_val)[index]));
Unref(ival);
}
// Note: we do *not* Ref() the element, if any, at this point.
// AssignExpr::Eval() already does this; other callers must remember
// to do it similarly.
(*val.vector_val)[index] = element;
Modified();
return true;
}
bool VectorVal::AssignRepeat(unsigned int index, unsigned int how_many,
Val* element, const Expr* assigner)
{
ResizeAtLeast(index + how_many);
for ( unsigned int i = index; i < index + how_many; ++i )
if ( ! Assign(i, element, assigner) )
return false;
return true;
}
Val* VectorVal::Lookup(unsigned int index) const
{
if ( index >= val.vector_val->size() )
return 0;
return (*val.vector_val)[index];
}
unsigned int VectorVal::Resize(unsigned int new_num_elements)
{
unsigned int oldsize = val.vector_val->size();
val.vector_val->reserve(new_num_elements);
val.vector_val->resize(new_num_elements);
return oldsize;
}
unsigned int VectorVal::ResizeAtLeast(unsigned int new_num_elements)
{
unsigned int old_size = val.vector_val->size();
if ( new_num_elements <= old_size )
return old_size;
return Resize(new_num_elements);
}
bool VectorVal::AddProperties(Properties arg_props)
{
if ( ! MutableVal::AddProperties(arg_props) )
return false;
if ( ! RecursiveProps(arg_props) )
return true;
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
if ( (*val.vector_val)[i]->IsMutableVal() )
(*val.vector_val)[i]->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
return true;
}
bool VectorVal::RemoveProperties(Properties arg_props)
{
if ( ! MutableVal::RemoveProperties(arg_props) )
return false;
if ( ! RecursiveProps(arg_props) )
return true;
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
if ( (*val.vector_val)[i]->IsMutableVal() )
(*val.vector_val)[i]->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
return true;
}
IMPLEMENT_SERIAL(VectorVal, SER_VECTOR_VAL);
bool VectorVal::DoSerialize(SerialInfo* info) const
{
DO_SERIALIZE(SER_VECTOR_VAL, MutableVal);
info->s->WriteOpenTag("vector");
if ( ! SERIALIZE(unsigned(val.vector_val->size())) )
return false;
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
{
info->s->WriteOpenTag("value");
Val* v = (*val.vector_val)[i];
SERIALIZE_OPTIONAL(v);
info->s->WriteCloseTag("value");
}
info->s->WriteCloseTag("vector");
return true;
}
bool VectorVal::DoUnserialize(UnserialInfo* info)
{
DO_UNSERIALIZE(MutableVal);
val.vector_val = new vector<Val*>;
vector_type = type->Ref()->AsVectorType();
int len;
if ( ! UNSERIALIZE(&len) )
return false;
for ( int i = 0; i < len; ++i )
{
Val* v;
UNSERIALIZE_OPTIONAL(v, Val::Unserialize(info, TYPE_ANY));
Assign(i, v, 0);
}
return true;
}
void VectorVal::ValDescribe(ODesc* d) const
{
d->Add("[");
if ( val.vector_val->size() > 0 )
for ( unsigned int i = 0; i < (val.vector_val->size() - 1); ++i )
{
if ( (*val.vector_val)[i] )
(*val.vector_val)[i]->Describe(d);
d->Add(", ");
}
if ( val.vector_val->size() &&
(*val.vector_val)[val.vector_val->size() - 1] )
(*val.vector_val)[val.vector_val->size() - 1]->Describe(d);
d->Add("]");
}
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();
// More thought definitely needs to go into this.
if ( t_tag == TYPE_ANY || v_tag == TYPE_ANY )
return v;
if ( ! EitherArithmetic(t_tag, v_tag) ||
/* allow sets as initializers */
(is_init && v_tag == TYPE_TABLE) )
{
if ( same_type(t, vt, is_init) )
return v;
t->Error("type clash", v);
Unref(v);
return 0;
}
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:
reporter->InternalError("bad internal type in check_and_promote()");
Unref(v);
return 0;
}
Unref(v);
return promoted_v;
}
int same_val(const Val* /* v1 */, const Val* /* v2 */)
{
reporter->InternalError("same_val not implemented");
return 0;
}
bool is_atomic_val(const Val* v)
{
switch ( v->Type()->InternalType() ) {
case TYPE_INTERNAL_INT:
case TYPE_INTERNAL_UNSIGNED:
case TYPE_INTERNAL_DOUBLE:
case TYPE_INTERNAL_STRING:
case TYPE_INTERNAL_ADDR:
case TYPE_INTERNAL_SUBNET:
return true;
default:
return false;
}
}
int same_atomic_val(const Val* v1, const Val* v2)
{
// This is a very preliminary implementation of same_val(),
// true only for equal, simple atomic values of same type.
if ( v1->Type()->Tag() != v2->Type()->Tag() )
return 0;
switch ( v1->Type()->InternalType() ) {
case TYPE_INTERNAL_INT:
return v1->InternalInt() == v2->InternalInt();
case TYPE_INTERNAL_UNSIGNED:
return v1->InternalUnsigned() == v2->InternalUnsigned();
case TYPE_INTERNAL_DOUBLE:
return v1->InternalDouble() == v2->InternalDouble();
case TYPE_INTERNAL_STRING:
return Bstr_eq(v1->AsString(), v2->AsString());
case TYPE_INTERNAL_ADDR:
return v1->AsAddr() == v2->AsAddr();
case TYPE_INTERNAL_SUBNET:
return v1->AsSubNet() == v2->AsSubNet();
default:
reporter->InternalError("same_atomic_val called for non-atomic value");
return 0;
}
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->Style() != RAW_STYLE )
d->Add(", ");
(*vals)[i]->Describe(d);
}
}
void delete_vals(val_list* vals)
{
if ( vals )
{
loop_over_list(*vals, i)
Unref((*vals)[i]);
delete vals;
}
}