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/broker/Store.h"
|
||||||
#include "zeek/threading/formatters/detail/json.h"
|
#include "zeek/threading/formatters/detail/json.h"
|
||||||
|
|
||||||
|
#include "zeek/3rdparty/doctest.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace zeek {
|
namespace zeek {
|
||||||
|
@ -4120,3 +4122,108 @@ const PortValPtr& ValManager::Port(uint32_t port_num) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zeek
|
} // 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>
|
template<typename T>
|
||||||
inline constexpr bool is_zeek_val_v = is_zeek_val<T>::value;
|
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 {
|
class RecordVal final : public Val, public notifier::detail::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit RecordVal(RecordTypePtr t, bool init_fields = true);
|
explicit RecordVal(RecordTypePtr t, bool init_fields = true);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue