diff --git a/CHANGES b/CHANGES index 7cab5bec9d..cdce15ce15 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,12 @@ +3.2.0-dev.25 | 2020-02-13 19:05:56 -0800 + + * Reset the number of entries in a dict when calling Clear() (Tim Wojtulewicz, Corelight) + + * Code cleanup in Dict.h (Tim Wojtulewicz, Corelight) + + * Add unit testing for the public Dictionary API (Tim Wojtulewicz, Corelight) + 3.2.0-dev.21 | 2020-02-13 17:14:26 -0800 * Check for failure when registering event manager with iosource manager diff --git a/VERSION b/VERSION index 5ffea7cd36..d8d229a7c5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.0-dev.21 +3.2.0-dev.25 diff --git a/src/Dict.cc b/src/Dict.cc index fa1f9e2bc9..5b5be6fe4b 100644 --- a/src/Dict.cc +++ b/src/Dict.cc @@ -6,6 +6,8 @@ #include #endif +#include "3rdparty/doctest.h" + #include "Dict.h" #include "Reporter.h" @@ -21,10 +23,11 @@ // increase the size of the hash table as needed. #define DEFAULT_DICT_SIZE 16 +TEST_SUITE_BEGIN("Dict"); + class DictEntry { public: - DictEntry(void* k, int l, hash_t h, void* val) - { key = k; len = l; hash = h; value = val; } + DictEntry(void* k, int l, hash_t h, void* val) : key(k), len(l), hash(h), value(val) {} ~DictEntry() { @@ -41,38 +44,152 @@ public: // bucket at which to start looking for the next value to return. class IterCookie { public: - IterCookie(int b, int o) - { - bucket = b; - offset = o; - ttbl = 0; - num_buckets_p = 0; - } + IterCookie(int b, int o) : bucket(b), offset(o) {} int bucket, offset; - PList** ttbl; - const int* num_buckets_p; + PList** ttbl = nullptr; + const int* num_buckets_p = nullptr; PList inserted; // inserted while iterating }; +TEST_CASE("dict construction") + { + PDict dict; + CHECK(dict.IsOrdered() == false); + CHECK(dict.Length() == 0); + + PDict dict2(ORDERED); + CHECK(dict2.IsOrdered() == true); + CHECK(dict2.Length() == 0); + } + +TEST_CASE("dict operation") + { + PDict dict; + + uint32_t val = 10; + uint32_t key_val = 5; + + HashKey* key = new HashKey(key_val); + dict.Insert(key, &val); + CHECK(dict.Length() == 1); + + HashKey* key2 = new HashKey(key_val); + uint32_t* lookup = dict.Lookup(key2); + CHECK(*lookup == val); + + dict.Remove(key2); + CHECK(dict.Length() == 0); + uint32_t* lookup2 = dict.Lookup(key2); + CHECK(lookup2 == (uint32_t*)0); + delete key2; + + CHECK(dict.MaxLength() == 1); + CHECK(dict.NumCumulativeInserts() == 1); + + dict.Insert(key, &val); + dict.Remove(key); + + CHECK(dict.MaxLength() == 1); + CHECK(dict.NumCumulativeInserts() == 2); + + uint32_t val2 = 15; + uint32_t key_val2 = 25; + key2 = new HashKey(key_val2); + + dict.Insert(key, &val); + dict.Insert(key2, &val2); + CHECK(dict.Length() == 2); + CHECK(dict.NumCumulativeInserts() == 4); + + dict.Clear(); + CHECK(dict.Length() == 0); + + delete key; + delete key2; + } + +TEST_CASE("dict nthentry") + { + PDict unordered(UNORDERED); + PDict ordered(ORDERED); + + uint32_t val = 15; + uint32_t key_val = 5; + HashKey* okey = new HashKey(key_val); + HashKey* ukey = new HashKey(key_val); + + uint32_t val2 = 10; + uint32_t key_val2 = 25; + HashKey* okey2 = new HashKey(key_val2); + HashKey* ukey2 = new HashKey(key_val2); + + unordered.Insert(ukey, &val); + unordered.Insert(ukey2, &val2); + + ordered.Insert(okey, &val); + ordered.Insert(okey2, &val2); + + // NthEntry returns null for unordered dicts + uint32_t* lookup = unordered.NthEntry(0); + CHECK(lookup == (uint32_t*)0); + + // Ordered dicts are based on order of insertion, nothing about the + // data itself + lookup = ordered.NthEntry(0); + CHECK(*lookup == 15); + + delete okey; + delete okey2; + delete ukey; + delete ukey2; + } + +TEST_CASE("dict iteration") + { + PDict dict; + + uint32_t val = 15; + uint32_t key_val = 5; + HashKey* key = new HashKey(key_val); + + uint32_t val2 = 10; + uint32_t key_val2 = 25; + HashKey* key2 = new HashKey(key_val2); + + dict.Insert(key, &val); + dict.Insert(key2, &val2); + + HashKey* it_key; + IterCookie* it = dict.InitForIteration(); + CHECK(it != nullptr); + int count = 0; + + while ( uint32_t* entry = dict.NextEntry(it_key, it) ) + { + if ( count == 0 ) + { + CHECK(it_key->Hash() == key2->Hash()); + CHECK(*entry == 10); + } + else + { + CHECK(it_key->Hash() == key->Hash()); + CHECK(*entry == 15); + } + count++; + + delete it_key; + } + + delete key; + delete key2; + } + Dictionary::Dictionary(dict_order ordering, int initial_size) { - tbl = 0; - tbl2 = 0; - if ( ordering == ORDERED ) order = new PList; - else - order = 0; - - delete_func = 0; - tbl_next_ind = 0; - - cumulative_entries = 0; - num_buckets = num_entries = max_num_entries = thresh_entries = 0; - den_thresh = 0; - num_buckets2 = num_entries2 = max_num_entries2 = thresh_entries2 = 0; - den_thresh2 = 0; if ( initial_size > 0 ) Init(initial_size); @@ -87,8 +204,10 @@ Dictionary::~Dictionary() void Dictionary::Clear() { DeInit(); - tbl = 0; - tbl2 = 0; + tbl = nullptr; + tbl2 = nullptr; + num_entries = 0; + num_entries2 = 0; } void Dictionary::DeInit() @@ -111,8 +230,9 @@ void Dictionary::DeInit() } delete [] tbl; + tbl = nullptr; - if ( tbl2 == 0 ) + if ( ! tbl2 ) return; for ( int i = 0; i < num_buckets2; ++i ) @@ -130,7 +250,7 @@ void Dictionary::DeInit() } delete [] tbl2; - tbl2 = 0; + tbl2 = nullptr; } void* Dictionary::Lookup(const void* key, int key_size, hash_t hash) const @@ -386,7 +506,7 @@ void Dictionary::Init(int size) tbl = new PList*[num_buckets]; for ( int i = 0; i < num_buckets; ++i ) - tbl[i] = 0; + tbl[i] = nullptr; max_num_entries = num_entries = 0; SetDensityThresh(DEFAULT_DENSITY_THRESH); @@ -398,7 +518,7 @@ void Dictionary::Init2(int size) tbl2 = new PList*[num_buckets2]; for ( int i = 0; i < num_buckets2; ++i ) - tbl2[i] = 0; + tbl2[i] = nullptr; max_num_entries2 = num_entries2 = 0; } @@ -575,7 +695,7 @@ void Dictionary::FinishChangeSize() delete [] tbl; tbl = tbl2; - tbl2 = 0; + tbl2 = nullptr; num_buckets = num_buckets2; num_entries = num_entries2; @@ -632,3 +752,5 @@ void generic_delete_func(void* v) { free(v); } + +TEST_SUITE_END(); diff --git a/src/Dict.h b/src/Dict.h index dc9ee9982c..81a61baaf8 100644 --- a/src/Dict.h +++ b/src/Dict.h @@ -11,7 +11,7 @@ class IterCookie; // Type indicating whether the dictionary should keep track of the order // of insertions. -typedef enum { ORDERED, UNORDERED } dict_order; +enum dict_order { ORDERED, UNORDERED }; // Type for function to be called when deleting elements. typedef void (*dict_delete_func)(void*); @@ -70,7 +70,7 @@ public: } // True if the dictionary is ordered, false otherwise. - int IsOrdered() const { return order != 0; } + bool IsOrdered() const { return order != 0; } // If the dictionary is ordered then returns the n'th entry's value; // the second method also returns the key. The first entry inserted @@ -158,26 +158,26 @@ private: // When we're resizing, we'll have tbl (old) and tbl2 (new) // tbl_next_ind keeps track of how much we've moved to tbl2 // (it's the next index we're going to move). - PList** tbl; - int num_buckets; - int num_entries; - int max_num_entries; - uint64_t cumulative_entries; - double den_thresh; - int thresh_entries; + PList** tbl = nullptr; + int num_buckets = 0; + int num_entries = 0; + int max_num_entries = 0; + uint64_t cumulative_entries = 0; + double den_thresh = 0.0; + int thresh_entries = 0; // Resizing table (replicates tbl above). - PList** tbl2; - int num_buckets2; - int num_entries2; - int max_num_entries2; - double den_thresh2; - int thresh_entries2; + PList** tbl2 = nullptr; + int num_buckets2 = 0; + int num_entries2 = 0; + int max_num_entries2 = 0; + double den_thresh2 = 0; + int thresh_entries2 = 0; - hash_t tbl_next_ind; + hash_t tbl_next_ind = 0; - PList* order; - dict_delete_func delete_func; + PList* order = nullptr; + dict_delete_func delete_func = nullptr; PList cookies; };