mirror of
https://github.com/zeek/zeek.git
synced 2025-10-06 16:48:19 +00:00
Val: Introduce ZValSlot
This introduces a new class for replacing the std::optional in RecordVal's record_val vector. It may also be useful within VectorVal.
This commit is contained in:
parent
00ad3e31c6
commit
27777ac214
2 changed files with 236 additions and 0 deletions
107
src/Val.cc
107
src/Val.cc
|
@ -44,6 +44,8 @@
|
|||
#include "zeek/broker/Store.h"
|
||||
#include "zeek/threading/formatters/detail/json.h"
|
||||
|
||||
#include "zeek/3rdparty/doctest.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace zeek {
|
||||
|
@ -4120,3 +4122,108 @@ const PortValPtr& ValManager::Port(uint32_t port_num) {
|
|||
}
|
||||
|
||||
} // namespace zeek
|
||||
|
||||
TEST_SUITE_BEGIN("ZValSlot");
|
||||
|
||||
TEST_CASE("default constructor") {
|
||||
// default constructor doesn't do anything.
|
||||
zeek::ZValSlot slot;
|
||||
}
|
||||
|
||||
TEST_CASE("slot hold CountVal") {
|
||||
auto t = zeek::base_type(zeek::TYPE_COUNT);
|
||||
auto v = zeek::make_intrusive<zeek::CountVal>(104242);
|
||||
zeek::ZValSlot slot = zeek::ZValSlot(v, t);
|
||||
|
||||
CHECK(slot.IsSet());
|
||||
CHECK(slot.Tag() == zeek::TYPE_COUNT);
|
||||
CHECK(! slot.IsManaged());
|
||||
CHECK(v->RefCnt() == 1); // Not managed, so the slot does not hold a ref to the original value.
|
||||
CHECK(slot->AsCount() == 104242);
|
||||
|
||||
auto nv = slot->ToVal(t);
|
||||
CHECK(nv->RefCnt() == 1); // Not managed, so the slot does not hold a ref to the original value.
|
||||
}
|
||||
|
||||
TEST_CASE("slot hold RecordVal") {
|
||||
auto t = zeek::id::find_type<zeek::RecordType>("conn_id_ctx");
|
||||
auto v = zeek::make_intrusive<zeek::RecordVal>(t);
|
||||
CHECK(v->RefCnt() == 1);
|
||||
zeek::ZValSlot slot = zeek::ZValSlot(v, t);
|
||||
|
||||
CHECK(slot.IsSet());
|
||||
CHECK(slot.Tag() == zeek::TYPE_RECORD);
|
||||
CHECK(slot.IsManaged());
|
||||
CHECK(v->RefCnt() == 2); // Managed, slot takes a ref.
|
||||
|
||||
slot.Reset();
|
||||
|
||||
CHECK(v->RefCnt() == 1);
|
||||
v = nullptr;
|
||||
}
|
||||
|
||||
TEST_CASE("assign count ZVal to slot") {
|
||||
auto t = zeek::base_type(zeek::TYPE_COUNT);
|
||||
auto v1 = zeek::make_intrusive<zeek::CountVal>(42);
|
||||
auto v2 = zeek::make_intrusive<zeek::CountVal>(100000);
|
||||
|
||||
zeek::ZValSlot slot = zeek::ZValSlot(v1, t);
|
||||
CHECK(v1->RefCnt() == 1);
|
||||
|
||||
slot = zeek::ZVal(v2, t);
|
||||
|
||||
// Unmanaged
|
||||
CHECK(v1->RefCnt() == 1);
|
||||
CHECK(v2->RefCnt() == 1);
|
||||
|
||||
auto v3 = slot->ToVal(t);
|
||||
|
||||
// New CountVal for 100000
|
||||
CHECK(v3 != v2);
|
||||
CHECK(v3->RefCnt() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("assign record ZVal to slot") {
|
||||
auto t = zeek::id::find_type<zeek::RecordType>("conn_id_ctx");
|
||||
auto v1 = zeek::make_intrusive<zeek::RecordVal>(t);
|
||||
auto v2 = zeek::make_intrusive<zeek::RecordVal>(t);
|
||||
|
||||
zeek::ZValSlot slot = zeek::ZValSlot(v1, t);
|
||||
CHECK(v1->RefCnt() == 2); // v1 and slot
|
||||
|
||||
slot = zeek::ZVal(v2, t);
|
||||
CHECK(v1->RefCnt() == 1); // slot released
|
||||
CHECK(v2->RefCnt() == 2); // v2 and slot
|
||||
|
||||
auto v3 = slot->ToVal(t);
|
||||
CHECK(v3 == v2);
|
||||
CHECK(v2->RefCnt() == 3); // v2, slot and v3
|
||||
}
|
||||
|
||||
TEST_CASE("assign slot assignment") {
|
||||
auto t = zeek::id::find_type<zeek::RecordType>("conn_id_ctx");
|
||||
auto v1 = zeek::make_intrusive<zeek::RecordVal>(t);
|
||||
auto v2 = zeek::make_intrusive<zeek::RecordVal>(t);
|
||||
|
||||
zeek::ZValSlot slot1 = zeek::ZValSlot(v1, t);
|
||||
zeek::ZValSlot slot2 = zeek::ZValSlot(v2, t);
|
||||
|
||||
CHECK(v1->RefCnt() == 2); // v1 and slot1
|
||||
CHECK(v2->RefCnt() == 2); // v2 and slot2
|
||||
|
||||
slot1 = slot2;
|
||||
CHECK(v1->RefCnt() == 1); // slot1 released
|
||||
CHECK(v2->RefCnt() == 3); // v2, slot1 and slot2
|
||||
}
|
||||
|
||||
TEST_CASE("copy slot") {
|
||||
auto t = zeek::id::find_type<zeek::RecordType>("conn_id_ctx");
|
||||
auto v = zeek::make_intrusive<zeek::RecordVal>(t);
|
||||
|
||||
zeek::ZValSlot slot1 = zeek::ZValSlot(v, t);
|
||||
zeek::ZValSlot slot2 = slot1;
|
||||
|
||||
CHECK(v->RefCnt() == 3); // v1, slot1 and slot2
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
129
src/Val.h
129
src/Val.h
|
@ -1114,6 +1114,135 @@ struct is_zeek_val {
|
|||
template<typename T>
|
||||
inline constexpr bool is_zeek_val_v = is_zeek_val<T>::value;
|
||||
|
||||
/**
|
||||
* A ZValSlot holds a ZVal instance and some auxiliary information that allows automatic
|
||||
* memory management as well as acting as an optional unset field.
|
||||
*
|
||||
* This class originated from the observation that a std::optional<ZVal>
|
||||
* as previously used in VectorVal and RecordVal objects already takes up
|
||||
* 16 bytes on 64 bit architectures with GCC. The ZValSlot class essentially
|
||||
* uses the left-over 7 bytes from the std::optional to allow easier memory
|
||||
* management without needing to keep external auxilarly information around.
|
||||
*
|
||||
* The is_managed flag and type_tag flags are meant to be immutable except
|
||||
* when a ZValSlot is reassigned.
|
||||
*
|
||||
* A ZValSlot instance holds a reference to a managed value. Such ZValSlot
|
||||
* instances can be copied or assigned and the reference count of the ZVal
|
||||
* will be updated accordingly.
|
||||
*/
|
||||
class ZValSlot {
|
||||
public:
|
||||
/**
|
||||
* Totally uninitialized, watch out!
|
||||
*/
|
||||
ZValSlot() {}
|
||||
|
||||
/**
|
||||
* Initialize a set ZValSlot given a ValPtr and corresponding TypePtr.
|
||||
*
|
||||
* This has the same ref counting semantics as the corresponding ZVal
|
||||
* constructor, increasing the ref count of any managed value.
|
||||
*/
|
||||
ZValSlot(ValPtr v, const TypePtr& t)
|
||||
: is_set(true), is_managed(ZVal::IsManagedType(t)), type_tag(t->Tag()), zval(v, t) {}
|
||||
|
||||
/**
|
||||
* Initialize a ZValSlot with just the TypePtr.
|
||||
*
|
||||
* This is useful for optional fields in a record value where
|
||||
* the type is known at construction time.
|
||||
*/
|
||||
ZValSlot(const TypePtr& t) : is_set(false), is_managed(ZVal::IsManagedType(t)), type_tag(t->Tag()) {}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
ZValSlot(const ZValSlot& s) : is_set(s.is_set), is_managed(s.is_managed), type_tag(s.type_tag), zval(s.zval) {
|
||||
if ( is_set && is_managed )
|
||||
Ref(zval.ManagedVal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~ZValSlot() { Reset(); }
|
||||
|
||||
ZValSlot& operator=(const ZValSlot& s) {
|
||||
if ( is_set && is_managed )
|
||||
Unref(zval.ManagedVal());
|
||||
|
||||
is_set = s.is_set;
|
||||
is_managed = s.is_managed;
|
||||
type_tag = s.type_tag;
|
||||
zval = s.zval;
|
||||
|
||||
if ( is_set && is_managed )
|
||||
Ref(zval.ManagedVal());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a ZVal to a slot.
|
||||
*
|
||||
* This uses the is_managed member to determine if the
|
||||
* given ZVal should be treated as managed and whether.
|
||||
*
|
||||
* Assigning a ZVal to a managed slot adopts a reference!
|
||||
* This is a bit quirky, but it's what the plain ZVal
|
||||
* constructors also do.
|
||||
*/
|
||||
ZValSlot& operator=(const ZVal& zv) {
|
||||
if ( is_set && is_managed )
|
||||
Unref(zval.ManagedVal());
|
||||
|
||||
is_set = true;
|
||||
zval = zv;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const noexcept { return is_set; }
|
||||
const ZVal* operator->() const noexcept { return &zval; }
|
||||
ZVal& operator*() noexcept { return zval; }
|
||||
const ZVal& operator*() const noexcept { return zval; }
|
||||
|
||||
bool IsSet() const noexcept { return is_set; }
|
||||
bool IsManaged() const noexcept { return is_managed; }
|
||||
TypeTag Tag() const noexcept { return type_tag; }
|
||||
|
||||
void Reset() {
|
||||
if ( is_set && is_managed )
|
||||
Unref(zval.ManagedVal());
|
||||
|
||||
is_set = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a slot's ZVal to a ValPtr given a TypePtr.
|
||||
*
|
||||
* @param t Type to use for conversion to Val. Needs to agree with the type that was used to initialize the
|
||||
* slot.
|
||||
*
|
||||
* @return A ValPtr instance for the slot.
|
||||
*/
|
||||
ValPtr ToVal(const TypePtr& t) {
|
||||
assert(IsSet());
|
||||
// assert(Tag() == TYPE_ANY || Tag() == t->Tag());
|
||||
|
||||
return zval.ToVal(t);
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set;
|
||||
bool is_managed;
|
||||
TypeTag type_tag;
|
||||
ZVal zval;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ZValSlot) <= 16);
|
||||
|
||||
class RecordVal final : public Val, public notifier::detail::Modifiable {
|
||||
public:
|
||||
explicit RecordVal(RecordTypePtr t, bool init_fields = true);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue