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