mirror of
https://github.com/zeek/zeek.git
synced 2025-10-05 08:08:19 +00:00

* origin/topic/jazoff/datastructures-defer-init: Defer initialization of lists and dicts until an item is added.
231 lines
6.9 KiB
C++
231 lines
6.9 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#ifndef dict_h
|
|
#define dict_h
|
|
|
|
#include "List.h"
|
|
#include "Hash.h"
|
|
|
|
class Dictionary;
|
|
class DictEntry;
|
|
class IterCookie;
|
|
|
|
declare(PList,DictEntry);
|
|
declare(PList,IterCookie);
|
|
|
|
// Type indicating whether the dictionary should keep track of the order
|
|
// of insertions.
|
|
typedef enum { ORDERED, UNORDERED } dict_order;
|
|
|
|
// Type for function to be called when deleting elements.
|
|
typedef void (*dict_delete_func)(void*);
|
|
|
|
// A dict_delete_func that just calls delete.
|
|
extern void generic_delete_func(void*);
|
|
|
|
class Dictionary {
|
|
public:
|
|
explicit Dictionary(dict_order ordering = UNORDERED,
|
|
int initial_size = 0);
|
|
virtual ~Dictionary();
|
|
|
|
// Member functions for looking up a key, inserting/changing its
|
|
// contents, and deleting it. These come in two flavors: one
|
|
// which takes a HashKey, and the other which takes a raw key,
|
|
// its size, and its (unmodulated) hash.
|
|
void* Lookup(const HashKey* key) const
|
|
{ return Lookup(key->Key(), key->Size(), key->Hash()); }
|
|
void* Lookup(const void* key, int key_size, hash_t hash) const;
|
|
|
|
// Returns previous value, or 0 if none.
|
|
void* Insert(HashKey* key, void* val)
|
|
{
|
|
return Insert(key->TakeKey(), key->Size(), key->Hash(), val, 0);
|
|
}
|
|
// If copy_key is true, then the key is copied, otherwise it's assumed
|
|
// that it's a heap pointer that now belongs to the Dictionary to
|
|
// manage as needed.
|
|
void* Insert(void* key, int key_size, hash_t hash, void* val,
|
|
int copy_key);
|
|
|
|
// Removes the given element. Returns a pointer to the element in
|
|
// case it needs to be deleted. Returns 0 if no such element exists.
|
|
// If dontdelete is true, the key's bytes will not be deleted.
|
|
void* Remove(const HashKey* key)
|
|
{ return Remove(key->Key(), key->Size(), key->Hash()); }
|
|
void* Remove(const void* key, int key_size, hash_t hash,
|
|
bool dont_delete = false);
|
|
|
|
// Number of entries.
|
|
int Length() const
|
|
{ return tbl2 ? num_entries + num_entries2 : num_entries; }
|
|
|
|
// Largest it's ever been.
|
|
int MaxLength() const
|
|
{
|
|
return tbl2 ?
|
|
max_num_entries + max_num_entries2 : max_num_entries;
|
|
}
|
|
|
|
// Total number of entries ever.
|
|
uint64 NumCumulativeInserts() const
|
|
{
|
|
return cumulative_entries;
|
|
}
|
|
|
|
// True if the dictionary is ordered, false otherwise.
|
|
int 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
|
|
// corresponds to n=0.
|
|
//
|
|
// Returns nil if the dictionary is not ordered or if "n" is out
|
|
// of range.
|
|
void* NthEntry(int n) const
|
|
{
|
|
const void* key;
|
|
int key_len;
|
|
return NthEntry(n, key, key_len);
|
|
}
|
|
void* NthEntry(int n, const void*& key, int& key_len) const;
|
|
|
|
// To iterate through the dictionary, first call InitForIteration()
|
|
// to get an "iteration cookie". The cookie can then be handed
|
|
// to NextEntry() to get the next entry in the iteration and update
|
|
// the cookie. If NextEntry() indicates no more entries, it will
|
|
// also delete the cookie, or the cookie can be manually deleted
|
|
// prior to this if no longer needed.
|
|
//
|
|
// Unexpected results will occur if the elements of
|
|
// the dictionary are changed between calls to NextEntry() without
|
|
// first calling InitForIteration().
|
|
//
|
|
// If return_hash is true, a HashKey for the entry is returned in h,
|
|
// which should be delete'd when no longer needed.
|
|
IterCookie* InitForIteration() const;
|
|
void* NextEntry(HashKey*& h, IterCookie*& cookie, int return_hash) const;
|
|
void StopIteration(IterCookie* cookie) const;
|
|
|
|
void SetDeleteFunc(dict_delete_func f) { delete_func = f; }
|
|
|
|
// With a robust cookie, it is safe to change the dictionary while
|
|
// iterating. This means that (i) we will eventually visit all
|
|
// unmodified entries as well as all entries added during iteration,
|
|
// and (ii) we won't visit any still-unseen entries which are getting
|
|
// removed. (We don't get this for free, so only use it if
|
|
// necessary.)
|
|
void MakeRobustCookie(IterCookie* cookie)
|
|
{ cookies.append(cookie); }
|
|
|
|
// Remove all entries.
|
|
void Clear();
|
|
|
|
unsigned int MemoryAllocation() const;
|
|
|
|
private:
|
|
void Init(int size);
|
|
void Init2(int size); // initialize second table for resizing
|
|
void DeInit();
|
|
|
|
// Internal version of Insert().
|
|
void* Insert(DictEntry* entry, int copy_key);
|
|
|
|
void* DoRemove(DictEntry* entry, hash_t h,
|
|
PList(DictEntry)* chain, int chain_offset);
|
|
|
|
int NextPrime(int n) const;
|
|
int IsPrime(int n) const;
|
|
void StartChangeSize(int new_size);
|
|
void FinishChangeSize();
|
|
void MoveChains();
|
|
|
|
// The following get and set the "density" threshold - if the
|
|
// average hash chain length exceeds this threshold, the
|
|
// table will be resized. The default value is 3.0.
|
|
double DensityThresh() const { return den_thresh; }
|
|
|
|
void SetDensityThresh(double thresh)
|
|
{
|
|
den_thresh = thresh;
|
|
thresh_entries = int(thresh * double(num_buckets));
|
|
}
|
|
|
|
// Same for the second table, when resizing.
|
|
void SetDensityThresh2(double thresh)
|
|
{
|
|
den_thresh2 = thresh;
|
|
thresh_entries2 = int(thresh * double(num_buckets2));
|
|
}
|
|
|
|
// Normally we only have tbl.
|
|
// 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(DictEntry)** tbl;
|
|
int num_buckets;
|
|
int num_entries;
|
|
int max_num_entries;
|
|
uint64 cumulative_entries;
|
|
double den_thresh;
|
|
int thresh_entries;
|
|
|
|
// Resizing table (replicates tbl above).
|
|
PList(DictEntry)** tbl2;
|
|
int num_buckets2;
|
|
int num_entries2;
|
|
int max_num_entries2;
|
|
double den_thresh2;
|
|
int thresh_entries2;
|
|
|
|
hash_t tbl_next_ind;
|
|
|
|
PList(DictEntry)* order;
|
|
dict_delete_func delete_func;
|
|
|
|
PList(IterCookie) cookies;
|
|
};
|
|
|
|
|
|
#define PDict(type) type ## PDict
|
|
#define PDictdeclare(type) \
|
|
class PDict(type) : public Dictionary { \
|
|
public: \
|
|
explicit PDict(type)(dict_order ordering = UNORDERED, \
|
|
int initial_size = 0) : \
|
|
Dictionary(ordering, initial_size) {} \
|
|
type* Lookup(const char* key) const \
|
|
{ \
|
|
HashKey h(key); \
|
|
return (type*) Dictionary::Lookup(&h); \
|
|
} \
|
|
type* Lookup(const HashKey* key) const \
|
|
{ return (type*) Dictionary::Lookup(key); } \
|
|
type* Insert(const char* key, type* val) \
|
|
{ \
|
|
HashKey h(key); \
|
|
return (type*) Dictionary::Insert(&h, (void*) val); \
|
|
} \
|
|
type* Insert(HashKey* key, type* val) \
|
|
{ return (type*) Dictionary::Insert(key, (void*) val); }\
|
|
type* NthEntry(int n) const \
|
|
{ return (type*) Dictionary::NthEntry(n); } \
|
|
type* NthEntry(int n, const char*& key) const \
|
|
{ \
|
|
int key_len; \
|
|
return (type*) Dictionary::NthEntry(n, (const void*&) key,\
|
|
key_len); \
|
|
} \
|
|
type* NextEntry(IterCookie*& cookie) const \
|
|
{ \
|
|
HashKey* h; \
|
|
return (type*) Dictionary::NextEntry(h, cookie, 0); \
|
|
} \
|
|
type* NextEntry(HashKey*& h, IterCookie*& cookie) const \
|
|
{ return (type*) Dictionary::NextEntry(h, cookie, 1); } \
|
|
type* RemoveEntry(const HashKey* key) \
|
|
{ return (type*) Remove(key->Key(), key->Size(), \
|
|
key->Hash()); } \
|
|
}
|
|
|
|
#endif
|