1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 #ifndef _KERNEL_UTIL_OPEN_HASH_TABLE_H 6 #define _KERNEL_UTIL_OPEN_HASH_TABLE_H 7 8 9 #include <OS.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #ifdef _KERNEL_MODE 14 # include <KernelExport.h> 15 # include <util/kernel_cpp.h> 16 #endif 17 18 19 /*! 20 The Definition template must have four methods: `HashKey', `Hash', 21 `Compare' and `GetLink;. It must also define several types as shown in the 22 following example: 23 24 struct Foo { 25 int bar; 26 27 Foo* fNext; 28 }; 29 30 struct HashTableDefinition { 31 typedef int KeyType; 32 typedef Foo ValueType; 33 34 HashTableDefinition(const HashTableDefinition&) {} 35 36 size_t HashKey(int key) const { return key >> 1; } 37 size_t Hash(Foo* value) const { return HashKey(value->bar); } 38 bool Compare(int key, Foo* value) const { return value->bar == key; } 39 Foo*& GetLink(Foo* value) const { return value->fNext; } 40 }; 41 */ 42 43 44 template<typename Definition, bool AutoExpand = true, 45 bool CheckDuplicates = false> 46 class BOpenHashTable { 47 public: 48 typedef BOpenHashTable<Definition, AutoExpand, CheckDuplicates> HashTable; 49 typedef typename Definition::KeyType KeyType; 50 typedef typename Definition::ValueType ValueType; 51 52 static const size_t kMinimumSize = 8; 53 54 // we use malloc() / free() for allocation. If in the future this 55 // is revealed to be insufficient we can switch to a template based 56 // allocator. All allocations are of power of 2 lengths. 57 58 // regrowth factor: 200 / 256 = 78.125% 59 // 50 / 256 = 19.53125% 60 61 BOpenHashTable() 62 : 63 fTableSize(0), 64 fItemCount(0), 65 fTable(NULL) 66 { 67 } 68 69 BOpenHashTable(const Definition& definition) 70 : 71 fDefinition(definition), 72 fTableSize(0), 73 fItemCount(0), 74 fTable(NULL) 75 { 76 } 77 78 ~BOpenHashTable() 79 { 80 free(fTable); 81 } 82 83 status_t Init(size_t initialSize = kMinimumSize) 84 { 85 if (initialSize > 0 && !_Resize(initialSize)) 86 return B_NO_MEMORY; 87 return B_OK; 88 } 89 90 size_t TableSize() const 91 { 92 return fTableSize; 93 } 94 95 size_t CountElements() const 96 { 97 return fItemCount; 98 } 99 100 ValueType* Lookup(const KeyType& key) const 101 { 102 if (fTableSize == 0) 103 return NULL; 104 105 size_t index = fDefinition.HashKey(key) & (fTableSize - 1); 106 ValueType* slot = fTable[index]; 107 108 while (slot) { 109 if (fDefinition.Compare(key, slot)) 110 break; 111 slot = _Link(slot); 112 } 113 114 return slot; 115 } 116 117 status_t Insert(ValueType* value) 118 { 119 if (fTableSize == 0) { 120 if (!_Resize(kMinimumSize)) 121 return B_NO_MEMORY; 122 } else if (AutoExpand && fItemCount >= (fTableSize * 200 / 256)) 123 _Resize(fTableSize * 2); 124 125 InsertUnchecked(value); 126 return B_OK; 127 } 128 129 void InsertUnchecked(ValueType* value) 130 { 131 if (CheckDuplicates && _ExhaustiveSearch(value)) { 132 #ifdef _KERNEL_MODE 133 panic("Hash Table: value already in table."); 134 #else 135 debugger("Hash Table: value already in table."); 136 #endif 137 } 138 139 _Insert(fTable, fTableSize, value); 140 fItemCount++; 141 } 142 143 // TODO: a ValueType* Remove(const KeyType& key) method is missing 144 145 bool Remove(ValueType* value) 146 { 147 if (!RemoveUnchecked(value)) 148 return false; 149 150 if (AutoExpand && fTableSize > kMinimumSize 151 && fItemCount < (fTableSize * 50 / 256)) 152 _Resize(fTableSize / 2); 153 154 return true; 155 } 156 157 bool RemoveUnchecked(ValueType* value) 158 { 159 size_t index = fDefinition.Hash(value) & (fTableSize - 1); 160 ValueType* previous = NULL; 161 ValueType* slot = fTable[index]; 162 163 while (slot) { 164 ValueType* next = _Link(slot); 165 166 if (value == slot) { 167 if (previous) 168 _Link(previous) = next; 169 else 170 fTable[index] = next; 171 break; 172 } 173 174 previous = slot; 175 slot = next; 176 } 177 178 if (slot == NULL) 179 return false; 180 181 if (CheckDuplicates && _ExhaustiveSearch(value)) { 182 #ifdef _KERNEL_MODE 183 panic("Hash Table: duplicate detected."); 184 #else 185 debugger("Hash Table: duplicate detected."); 186 #endif 187 } 188 189 fItemCount--; 190 return true; 191 } 192 193 /*! Removes all elements from the hash table. No resizing happens. The 194 elements are not deleted. If \a returnElements is \c true, the method 195 returns all elements chained via their hash table link. 196 */ 197 ValueType* Clear(bool returnElements = false) 198 { 199 if (this->fItemCount == 0) 200 return NULL; 201 202 ValueType* result = NULL; 203 204 if (returnElements) { 205 ValueType** nextPointer = &result; 206 207 // iterate through all buckets 208 for (size_t i = 0; i < fTableSize; i++) { 209 ValueType* element = fTable[i]; 210 if (element != NULL) { 211 // add the bucket to the list 212 *nextPointer = element; 213 214 // update nextPointer to point to the fNext of the last 215 // element in the bucket 216 while (element != NULL) { 217 nextPointer = &_Link(element); 218 element = *nextPointer; 219 } 220 } 221 } 222 } 223 224 memset(this->fTable, 0, sizeof(ValueType*) * this->fTableSize); 225 226 return result; 227 } 228 229 /*! If the table needs resizing, the number of bytes for the required 230 allocation is returned. If no resizing is needed, 0 is returned. 231 */ 232 size_t ResizeNeeded() const 233 { 234 size_t size = fTableSize; 235 if (size == 0 || fItemCount >= (size * 200 / 256)) { 236 // grow table 237 if (size == 0) 238 size = kMinimumSize; 239 while (fItemCount >= size * 200 / 256) 240 size <<= 1; 241 } else if (size > kMinimumSize && fItemCount < size * 50 / 256) { 242 // shrink table 243 while (fItemCount < size * 50 / 256) 244 size >>= 1; 245 if (size < kMinimumSize) 246 size = kMinimumSize; 247 } 248 249 if (size == fTableSize) 250 return 0; 251 252 return size * sizeof(ValueType*); 253 } 254 255 /*! Resizes the table using the given allocation. The allocation must not 256 be \c NULL. It must be of size \a size, which must a value returned 257 earlier by ResizeNeeded(). If the size requirements have changed in the 258 meantime, the method free()s the given allocation and returns \c false. 259 Otherwise \c true is returned. 260 */ 261 bool Resize(void* allocation, size_t size) 262 { 263 if (size != ResizeNeeded()) { 264 free(allocation); 265 return false; 266 } 267 268 _Resize((ValueType**)allocation, size / sizeof(ValueType*)); 269 return true; 270 } 271 272 class Iterator { 273 public: 274 Iterator(const HashTable* table) 275 : fTable(table) 276 { 277 Rewind(); 278 } 279 280 Iterator(const HashTable* table, size_t index, ValueType* value) 281 : fTable(table), fIndex(index), fNext(value) {} 282 283 bool HasNext() const { return fNext != NULL; } 284 285 ValueType* Next() 286 { 287 ValueType* current = fNext; 288 _GetNext(); 289 return current; 290 } 291 292 void Rewind() 293 { 294 // get the first one 295 fIndex = 0; 296 fNext = NULL; 297 _GetNext(); 298 } 299 300 protected: 301 Iterator() {} 302 303 void _GetNext() 304 { 305 if (fNext) 306 fNext = fTable->_Link(fNext); 307 308 while (fNext == NULL && fIndex < fTable->fTableSize) 309 fNext = fTable->fTable[fIndex++]; 310 } 311 312 const HashTable* fTable; 313 size_t fIndex; 314 ValueType* fNext; 315 }; 316 317 Iterator GetIterator() const 318 { 319 return Iterator(this); 320 } 321 322 Iterator GetIterator(const KeyType& key) const 323 { 324 if (fTableSize == 0) 325 return Iterator(this, fTableSize, NULL); 326 327 size_t index = fDefinition.HashKey(key) & (fTableSize - 1); 328 ValueType* slot = fTable[index]; 329 330 while (slot) { 331 if (fDefinition.Compare(key, slot)) 332 break; 333 slot = _Link(slot); 334 } 335 336 if (slot == NULL) 337 return Iterator(this, fTableSize, NULL); 338 339 return Iterator(this, index + 1, slot); 340 } 341 342 protected: 343 // for g++ 2.95 344 friend class Iterator; 345 346 void _Insert(ValueType** table, size_t tableSize, ValueType* value) 347 { 348 size_t index = fDefinition.Hash(value) & (tableSize - 1); 349 350 _Link(value) = table[index]; 351 table[index] = value; 352 } 353 354 bool _Resize(size_t newSize) 355 { 356 ValueType** newTable 357 = (ValueType**)malloc(sizeof(ValueType*) * newSize); 358 if (newTable == NULL) 359 return false; 360 361 _Resize(newTable, newSize); 362 return true; 363 } 364 365 void _Resize(ValueType** newTable, size_t newSize) 366 { 367 for (size_t i = 0; i < newSize; i++) 368 newTable[i] = NULL; 369 370 if (fTable) { 371 for (size_t i = 0; i < fTableSize; i++) { 372 ValueType* bucket = fTable[i]; 373 while (bucket) { 374 ValueType* next = _Link(bucket); 375 _Insert(newTable, newSize, bucket); 376 bucket = next; 377 } 378 } 379 380 free(fTable); 381 } 382 383 fTableSize = newSize; 384 fTable = newTable; 385 } 386 387 ValueType*& _Link(ValueType* bucket) const 388 { 389 return fDefinition.GetLink(bucket); 390 } 391 392 bool _ExhaustiveSearch(ValueType* value) const 393 { 394 for (size_t i = 0; i < fTableSize; i++) { 395 ValueType* bucket = fTable[i]; 396 while (bucket) { 397 if (bucket == value) 398 return true; 399 bucket = _Link(bucket); 400 } 401 } 402 403 return false; 404 } 405 406 Definition fDefinition; 407 size_t fTableSize; 408 size_t fItemCount; 409 ValueType** fTable; 410 }; 411 412 #endif // _KERNEL_UTIL_OPEN_HASH_TABLE_H 413