Merge remote-tracking branch 'origin/topic/vern/set-ops2'

* origin/topic/vern/set-ops2:
  documentation, test suite update
  implemented set relationals
  bug fix for set intersection
  set intersection implemented
  mirroring previous topic/vern/set-ops to get branch up to date, since I'm a n00b

Fixed a couple memory leaks and added a leak test
This commit is contained in:
Jon Siwek 2018-08-02 10:40:36 -05:00
commit 06c6e1188a
11 changed files with 528 additions and 26 deletions

View file

@ -672,6 +672,9 @@ Val* BinaryExpr::Fold(Val* v1, Val* v2) const
if ( v1->Type()->Tag() == TYPE_PATTERN )
return PatternFold(v1, v2);
if ( v1->Type()->IsSet() )
return SetFold(v1, v2);
if ( it == TYPE_INTERNAL_ADDR )
return AddrFold(v1, v2);
@ -858,6 +861,7 @@ Val* BinaryExpr::StringFold(Val* v1, Val* v2) const
return new Val(result, TYPE_BOOL);
}
Val* BinaryExpr::PatternFold(Val* v1, Val* v2) const
{
const RE_Matcher* re1 = v1->AsPattern();
@ -873,6 +877,61 @@ Val* BinaryExpr::PatternFold(Val* v1, Val* v2) const
return new PatternVal(res);
}
Val* BinaryExpr::SetFold(Val* v1, Val* v2) const
{
TableVal* tv1 = v1->AsTableVal();
TableVal* tv2 = v2->AsTableVal();
TableVal* result;
bool res;
switch ( tag ) {
case EXPR_AND:
return tv1->Intersect(tv2);
case EXPR_OR:
result = v1->Clone()->AsTableVal();
if ( ! tv2->AddTo(result, false, false) )
reporter->InternalError("set union failed to type check");
return result;
case EXPR_SUB:
result = v1->Clone()->AsTableVal();
if ( ! tv2->RemoveFrom(result) )
reporter->InternalError("set difference failed to type check");
return result;
case EXPR_EQ:
res = tv1->EqualTo(tv2);
break;
case EXPR_NE:
res = ! tv1->EqualTo(tv2);
break;
case EXPR_LT:
res = tv1->IsSubsetOf(tv2) && tv1->Size() < tv2->Size();
break;
case EXPR_LE:
res = tv1->IsSubsetOf(tv2);
break;
case EXPR_GE:
case EXPR_GT:
// These should't happen due to canonicalization.
reporter->InternalError("confusion over canonicalization in set comparison");
break;
default:
BadTag("BinaryExpr::SetFold", expr_name(tag));
return 0;
}
return new Val(res, TYPE_BOOL);
}
Val* BinaryExpr::AddrFold(Val* v1, Val* v2) const
{
IPAddr a1 = v1->AsAddr();
@ -1454,24 +1513,39 @@ SubExpr::SubExpr(Expr* arg_op1, Expr* arg_op2)
if ( IsError() )
return;
TypeTag bt1 = op1->Type()->Tag();
if ( IsVector(bt1) )
bt1 = op1->Type()->AsVectorType()->YieldType()->Tag();
const BroType* t1 = op1->Type();
const BroType* t2 = op2->Type();
TypeTag bt2 = op2->Type()->Tag();
TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->YieldType()->Tag();
TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
bt2 = op2->Type()->AsVectorType()->YieldType()->Tag();
bt2 = t2->AsVectorType()->YieldType()->Tag();
BroType* base_result_type = 0;
if ( bt1 == TYPE_TIME && bt2 == TYPE_INTERVAL )
base_result_type = base_type(bt1);
else if ( bt1 == TYPE_TIME && bt2 == TYPE_TIME )
SetType(base_type(TYPE_INTERVAL));
else if ( bt1 == TYPE_INTERVAL && bt2 == TYPE_INTERVAL )
base_result_type = base_type(bt1);
else if ( t1->IsSet() && t2->IsSet() )
{
if ( same_type(t1, t2) )
SetType(op1->Type()->Ref());
else
ExprError("incompatible \"set\" operands");
}
else if ( BothArithmetic(bt1, bt2) )
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
else
ExprError("requires arithmetic operands");
@ -1888,13 +1962,16 @@ BitExpr::BitExpr(BroExprTag arg_tag, Expr* arg_op1, Expr* arg_op2)
if ( IsError() )
return;
TypeTag bt1 = op1->Type()->Tag();
if ( IsVector(bt1) )
bt1 = op1->Type()->AsVectorType()->YieldType()->Tag();
const BroType* t1 = op1->Type();
const BroType* t2 = op2->Type();
TypeTag bt2 = op2->Type()->Tag();
TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->YieldType()->Tag();
TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
bt2 = op2->Type()->AsVectorType()->YieldType()->Tag();
bt2 = t2->AsVectorType()->YieldType()->Tag();
if ( (bt1 == TYPE_COUNT || bt1 == TYPE_COUNTER) &&
(bt2 == TYPE_COUNT || bt2 == TYPE_COUNTER) )
@ -1917,8 +1994,16 @@ BitExpr::BitExpr(BroExprTag arg_tag, Expr* arg_op1, Expr* arg_op2)
SetType(base_type(TYPE_PATTERN));
}
else if ( t1->IsSet() && t2->IsSet() )
{
if ( same_type(t1, t2) )
SetType(op1->Type()->Ref());
else
ExprError("incompatible \"set\" operands");
}
else
ExprError("requires \"count\" operands");
ExprError("requires \"count\" or compatible \"set\" operands");
}
IMPLEMENT_SERIAL(BitExpr, SER_BIT_EXPR);
@ -1943,13 +2028,16 @@ EqExpr::EqExpr(BroExprTag arg_tag, Expr* arg_op1, Expr* arg_op2)
Canonicize();
TypeTag bt1 = op1->Type()->Tag();
if ( IsVector(bt1) )
bt1 = op1->Type()->AsVectorType()->YieldType()->Tag();
const BroType* t1 = op1->Type();
const BroType* t2 = op2->Type();
TypeTag bt2 = op2->Type()->Tag();
TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->YieldType()->Tag();
TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
bt2 = op2->Type()->AsVectorType()->YieldType()->Tag();
bt2 = t2->AsVectorType()->YieldType()->Tag();
if ( is_vector(op1) || is_vector(op2) )
SetType(new VectorType(base_type(TYPE_BOOL)));
@ -1979,10 +2067,20 @@ EqExpr::EqExpr(BroExprTag arg_tag, Expr* arg_op1, Expr* arg_op2)
break;
case TYPE_ENUM:
if ( ! same_type(op1->Type(), op2->Type()) )
if ( ! same_type(t1, t2) )
ExprError("illegal enum comparison");
break;
case TYPE_TABLE:
if ( t1->IsSet() && t2->IsSet() )
{
if ( ! same_type(t1, t2) )
ExprError("incompatible sets in comparison");
break;
}
// FALL THROUGH
default:
ExprError("illegal comparison");
}
@ -2045,13 +2143,16 @@ RelExpr::RelExpr(BroExprTag arg_tag, Expr* arg_op1, Expr* arg_op2)
Canonicize();
TypeTag bt1 = op1->Type()->Tag();
if ( IsVector(bt1) )
bt1 = op1->Type()->AsVectorType()->YieldType()->Tag();
const BroType* t1 = op1->Type();
const BroType* t2 = op2->Type();
TypeTag bt2 = op2->Type()->Tag();
TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->YieldType()->Tag();
TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
bt2 = op2->Type()->AsVectorType()->YieldType()->Tag();
bt2 = t2->AsVectorType()->YieldType()->Tag();
if ( is_vector(op1) || is_vector(op2) )
SetType(new VectorType(base_type(TYPE_BOOL)));
@ -2061,6 +2162,12 @@ RelExpr::RelExpr(BroExprTag arg_tag, Expr* arg_op1, Expr* arg_op2)
if ( BothArithmetic(bt1, bt2) )
PromoteOps(max_type(bt1, bt2));
else if ( t1->IsSet() && t2->IsSet() )
{
if ( ! same_type(t1, t2) )
ExprError("incompatible sets in comparison");
}
else if ( bt1 != bt2 )
ExprError("operands must be of the same type");

View file

@ -332,6 +332,9 @@ protected:
// Same for when the constants are patterns.
virtual Val* PatternFold(Val* v1, Val* v2) const;
// Same for when the constants are sets.
virtual Val* SetFold(Val* v1, Val* v2) const;
// Same for when the constants are addresses or subnets.
virtual Val* AddrFold(Val* v1, Val* v2) const;
virtual Val* SubNetFold(Val* v1, Val* v2) const;

View file

@ -1706,9 +1706,11 @@ int TableVal::RemoveFrom(Val* val) const
HashKey* k;
while ( tbl->NextEntry(k, c) )
{
Val* index = RecoverIndex(k);
Unref(index);
// Not sure that this is 100% sound, since the HashKey
// comes from one table but is being used in another.
// OTOH, they are both the same type, so as long as
// we don't have hash keys that are keyed per dictionary,
// it should work ...
Unref(t->Delete(k));
delete k;
}
@ -1716,6 +1718,91 @@ int TableVal::RemoveFrom(Val* val) const
return 1;
}
TableVal* TableVal::Intersect(const TableVal* tv) const
{
TableVal* result = new TableVal(table_type);
const PDict(TableEntryVal)* t0 = AsTable();
const PDict(TableEntryVal)* t1 = tv->AsTable();
PDict(TableEntryVal)* t2 = result->AsNonConstTable();
// Figure out which is smaller; assign it to t1.
if ( t1->Length() > t0->Length() )
{ // Swap.
const PDict(TableEntryVal)* tmp = t1;
t1 = t0;
t0 = tmp;
}
IterCookie* c = t1->InitForIteration();
HashKey* k;
while ( t1->NextEntry(k, c) )
{
// Here we leverage the same assumption about consistent
// hashes as in TableVal::RemoveFrom above.
if ( t0->Lookup(k) )
t2->Insert(k, new TableEntryVal(0));
delete k;
}
return result;
}
bool TableVal::EqualTo(const TableVal* tv) const
{
const PDict(TableEntryVal)* t0 = AsTable();
const PDict(TableEntryVal)* t1 = tv->AsTable();
if ( t0->Length() != t1->Length() )
return false;
IterCookie* c = t0->InitForIteration();
HashKey* k;
while ( t0->NextEntry(k, c) )
{
// Here we leverage the same assumption about consistent
// hashes as in TableVal::RemoveFrom above.
if ( ! t1->Lookup(k) )
{
delete k;
t0->StopIteration(c);
return false;
}
delete k;
}
return true;
}
bool TableVal::IsSubsetOf(const TableVal* tv) const
{
const PDict(TableEntryVal)* t0 = AsTable();
const PDict(TableEntryVal)* t1 = tv->AsTable();
if ( t0->Length() > t1->Length() )
return false;
IterCookie* c = t0->InitForIteration();
HashKey* k;
while ( t0->NextEntry(k, c) )
{
// Here we leverage the same assumption about consistent
// hashes as in TableVal::RemoveFrom above.
if ( ! t1->Lookup(k) )
{
delete k;
t0->StopIteration(c);
return false;
}
delete k;
}
return true;
}
int TableVal::ExpandAndInit(Val* index, Val* new_val)
{
BroType* index_type = index->Type();

View file

@ -809,6 +809,22 @@ public:
// Returns true if the addition typechecked, false if not.
int RemoveFrom(Val* v) const override;
// Returns a new table that is the intersection of this
// table and the given table. Intersection is just done
// on index, not on yield value, so this really only makes
// sense for sets.
TableVal* Intersect(const TableVal* v) const;
// Returns true if this set contains the same members as the
// given set. Note that comparisons are done using hash keys,
// so errors can arise for compound sets such as sets-of-sets.
// See https://bro-tracker.atlassian.net/browse/BIT-1949.
bool EqualTo(const TableVal* v) const;
// Returns true if this set is a subset (not necessarily proper)
// of the given set.
bool IsSubsetOf(const TableVal* v) const;
// Expands any lists in the index into multiple initializations.
// Returns true if the initializations typecheck, false if not.
int ExpandAndInit(Val* index, Val* new_val);