/* ================================================================ HashTable ```````````````` HashTable is a template class for storing the provided data type T along with an identifier key as a char*. Values are stored in the predefined HashTable with a hash of their key used as the position offset. Data collisions are handled by storing data in a HashEntry object and checking if the key itself does not match a stored copy. If the key does not match, the HashEntry provides a pointer to the next HashEntry. This continues on as a linked list until a match is or is not found. If a match is not found, the HashTable returns a special user-defined "null" variable that should match the provided data type to be stored. A HashTable may be resized, but it should _ONLY_ be resized if there is _NO_ data in the table. Future iterations of this template may allow for resizing by creating a new table, copying over the old stored data into their new positions, then replacing the old table with the new one. ================================================================ */ #ifndef HASHTABLE_HPP #define HASHTABLE_HPP #include // malloc #include // strlen #include // INT_MAX #include template class HashEntry { public: HashEntry(const char *key_, T data_) { int key_len = strlen(key_)+1; key = (char*)malloc(key_len*sizeof(char)); memcpy(key, key_, key_len*sizeof(char)); data = data_; next = NULL; }; ~HashEntry() { free(key); }; char *key; T data; HashEntry *next; }; template class HashTable { public: HashTable() { HashTable(128); }; HashTable(int hash_size) { pos = 0; // iterator pos size = hash_size; iter = NULL; last_iter = NULL; table = (HashEntry**)malloc(sizeof(HashEntry*)*size); for (int i = 0; i < size; i++) { table[i] = NULL; } }; ~HashTable() { for (int i = 0; i < size; i++) { if (table[i] != NULL) delete table[i]; } free(table); }; // resize should _only_ be called when the HashTable has _no_ data! void resize(int hash_size) { size = hash_size; table = (HashEntry**)realloc(table, sizeof(HashEntry*)*size); for (int i = 0; i < size; i++) { table[i] = NULL; } }; void setnull(T null_value) { null = null_value; } // T get(const char* key) { int key_hash = getHash(key); // find our hash if it exists HashEntry *entry = table[key_hash]; while (entry != NULL) { if (strcmp(entry->key, key) == 0) { return entry->data; } entry = entry->next; } // doesn't exist, return the user's null value return null; }; bool exists(const char *key) { int key_hash = getHash(key); // find our hash if it exists HashEntry *entry = table[key_hash]; while (entry != NULL) { if (strcmp(entry->key, key) == 0) { return true; } entry = entry->next; } // doesn't exist, return the user's null value return false; } T set(const char *key, T value) { int key_hash = getHash(key); // find our hash if it exists HashEntry *entry = table[key_hash]; if (entry == NULL) { table[key_hash] = new HashEntry(key, value); return table[key_hash]->data; } while (entry->next != NULL) { if (strcmp(entry->key, key) == 0) { entry->data = value; return entry->data; } entry = entry->next; } entry->next = new HashEntry(key, value); return entry->next->data; }; void del(const char *key) { int key_hash = getHash(key); HashEntry *entry = table[key_hash]; HashEntry *last_entry = NULL; if (entry != NULL) { if (strcmp(entry->key, key) == 0) { table[key_hash] = entry->next; delete entry; return; } entry = entry->next; } while (entry != NULL) { if (strcmp(entry->key, key) == 0) { if (last_entry != NULL) last_entry->next = entry->next; delete entry; return; } last_entry = entry; entry = entry->next; } } int getHash(const char *key) { int key_hash = 0; int key_len = strlen(key); // generate our hash for (int i = 0; key_hash < INT_MAX && i < key_len; i++) { key_hash += key[i]; } // limit hash to the HashTable's size and return return (key_hash % size); } /*T iterate() { int i = 1; while(i) { if (iter == NULL) { if (pos > size) { iter = NULL; pos = 0; return null; } iter = table[pos++]; } else { if (iter->data != null) { std::cout << "found one at" << i-1 << std::endl; HashEntry *ret = iter; iter = iter->next; return ret->data; } } } return iter->data; }*/ T operator[](char* key) { int key_hash = 0; int key_len = strlen(key); // get our key hash for (int i = 0; key_hash < INT_MAX && i < key_len; i++) { key_hash += key[i]; } // limit it to our HashTable's size key_hash = key_hash % size; // find our hash if it exists HashEntry *entry = table[key_hash]; while (entry != NULL) { if (strcmp(entry->key, key) == 0) { return entry->data; } entry = entry->next; } // doesn't exist, return the user's null value return null; } T& operator[](char *key) const { int key_hash = 0; int key_len = strlen(key); // get our key hash for (int i = 0; key_hash < INT_MAX && i < key_len; i++) { key_hash += key[i]; } // limit it to our HashTable's size key_hash = key_hash % size; // find our hash if it exists HashEntry *last_entry, *entry = table[key_hash]; if (entry == NULL) { table[key_hash] = new HashEntry(key, null); return entry->data; } while (entry->next != NULL) { if (strcmp(entry->key, key) == 0) { return entry->data; } last_entry = entry; entry = entry->next; } entry->next = new HashEntry(key, null); return entry->next; } HashEntry *iterate() { int i = 1; while(i) { if (iter == NULL) { if (pos >= size) { iter = NULL; pos = 0; return NULL; } iter = table[pos++]; } else { HashEntry *ret = iter; iter = iter->next; return ret; } } return NULL; } private: int size; T null; // user defined "null" value HashEntry **table; int pos; HashEntry *iter; HashEntry *last_iter; }; #endif