The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 1 | // Copyright 2006 The Android Open Source Project |
| 2 | |
| 3 | #ifndef HASH_TABLE_H |
| 4 | #define HASH_TABLE_H |
| 5 | |
| 6 | #include <string.h> |
| 7 | #include <inttypes.h> |
| 8 | |
| 9 | template<class T> |
| 10 | class HashTable { |
| 11 | public: |
| 12 | HashTable(int size, T default_value = T()); |
| 13 | ~HashTable(); |
| 14 | |
| 15 | typedef struct entry { |
| 16 | entry *next; |
| 17 | char *key; |
| 18 | T value; |
| 19 | } entry_type; |
| 20 | |
| 21 | typedef T value_type; |
| 22 | |
| 23 | void Update(const char *key, T value); |
Jack Veenstra | 2bb9bb4 | 2009-05-19 15:07:29 -0700 | [diff] [blame^] | 24 | bool Remove(const char *key); |
The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 25 | T Find(const char *key); |
| 26 | entry_type* GetFirst(); |
| 27 | entry_type* GetNext(); |
| 28 | |
| 29 | private: |
| 30 | uint32_t HashFunction(const char *key); |
| 31 | |
| 32 | int size_; |
| 33 | int mask_; |
| 34 | T default_value_; |
| 35 | entry_type **table_; |
| 36 | int num_entries_; |
| 37 | int current_index_; |
| 38 | entry_type *current_ptr_; |
| 39 | }; |
| 40 | |
| 41 | template<class T> |
| 42 | HashTable<T>::HashTable(int size, T default_value) |
| 43 | { |
| 44 | int pow2; |
| 45 | |
| 46 | // Round up size to a power of two |
| 47 | for (pow2 = 2; pow2 < size; pow2 <<= 1) |
| 48 | ; // empty body |
| 49 | |
| 50 | size_ = pow2; |
| 51 | mask_ = pow2 - 1; |
| 52 | default_value_ = default_value; |
| 53 | |
| 54 | // Allocate a table of pointers and initialize them all to NULL. |
| 55 | table_ = new entry_type*[size_]; |
| 56 | for (int ii = 0; ii < size_; ++ii) |
| 57 | table_[ii] = NULL; |
| 58 | num_entries_ = 0; |
| 59 | current_index_ = 0; |
| 60 | current_ptr_ = NULL; |
| 61 | } |
| 62 | |
| 63 | template<class T> |
| 64 | HashTable<T>::~HashTable() |
| 65 | { |
| 66 | for (int ii = 0; ii < size_; ++ii) { |
| 67 | entry_type *ptr, *next; |
| 68 | |
| 69 | // Delete all the pointers in the chain at this table position. |
| 70 | // Save the next pointer before deleting each entry so that we |
| 71 | // don't dereference part of a deallocated object. |
| 72 | for (ptr = table_[ii]; ptr; ptr = next) { |
| 73 | next = ptr->next; |
| 74 | delete[] ptr->key; |
| 75 | delete ptr; |
| 76 | } |
| 77 | } |
| 78 | delete[] table_; |
| 79 | } |
| 80 | |
| 81 | // Professor Daniel J. Bernstein's hash function. See |
| 82 | // http://www.partow.net/programming/hashfunctions/ |
| 83 | template<class T> |
| 84 | uint32_t HashTable<T>::HashFunction(const char *key) |
| 85 | { |
| 86 | uint32_t hash = 5381; |
| 87 | |
| 88 | int len = strlen(key); |
| 89 | for(int ii = 0; ii < len; ++key, ++ii) |
| 90 | hash = ((hash << 5) + hash) + *key; |
| 91 | |
| 92 | return hash; |
| 93 | } |
| 94 | |
| 95 | template<class T> |
| 96 | void HashTable<T>::Update(const char *key, T value) |
| 97 | { |
| 98 | // Hash the key to get the table position |
| 99 | int len = strlen(key); |
| 100 | int pos = HashFunction(key) & mask_; |
| 101 | |
| 102 | // Search the chain for a matching key |
| 103 | for (entry_type *ptr = table_[pos]; ptr; ptr = ptr->next) { |
| 104 | if (strcmp(ptr->key, key) == 0) { |
| 105 | ptr->value = value; |
| 106 | return; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // Create a new hash entry and fill in the values |
| 111 | entry_type *ptr = new entry_type; |
| 112 | |
| 113 | // Copy the string |
| 114 | ptr->key = new char[len + 1]; |
| 115 | strcpy(ptr->key, key); |
| 116 | ptr->value = value; |
| 117 | |
| 118 | // Insert the new entry at the beginning of the list |
| 119 | ptr->next = table_[pos]; |
| 120 | table_[pos] = ptr; |
| 121 | num_entries_ += 1; |
| 122 | } |
| 123 | |
| 124 | template<class T> |
Jack Veenstra | 2bb9bb4 | 2009-05-19 15:07:29 -0700 | [diff] [blame^] | 125 | bool HashTable<T>::Remove(const char *key) |
| 126 | { |
| 127 | // Hash the key to get the table position |
| 128 | int len = strlen(key); |
| 129 | int pos = HashFunction(key) & mask_; |
| 130 | |
| 131 | // Search the chain for a matching key and keep track of the previous |
| 132 | // element in the chain. |
| 133 | entry_type *prev = NULL; |
| 134 | for (entry_type *ptr = table_[pos]; ptr; prev = ptr, ptr = ptr->next) { |
| 135 | if (strcmp(ptr->key, key) == 0) { |
| 136 | if (prev == NULL) { |
| 137 | table_[pos] = ptr->next; |
| 138 | } else { |
| 139 | prev->next = ptr->next; |
| 140 | } |
| 141 | delete ptr->key; |
| 142 | delete ptr; |
| 143 | return true; |
| 144 | } |
| 145 | } |
| 146 | return false; |
| 147 | } |
| 148 | |
| 149 | template<class T> |
The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 150 | typename HashTable<T>::value_type HashTable<T>::Find(const char *key) |
| 151 | { |
| 152 | // Hash the key to get the table position |
| 153 | int pos = HashFunction(key) & mask_; |
| 154 | |
| 155 | // Search the chain for a matching key |
| 156 | for (entry_type *ptr = table_[pos]; ptr; ptr = ptr->next) { |
| 157 | if (strcmp(ptr->key, key) == 0) |
| 158 | return ptr->value; |
| 159 | } |
| 160 | |
| 161 | // If we get here, then we didn't find the key |
| 162 | return default_value_; |
| 163 | } |
| 164 | |
| 165 | template<class T> |
| 166 | typename HashTable<T>::entry_type* HashTable<T>::GetFirst() |
| 167 | { |
| 168 | // Find the first non-NULL table entry. |
| 169 | for (current_index_ = 0; current_index_ < size_; ++current_index_) { |
| 170 | if (table_[current_index_]) |
| 171 | break; |
| 172 | } |
| 173 | |
| 174 | // If there are no table entries, then return NULL. |
| 175 | if (current_index_ == size_) |
| 176 | return NULL; |
| 177 | |
| 178 | // Remember and return the current element. |
| 179 | current_ptr_ = table_[current_index_]; |
| 180 | return current_ptr_; |
| 181 | } |
| 182 | |
| 183 | template<class T> |
| 184 | typename HashTable<T>::entry_type* HashTable<T>::GetNext() |
| 185 | { |
| 186 | // If we already iterated part way through the hash table, then continue |
| 187 | // to the next element. |
| 188 | if (current_ptr_) { |
| 189 | current_ptr_ = current_ptr_->next; |
| 190 | |
| 191 | // If we are pointing to a valid element, then return it. |
| 192 | if (current_ptr_) |
| 193 | return current_ptr_; |
| 194 | |
| 195 | // Otherwise, start searching at the next table index. |
| 196 | current_index_ += 1; |
| 197 | } |
| 198 | |
| 199 | // Find the next non-NULL table entry. |
| 200 | for (; current_index_ < size_; ++current_index_) { |
| 201 | if (table_[current_index_]) |
| 202 | break; |
| 203 | } |
| 204 | |
| 205 | // If there are no more non-NULL table entries, then return NULL. |
| 206 | if (current_index_ == size_) { |
| 207 | // Reset the current index so that we will start over from the |
| 208 | // beginning on the next call to GetNext(). |
| 209 | current_index_ = 0; |
| 210 | return NULL; |
| 211 | } |
| 212 | |
| 213 | // Remember and return the current element. |
| 214 | current_ptr_ = table_[current_index_]; |
| 215 | return current_ptr_; |
| 216 | } |
| 217 | |
| 218 | |
| 219 | #endif // HASH_TABLE_H |