diff --git a/src/Hash.cc b/src/Hash.cc index 9e07af5041..9229655800 100644 --- a/src/Hash.cc +++ b/src/Hash.cc @@ -8,6 +8,7 @@ #include #include +#include "zeek/3rdparty/doctest.h" #include "zeek/DebugLogger.h" #include "zeek/Desc.h" #include "zeek/Reporter.h" @@ -184,6 +185,29 @@ HashKey::HashKey(const void* arg_key, size_t arg_size, hash_t arg_hash, bool /* key = (char*)arg_key; } +HashKey::HashKey(const HashKey& other) : HashKey(other.key, other.size, other.hash) { } + +HashKey::HashKey(HashKey&& other) noexcept + { + hash = other.hash; + size = other.size; + write_size = other.write_size; + read_size = other.read_size; + + is_our_dynamic = other.is_our_dynamic; + key = other.key; + + other.size = 0; + other.is_our_dynamic = false; + other.key = nullptr; + } + +HashKey::~HashKey() + { + if ( is_our_dynamic ) + delete[] reinterpret_cast(key); + } + hash_t HashKey::Hash() const { if ( hash == 0 ) @@ -543,4 +567,115 @@ void HashKey::EnsureReadSpace(size_t n) const n, size - read_size); } +bool HashKey::operator==(const HashKey& other) const + { + // Quick exit for the same object. + if ( this == &other ) + return true; + + return Equal(other.key, other.size, other.hash); + } + +bool HashKey::operator!=(const HashKey& other) const + { + // Quick exit for different objects. + if ( this != &other ) + return true; + + return ! Equal(other.key, other.size, other.hash); + } + +bool HashKey::Equal(const void* other_key, size_t other_size, hash_t other_hash) const + { + // If the key memory is the same just return true. + if ( key == other_key && size == other_size ) + return true; + + // If either key is nullptr, return false. If they were both nullptr, it + // would have fallen in to the above block already. + if ( key == nullptr || other_key == nullptr ) + return false; + + return (hash == other_hash) && (size == other_size) && (memcmp(key, other_key, size) == 0); + } + +HashKey& HashKey::operator=(const HashKey& other) + { + if ( this == &other ) + return *this; + + if ( is_our_dynamic && IsAllocated() ) + delete[] key; + + hash = other.hash; + size = other.size; + is_our_dynamic = true; + write_size = other.write_size; + read_size = other.read_size; + + key = CopyKey(other.key, other.size); + + return *this; + } + +HashKey& HashKey::operator=(HashKey&& other) noexcept + { + if ( this == &other ) + return *this; + + hash = other.hash; + size = other.size; + write_size = other.write_size; + read_size = other.read_size; + + if ( is_our_dynamic && IsAllocated() ) + delete[] key; + + is_our_dynamic = other.is_our_dynamic; + key = other.key; + + other.size = 0; + other.is_our_dynamic = false; + other.key = nullptr; + + return *this; + } + +TEST_SUITE_BEGIN("Hash"); + +TEST_CASE("equality") + { + HashKey h1(12345); + HashKey h2(12345); + HashKey h3(67890); + + CHECK(h1 == h2); + CHECK(h1 != h3); + } + +TEST_CASE("copy assignment") + { + HashKey h1(12345); + HashKey h2 = h1; + HashKey h3{h1}; + + CHECK(h1 == h2); + CHECK(h1 == h3); + } + +TEST_CASE("move assignment") + { + HashKey h1(12345); + HashKey h2(12345); + HashKey h3(12345); + + HashKey h4 = std::move(h2); + HashKey h5{h3}; + + CHECK(h1 == h4); + CHECK(h1 == h5); + } + +TEST_SUITE_END(); + } // namespace zeek::detail diff --git a/src/Hash.h b/src/Hash.h index ad3e50245d..4f70ce1219 100644 --- a/src/Hash.h +++ b/src/Hash.h @@ -255,11 +255,14 @@ public: // function from the one above; its value is not used. HashKey(const void* key, size_t size, hash_t hash, bool dont_copy); - ~HashKey() - { - if ( is_our_dynamic ) - delete[](char*) key; - } + // Copy constructor. Always copies the key. + HashKey(const HashKey& other); + + // Move constructor. Takes ownership of the key. + HashKey(HashKey&& other) noexcept; + + // Destructor + ~HashKey(); // Hands over the key to the caller. This means that if the // key is our dynamic, we give it to the caller and mark it @@ -343,6 +346,17 @@ public: void Describe(ODesc* d) const; + bool operator==(const HashKey& other) const; + bool operator!=(const HashKey& other) const; + + bool Equal(const void* other_key, size_t other_size, hash_t other_hash) const; + + // Copy operator. Always copies the key. + HashKey& operator=(const HashKey& other); + + // Move operator. Takes ownership of the key. + HashKey& operator=(HashKey&& other) noexcept; + protected: char* CopyKey(const char* key, size_t size) const;