btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / headers / private / kernel / util / OpenHashTable.h
blobbb1479cd7bc968ad43730e15a370524d72871094
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
9 #include <OS.h>
10 #include <stdlib.h>
11 #include <string.h>
13 #ifdef _KERNEL_MODE
14 # include <KernelExport.h>
15 # include <util/kernel_cpp.h>
16 # include <util/TypeOperation.h>
17 #else
18 # include <TypeOperation.h>
19 #endif
22 /*!
23 The Definition template must have four methods: `HashKey', `Hash',
24 `Compare' and `GetLink;. It must also define several types as shown in the
25 following example:
27 struct Foo {
28 int bar;
30 Foo* fNext;
33 struct HashTableDefinition {
34 typedef int KeyType;
35 typedef Foo ValueType;
37 size_t HashKey(KeyType key) const
39 return key >> 1;
42 size_t Hash(ValueType* value) const
44 return HashKey(value->bar);
47 bool Compare(KeyType key, ValueType* value) const
49 return value->bar == key;
52 ValueType*& GetLink(ValueType* value) const
54 return value->fNext;
60 struct MallocAllocator {
61 void* Allocate(size_t size) const
63 return malloc(size);
66 void Free(void* memory) const
68 free(memory);
73 /** Implements an hash table with open hashing, that is, colliding entries are
74 * stored in a linked list. The table may be made to adjust its number of slots
75 * depending on the load factor (this should be enabled unless the object is to
76 * be used at times where memory allocations aren't possible, such as code
77 * called by the memory allocator).
79 * The link between entries is part of the ValueType stored items, which makes
80 * sure the table can always accept new items and will never fail because it is
81 * out of memory (except at Init time).
83 template<typename Definition, bool AutoExpand = true,
84 bool CheckDuplicates = false, typename Allocator = MallocAllocator>
85 class BOpenHashTable {
86 public:
87 typedef BOpenHashTable<Definition, AutoExpand, CheckDuplicates> HashTable;
88 typedef typename Definition::KeyType KeyType;
89 typedef typename Definition::ValueType ValueType;
91 static const size_t kMinimumSize = 8;
93 // All allocations are of power of 2 lengths.
95 // regrowth factor: 200 / 256 = 78.125%
96 // 50 / 256 = 19.53125%
98 BOpenHashTable()
100 fTableSize(0),
101 fItemCount(0),
102 fTable(NULL)
106 BOpenHashTable(const Definition& definition)
108 fDefinition(definition),
109 fTableSize(0),
110 fItemCount(0),
111 fTable(NULL)
115 BOpenHashTable(const Definition& definition, const Allocator& allocator)
117 fDefinition(definition),
118 fAllocator(allocator),
119 fTableSize(0),
120 fItemCount(0),
121 fTable(NULL)
125 ~BOpenHashTable()
127 fAllocator.Free(fTable);
130 status_t Init(size_t initialSize = kMinimumSize)
132 if (initialSize > 0 && !_Resize(initialSize))
133 return B_NO_MEMORY;
134 return B_OK;
137 size_t TableSize() const
139 return fTableSize;
142 bool IsEmpty() const
144 return fItemCount == 0;
147 size_t CountElements() const
149 return fItemCount;
152 ValueType* Lookup(typename TypeOperation<KeyType>::ConstRefT key) const
154 if (fTableSize == 0)
155 return NULL;
157 size_t index = fDefinition.HashKey(key) & (fTableSize - 1);
158 ValueType* slot = fTable[index];
160 while (slot) {
161 if (fDefinition.Compare(key, slot))
162 break;
163 slot = _Link(slot);
166 return slot;
169 status_t Insert(ValueType* value)
171 if (fTableSize == 0) {
172 if (!_Resize(kMinimumSize))
173 return B_NO_MEMORY;
174 } else if (AutoExpand && fItemCount >= (fTableSize * 200 / 256))
175 _Resize(fTableSize * 2);
177 InsertUnchecked(value);
178 return B_OK;
181 /*! \brief Inserts a value without resizing the table.
183 Use this method if you need to insert a value into the table while
184 iterating it, as regular insertion can invalidate iterators.
186 void InsertUnchecked(ValueType* value)
188 if (CheckDuplicates && _ExhaustiveSearch(value)) {
189 #ifdef _KERNEL_MODE
190 panic("Hash Table: value already in table.");
191 #else
192 debugger("Hash Table: value already in table.");
193 #endif
196 _Insert(fTable, fTableSize, value);
197 fItemCount++;
200 // TODO: a ValueType* Remove(const KeyType& key) method is missing
202 bool Remove(ValueType* value)
204 if (!RemoveUnchecked(value))
205 return false;
207 if (AutoExpand && fTableSize > kMinimumSize
208 && fItemCount < (fTableSize * 50 / 256))
209 _Resize(fTableSize / 2);
211 return true;
214 /*! \brief Removes a value without resizing the table.
216 Use this method if you need to remove a value from the table while
217 iterating it, as Remove can invalidate iterators.
219 Also use this method if you know you are going to reinsert the item soon
220 (possibly with a different hash) to avoid shrinking then growing the
221 table again.
223 bool RemoveUnchecked(ValueType* value)
225 size_t index = fDefinition.Hash(value) & (fTableSize - 1);
226 ValueType* previous = NULL;
227 ValueType* slot = fTable[index];
229 while (slot) {
230 ValueType* next = _Link(slot);
232 if (value == slot) {
233 if (previous)
234 _Link(previous) = next;
235 else
236 fTable[index] = next;
237 break;
240 previous = slot;
241 slot = next;
244 if (slot == NULL)
245 return false;
247 if (CheckDuplicates && _ExhaustiveSearch(value)) {
248 #ifdef _KERNEL_MODE
249 panic("Hash Table: duplicate detected.");
250 #else
251 debugger("Hash Table: duplicate detected.");
252 #endif
255 fItemCount--;
256 return true;
259 /*! \brief Removes all elements from the hash table.
261 No resizing happens. The elements are not deleted. If \a returnElements
262 is \c true, the method returns all elements chained via their hash table
263 link.
265 ValueType* Clear(bool returnElements = false)
267 if (fItemCount == 0)
268 return NULL;
270 ValueType* result = NULL;
272 if (returnElements) {
273 ValueType** nextPointer = &result;
275 // iterate through all buckets
276 for (size_t i = 0; i < fTableSize; i++) {
277 ValueType* element = fTable[i];
278 if (element != NULL) {
279 // add the bucket to the list
280 *nextPointer = element;
282 // update nextPointer to point to the fNext of the last
283 // element in the bucket
284 while (element != NULL) {
285 nextPointer = &_Link(element);
286 element = *nextPointer;
292 memset(this->fTable, 0, sizeof(ValueType*) * this->fTableSize);
293 fItemCount = 0;
295 return result;
298 /*! If the table needs resizing, the number of bytes for the required
299 allocation is returned. If no resizing is needed, 0 is returned.
301 size_t ResizeNeeded() const
303 size_t size = fTableSize;
304 if (size == 0 || fItemCount >= (size * 200 / 256)) {
305 // grow table
306 if (size == 0)
307 size = kMinimumSize;
308 while (fItemCount >= size * 200 / 256)
309 size <<= 1;
310 } else if (size > kMinimumSize && fItemCount < size * 50 / 256) {
311 // shrink table
312 while (fItemCount < size * 50 / 256)
313 size >>= 1;
314 if (size < kMinimumSize)
315 size = kMinimumSize;
318 if (size == fTableSize)
319 return 0;
321 return size * sizeof(ValueType*);
324 /*! Resizes the table using the given allocation. The allocation must not
325 be \c NULL. It must be of size \a size, which must be a value returned
326 earlier by ResizeNeeded(). If the size requirements have changed in the
327 meantime, the method free()s the given allocation and returns \c false,
328 unless \a force is \c true, in which case the supplied allocation is
329 used in any event.
330 Otherwise \c true is returned.
331 If \a oldTable is non-null and resizing is successful, the old table
332 will not be freed, but will be returned via this parameter instead.
334 bool Resize(void* allocation, size_t size, bool force = false,
335 void** oldTable = NULL)
337 if (!force && size != ResizeNeeded()) {
338 fAllocator.Free(allocation);
339 return false;
342 _Resize((ValueType**)allocation, size / sizeof(ValueType*), oldTable);
343 return true;
346 /*! \brief Iterator for BOpenHashMap
348 The iterator is not invalidated when removing the current element from
349 the table, unless the removal triggers a resize.
351 class Iterator {
352 public:
353 Iterator(const HashTable* table)
354 : fTable(table)
356 Rewind();
359 Iterator(const HashTable* table, size_t index, ValueType* value)
360 : fTable(table), fIndex(index), fNext(value) {}
362 bool HasNext() const { return fNext != NULL; }
364 ValueType* Next()
366 ValueType* current = fNext;
367 _GetNext();
368 return current;
371 void Rewind()
373 // get the first one
374 fIndex = 0;
375 fNext = NULL;
376 _GetNext();
379 protected:
380 Iterator() {}
382 void _GetNext()
384 if (fNext)
385 fNext = fTable->_Link(fNext);
387 while (fNext == NULL && fIndex < fTable->fTableSize)
388 fNext = fTable->fTable[fIndex++];
391 const HashTable* fTable;
392 size_t fIndex;
393 ValueType* fNext;
396 Iterator GetIterator() const
398 return Iterator(this);
401 Iterator GetIterator(typename TypeOperation<KeyType>::ConstRefT key) const
403 if (fTableSize == 0)
404 return Iterator(this, fTableSize, NULL);
406 size_t index = fDefinition.HashKey(key) & (fTableSize - 1);
407 ValueType* slot = fTable[index];
409 while (slot) {
410 if (fDefinition.Compare(key, slot))
411 break;
412 slot = _Link(slot);
415 if (slot == NULL)
416 return Iterator(this, fTableSize, NULL);
418 return Iterator(this, index + 1, slot);
421 protected:
422 // for g++ 2.95
423 friend class Iterator;
425 void _Insert(ValueType** table, size_t tableSize, ValueType* value)
427 size_t index = fDefinition.Hash(value) & (tableSize - 1);
429 _Link(value) = table[index];
430 table[index] = value;
433 bool _Resize(size_t newSize)
435 ValueType** newTable
436 = (ValueType**)fAllocator.Allocate(sizeof(ValueType*) * newSize);
437 if (newTable == NULL)
438 return false;
440 _Resize(newTable, newSize);
441 return true;
444 void _Resize(ValueType** newTable, size_t newSize, void** _oldTable = NULL)
446 for (size_t i = 0; i < newSize; i++)
447 newTable[i] = NULL;
449 if (fTable) {
450 for (size_t i = 0; i < fTableSize; i++) {
451 ValueType* bucket = fTable[i];
452 while (bucket) {
453 ValueType* next = _Link(bucket);
454 _Insert(newTable, newSize, bucket);
455 bucket = next;
459 if (_oldTable != NULL)
460 *_oldTable = fTable;
461 else
462 fAllocator.Free(fTable);
463 } else if (_oldTable != NULL)
464 *_oldTable = NULL;
466 fTableSize = newSize;
467 fTable = newTable;
470 ValueType*& _Link(ValueType* bucket) const
472 return fDefinition.GetLink(bucket);
475 bool _ExhaustiveSearch(ValueType* value) const
477 for (size_t i = 0; i < fTableSize; i++) {
478 ValueType* bucket = fTable[i];
479 while (bucket) {
480 if (bucket == value)
481 return true;
482 bucket = _Link(bucket);
486 return false;
489 Definition fDefinition;
490 Allocator fAllocator;
491 size_t fTableSize;
492 size_t fItemCount;
493 ValueType** fTable;
496 #endif // _KERNEL_UTIL_OPEN_HASH_TABLE_H