234 lines
6.9 KiB
C++
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
|