// Copyright 2006 The Android Open Source Project
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include <string.h>
#include <inttypes.h>
template<class T>
class HashTable {
public:
HashTable(int size, T default_value = T());
~HashTable();
typedef struct entry {
entry *next;
char *key;
T value;
} entry_type;
typedef T value_type;
void Update(const char *key, T value);
bool Remove(const char *key);
T Find(const char *key);
entry_type* GetFirst();
entry_type* GetNext();
private:
uint32_t HashFunction(const char *key);
int size_;
int mask_;
T default_value_;
entry_type **table_;
int num_entries_;
int current_index_;
entry_type *current_ptr_;
};
template<class T>
HashTable<T>::HashTable(int size, T default_value)
{
int pow2;
// Round up size to a power of two
for (pow2 = 2; pow2 < size; pow2 <<= 1)
; // empty body
size_ = pow2;
mask_ = pow2 - 1;
default_value_ = default_value;
// Allocate a table of pointers and initialize them all to NULL.
table_ = new entry_type*[size_];
for (int ii = 0; ii < size_; ++ii)
table_[ii] = NULL;
num_entries_ = 0;
current_index_ = 0;
current_ptr_ = NULL;
}
template<class T>
HashTable<T>::~HashTable()
{
for (int ii = 0; ii < size_; ++ii) {
entry_type *ptr, *next;
// Delete all the pointers in the chain at this table position.
// Save the next pointer before deleting each entry so that we
// don't dereference part of a deallocated object.
for (ptr = table_[ii]; ptr; ptr = next) {
next = ptr->next;
delete[] ptr->key;
delete ptr;
}
}
delete[] table_;
}
// Professor Daniel J. Bernstein's hash function. See
// http://www.partow.net/programming/hashfunctions/
template<class T>
uint32_t HashTable<T>::HashFunction(const char *key)
{
uint32_t hash = 5381;
int len = strlen(key);
for(int ii = 0; ii < len; ++key, ++ii)
hash = ((hash << 5) + hash) + *key;
return hash;
}
template<class T>
void HashTable<T>::Update(const char *key, T value)
{
// Hash the key to get the table position
int len = strlen(key);
int pos = HashFunction(key) & mask_;
// Search the chain for a matching key
for (entry_type *ptr = table_[pos]; ptr; ptr = ptr->next) {
if (strcmp(ptr->key, key) == 0) {
ptr->value = value;
return;
}
}
// Create a new hash entry and fill in the values
entry_type *ptr = new entry_type;
// Copy the string
ptr->key = new char[len + 1];
strcpy(ptr->key, key);
ptr->value = value;
// Insert the new entry at the beginning of the list
ptr->next = table_[pos];
table_[pos] = ptr;
num_entries_ += 1;
}
template<class T>
bool HashTable<T>::Remove(const char *key)
{
// Hash the key to get the table position
int len = strlen(key);
int pos = HashFunction(key) & mask_;
// Search the chain for a matching key and keep track of the previous
// element in the chain.
entry_type *prev = NULL;
for (entry_type *ptr = table_[pos]; ptr; prev = ptr, ptr = ptr->next) {
if (strcmp(ptr->key, key) == 0) {
if (prev == NULL) {
table_[pos] = ptr->next;
} else {
prev->next = ptr->next;
}
delete ptr->key;
delete ptr;
return true;
}
}
return false;
}
template<class T>
typename HashTable<T>::value_type HashTable<T>::Find(const char *key)
{
// Hash the key to get the table position
int pos = HashFunction(key) & mask_;
// Search the chain for a matching key
for (entry_type *ptr = table_[pos]; ptr; ptr = ptr->next) {
if (strcmp(ptr->key, key) == 0)
return ptr->value;
}
// If we get here, then we didn't find the key
return default_value_;
}
template<class T>
typename HashTable<T>::entry_type* HashTable<T>::GetFirst()
{
// Find the first non-NULL table entry.
for (current_index_ = 0; current_index_ < size_; ++current_index_) {
if (table_[current_index_])
break;
}
// If there are no table entries, then return NULL.
if (current_index_ == size_)
return NULL;
// Remember and return the current element.
current_ptr_ = table_[current_index_];
return current_ptr_;
}
template<class T>
typename HashTable<T>::entry_type* HashTable<T>::GetNext()
{
// If we already iterated part way through the hash table, then continue
// to the next element.
if (current_ptr_) {
current_ptr_ = current_ptr_->next;
// If we are pointing to a valid element, then return it.
if (current_ptr_)
return current_ptr_;
// Otherwise, start searching at the next table index.
current_index_ += 1;
}
// Find the next non-NULL table entry.
for (; current_index_ < size_; ++current_index_) {
if (table_[current_index_])
break;
}
// If there are no more non-NULL table entries, then return NULL.
if (current_index_ == size_) {
// Reset the current index so that we will start over from the
// beginning on the next call to GetNext().
current_index_ = 0;
return NULL;
}
// Remember and return the current element.
current_ptr_ = table_[current_index_];
return current_ptr_;
}
#endif // HASH_TABLE_H