// 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