From af9e852c283519d26f540bf8aa5d89cb35b468ea Mon Sep 17 00:00:00 2001 From: Tim Wojtulewicz Date: Fri, 14 Jul 2023 13:35:26 -0700 Subject: [PATCH] Add conversion between set and vector using 'as' keyword --- NEWS | 5 ++ src/Val.cc | 73 +++++++++++++++++++ .../.stderr | 4 + .../out | 1 + .../language.vector-set-conversions/.stderr | 1 + .../language.vector-set-conversions/out | 25 +++++++ .../vector-set-conversions-errors.zeek | 24 ++++++ .../language/vector-set-conversions.zeek | 24 ++++++ 8 files changed, 157 insertions(+) create mode 100644 testing/btest/Baseline/language.vector-set-conversions-errors/.stderr create mode 100644 testing/btest/Baseline/language.vector-set-conversions-errors/out create mode 100644 testing/btest/Baseline/language.vector-set-conversions/.stderr create mode 100644 testing/btest/Baseline/language.vector-set-conversions/out create mode 100644 testing/btest/language/vector-set-conversions-errors.zeek create mode 100644 testing/btest/language/vector-set-conversions.zeek diff --git a/NEWS b/NEWS index a009088290..83bd36cd91 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,11 @@ New Functionality modules by prefixing their names with ``::``. Previously, these required an explicit ``GLOBAL::`` prefix to be used. Using ``GLOBAL::`` has been deprecated. +- The ``as`` keyword now supports casting between ``set`` and ``vector`` values + with the same element type. Converting ``set`` values with multiple index + values is not supported. We plan to extend the use of the ``as`` keyword to + support more type conversions in the future. + Changed Functionality --------------------- diff --git a/src/Val.cc b/src/Val.cc index d95e217fc6..d1f50a578e 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -4262,9 +4262,74 @@ ValPtr cast_value_to_type(Val* v, Type* t) return static_cast(dv.get())->castTo(t); } + // Allow casting between sets and vectors if the yield types are the same. + if ( v->GetType()->IsSet() && IsVector(t->Tag()) ) + { + auto set_type = v->GetType(); + auto indices = set_type->GetIndices(); + + if ( indices->GetTypes().size() > 1 || ! indices->IsPure() ) + return nullptr; + + auto ret_type = IntrusivePtr{NewRef{}, t->AsVectorType()}; + auto ret = make_intrusive(ret_type); + + auto lv = v->AsTableVal()->ToPureListVal(); + auto n = lv->Length(); + + for ( int i = 0; i < n; ++i ) + ret->Assign(i, lv->Idx(i)); + + return ret; + } + else if ( IsVector(v->GetType()->Tag()) && t->IsSet() ) + { + auto ret_type = IntrusivePtr{NewRef{}, t->AsSetType()}; + auto ret = make_intrusive(ret_type); + + auto vv = v->AsVectorVal(); + size_t size = vv->Size(); + + for ( size_t i = 0; i < size; i++ ) + { + auto ve = vv->ValAt(i); + ret->Assign(std::move(ve), nullptr); + } + + return ret; + } + return nullptr; } +static bool can_cast_set_and_vector(const Type* t1, const Type* t2) + { + const TableType* st = nullptr; + const VectorType* vt = nullptr; + + if ( t1->IsSet() && IsVector(t2->Tag()) ) + { + st = t1->AsSetType(); + vt = t2->AsVectorType(); + } + else if ( IsVector(t1->Tag()) && t2->IsSet() ) + { + st = t2->AsSetType(); + vt = t1->AsVectorType(); + } + + if ( st && vt ) + { + auto set_indices = st->GetIndices(); + if ( set_indices->GetTypes().size() > 1 || ! set_indices->IsPure() ) + return false; + + return same_type(set_indices->GetPureType(), vt->Yield()); + } + + return false; + } + bool can_cast_value_to_type(const Val* v, Type* t) { // Note: when changing this function, adapt all three of @@ -4288,6 +4353,10 @@ bool can_cast_value_to_type(const Val* v, Type* t) return static_cast(dv.get())->canCastTo(t); } + // Allow casting between sets and vectors if the yield types are the same. + if ( can_cast_set_and_vector(v->GetType().get(), t) ) + return true; + return false; } @@ -4307,6 +4376,10 @@ bool can_cast_value_to_type(const Type* s, Type* t) // will. return true; + // Allow casting between sets and vectors if the yield types are the same. + if ( can_cast_set_and_vector(s, t) ) + return true; + return false; } diff --git a/testing/btest/Baseline/language.vector-set-conversions-errors/.stderr b/testing/btest/Baseline/language.vector-set-conversions-errors/.stderr new file mode 100644 index 0000000000..42446a02a8 --- /dev/null +++ b/testing/btest/Baseline/language.vector-set-conversions-errors/.stderr @@ -0,0 +1,4 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/vector-set-conversions-errors.zeek, line 8: cast not supported (v1 as set[addr]) +error in <...>/vector-set-conversions-errors.zeek, line 15: cast not supported (s2 as vector of addr) +error in <...>/vector-set-conversions-errors.zeek, line 22: cast not supported (s3 as vector of port) diff --git a/testing/btest/Baseline/language.vector-set-conversions-errors/out b/testing/btest/Baseline/language.vector-set-conversions-errors/out new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/language.vector-set-conversions-errors/out @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/language.vector-set-conversions/.stderr b/testing/btest/Baseline/language.vector-set-conversions/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/language.vector-set-conversions/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/language.vector-set-conversions/out b/testing/btest/Baseline/language.vector-set-conversions/out new file mode 100644 index 0000000000..4e2d52d393 --- /dev/null +++ b/testing/btest/Baseline/language.vector-set-conversions/out @@ -0,0 +1,25 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +vector to set +[1, 1, 1, 2, 2, 3, 3, 4] +{ +4, +2, +3, +1 +} + +set to vector (count) +{ +4, +2, +3, +1 +} +[4, 2, 3, 1] + +set to vector (port) +{ +23/tcp, +21/tcp +} +[23/tcp, 21/tcp] diff --git a/testing/btest/language/vector-set-conversions-errors.zeek b/testing/btest/language/vector-set-conversions-errors.zeek new file mode 100644 index 0000000000..20d199269a --- /dev/null +++ b/testing/btest/language/vector-set-conversions-errors.zeek @@ -0,0 +1,24 @@ +# @TEST-DOC: Test error cases while converting between sets and vectorswith the 'as' keyword +# @TEST-EXEC-FAIL: zeek -b %INPUT > out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: TEST_DIFF_CANONIFIER="$SCRIPTS/diff-remove-abspath" btest-diff .stderr + +print("vector to set: type mismatch"); +local v1 = vector(1, 1, 1, 2, 2, 3, 3, 4); +local s1 = v1 as set[addr]; +print(v1); +print(s1); + +print(""); +print("set to vector: type mismatch"); +local s2 = set(1, 2, 3, 4); +local v2 = s2 as vector of addr; +print(s2); +print(v2); + +print(""); +print("set to vector: multiple indices"); +local s3: set[port,string] = { [21/tcp, "ftp"], [23/tcp, "telnet"] }; +local v3 = s3 as vector of port; +print(s3); +print(v3); diff --git a/testing/btest/language/vector-set-conversions.zeek b/testing/btest/language/vector-set-conversions.zeek new file mode 100644 index 0000000000..3b6080fae9 --- /dev/null +++ b/testing/btest/language/vector-set-conversions.zeek @@ -0,0 +1,24 @@ +# @TEST-DOC: Tests converting between sets and vectors with the 'as' keyword +# @TEST-EXEC: zeek -b %INPUT > out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff .stderr + +print("vector to set"); +local v1 = vector(1, 1, 1, 2, 2, 3, 3, 4); +local s1 = v1 as set[count]; +print(v1); +print(s1); + +print(""); +print("set to vector (count)"); +local s2 = set(1, 2, 3, 4); +local v2 = s2 as vector of count; +print(s2); +print(v2); + +print(""); +print("set to vector (port)"); +local s3 = set(21/tcp, 23/tcp); +local v3 = s3 as vector of port; +print(s3); +print(v3);