// Copyright 2006 The Android Open Source Project #ifndef HASH_TABLE_H #define HASH_TABLE_H #include #include template 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 HashTable::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 HashTable::~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 uint32_t HashTable::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 void HashTable::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 bool HashTable::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 typename HashTable::value_type HashTable::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 typename HashTable::entry_type* HashTable::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 typename HashTable::entry_type* HashTable::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