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