// hashtbl.h see license.txt for copyright and terms of use // hash table mapping arbitrary keys to void*, where // the stored pointers can be used to derive the key, // and cannot be NULL #ifndef HASHTBL_H #define HASHTBL_H #include "typ.h" // STATICDEF class HashTable { private: // types friend class HashTableIter; public: // types // given a stored data pointer, retrieve the associated key typedef void const* (*GetKeyFn)(void *data); // given a key, retrieve the associated hash value; // this should be a 32-bit integer ready to be mod'd by the table size typedef unsigned (*HashFn)(void const *key); // compare two keys; this is needed so we can handle collisions // in the hash function; return true if they are equal typedef bool (*EqualKeyFn)(void const *key1, void const *key2); // constants enum { defaultSize = 33 }; private: // data // maps GetKeyFn getKey; HashFn coreHashFn; EqualKeyFn equalKeys; // array of pointers to data, indexed by the hash value, // with collisions resolved by moving to adjacent entries; // some entries are NULL, meaning that hash value has no mapping void **hashTable; // Why use linear hashing instead of double hashing? To support // deletion. Since every probe sequence that goes through index k // will have a tail of k+1,k+2,... (mod tableSize) I can easily find // and re-insert all the elements whose position might have depended // on the presence of a now-deleted element. Excessive clustering // is (hopefully) avoided through load factor control. // number of slots in the hash table int tableSize; // number of mapped (non-NULL) entries int numEntries; // when false, we never make the table smaller (default: true) bool enableShrink; private: // funcs // disallowed HashTable(HashTable&); void operator=(HashTable&); void operator==(HashTable&); // hash fn for the current table size; always in [0,tableSize-1] unsigned hashFunction(void const *key) const; // given a collision at 'index', return the next index to try int nextIndex(int index) const { return (index+1) % tableSize; } // resize the table, transferring all the entries to their // new positions void resizeTable(int newSize); // return the index of the entry corresponding to 'data' if it // is mapped, or a pointer to the entry that should be filled // with its mapping, if unmapped int getEntry(void const *key) const; // make a new table with the given size void makeTable(int size); // check a single entry for integrity void checkEntry(int entry) const; public: // funcs HashTable(GetKeyFn gk, HashFn hf, EqualKeyFn ek, int initSize = HashTable::defaultSize); ~HashTable(); // return # of mapped entries int getNumEntries() const { return numEntries; } // if this hash value has a mapping, return it; otherwise, // return NULL void *get(void const *key) const; // add a mapping from 'key' to 'value'; there must not already // be a mapping for this key void add(void const *key, void *value); // remove the mapping for 'key' -- it must exist // returns the removed item void *remove(void const *key); // remove all mappings void empty(int initSize = HashTable::defaultSize); // set whether shrinkage is allowed; it's useful to be able to // disable this to avoid any allocation in certain situations void setEnableShrink(bool en) { enableShrink = en; } // allow external access to an accessor function void const *callGetKeyFn(void *data) { return getKey(data); } // check the data structure's invariants, and throw an exception // if there is a problem void selfCheck() const; // ------ useful default functions ------- // returns its argument static void const* identityKeyFn(void *data); // puts the argument through two cycles of a linear // congruential pseudo-random number generator static unsigned lcprngHashFn(void const *key); // does pointer equality comparison static bool pointerEqualKeyFn(void const *key1, void const *key2); }; unsigned lcprngTwoSteps(unsigned v); // iterate over all stored values in a HashTable // NOTE: you can't change the table while an iter exists class HashTableIter { private: // data HashTable &table; // table we're iterating over int index; // current slot to return in adv(); -1 when done private: // funcs void moveToSth(); public: // funcs HashTableIter(HashTable &table); bool isDone() const { return index == -1; } void adv(); void *data() const; // returns a value stored in the table }; #endif // HASHTBL_H