194 lines
4.8 KiB
C++
194 lines
4.8 KiB
C++
// 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);
|
|
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>
|
|
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
|