RtB/src/HashTable.hpp

234 lines
6.9 KiB
C++

/* ================================================================
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 <stdlib.h> // malloc
#include <string.h> // strlen
#include <limits.h> // INT_MAX
#include <iostream>
template<class T> 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 T> class HashTable {
public:
HashTable() { HashTable<T>(128); };
HashTable(int hash_size) {
pos = 0; // iterator pos
size = hash_size;
iter = NULL;
last_iter = NULL;
table = (HashEntry<T>**)malloc(sizeof(HashEntry<T>*)*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<T>**)realloc(table, sizeof(HashEntry<T>*)*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<T> *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<T> *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<T> *entry = table[key_hash];
if (entry == NULL) {
table[key_hash] = new HashEntry<T>(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<T>(key, value);
return entry->next->data;
};
void del(const char *key) {
int key_hash = getHash(key);
HashEntry<T> *entry = table[key_hash];
HashEntry<T> *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<T> *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<T> *last_entry, *entry = table[key_hash];
if (entry == NULL) {
table[key_hash] = new HashEntry<T>(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<T>(key, null);
return entry->next;
}
HashEntry<T> *iterate() {
int i = 1;
while(i) {
if (iter == NULL) {
if (pos >= size) {
iter = NULL;
pos = 0;
return NULL;
}
iter = table[pos++];
} else {
HashEntry<T> *ret = iter;
iter = iter->next;
return ret;
}
}
return NULL;
}
private:
int size;
T null; // user defined "null" value
HashEntry<T> **table;
int pos;
HashEntry<T> *iter;
HashEntry<T> *last_iter;
};
#endif