Fix FreeBSD build.
[haiku.git] / src / tools / fs_shell / KOpenHashTable.h
blob363c225fdfb259e9523a9657a02433b301302ad2
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 "fssh_api_wrapper.h"
11 #include <stdlib.h>
12 #include <string.h>
14 #ifdef _KERNEL_MODE
15 # include <KernelExport.h>
16 # include <util/kernel_cpp.h>
17 # include <util/TypeOperation.h>
18 #else
19 # include <TypeOperation.h>
20 #endif
23 /*!
24 The Definition template must have four methods: `HashKey', `Hash',
25 `Compare' and `GetLink;. It must also define several types as shown in the
26 following example:
28 struct Foo {
29 int bar;
31 Foo* fNext;
34 struct HashTableDefinition {
35 typedef int KeyType;
36 typedef Foo ValueType;
38 size_t HashKey(KeyType key) const
40 return key >> 1;
43 size_t Hash(ValueType* value) const
45 return HashKey(value->bar);
48 bool Compare(KeyType key, ValueType* value) const
50 return value->bar == key;
53 ValueType*& GetLink(ValueType* value) const
55 return value->fNext;
61 struct MallocAllocator {
62 void* Allocate(size_t size) const
64 return malloc(size);
67 void Free(void* memory) const
69 free(memory);
74 /** Implements an hash table with open hashing, that is, colliding entries are
75 * stored in a linked list. The table may be made to adjust its number of slots
76 * depending on the load factor (this should be enabled unless the object is to
77 * be used at times where memory allocations aren't possible, such as code
78 * called byt he memory allocator).
80 * The link between entries is part of the ValueType stored items, which makes
81 * sure the table can always accept new items and will never fail because it is
82 * out of memory (except at Init time).
84 template<typename Definition, bool AutoExpand = true,
85 bool CheckDuplicates = false, typename Allocator = MallocAllocator>
86 class BOpenHashTable {
87 public:
88 typedef BOpenHashTable<Definition, AutoExpand, CheckDuplicates> HashTable;
89 typedef typename Definition::KeyType KeyType;
90 typedef typename Definition::ValueType ValueType;
92 static const size_t kMinimumSize = 8;
94 // All allocations are of power of 2 lengths.
96 // regrowth factor: 200 / 256 = 78.125%
97 // 50 / 256 = 19.53125%
99 BOpenHashTable()
101 fTableSize(0),
102 fItemCount(0),
103 fTable(NULL)
107 BOpenHashTable(const Definition& definition)
109 fDefinition(definition),
110 fTableSize(0),
111 fItemCount(0),
112 fTable(NULL)
116 BOpenHashTable(const Definition& definition, const Allocator& allocator)
118 fDefinition(definition),
119 fAllocator(allocator),
120 fTableSize(0),
121 fItemCount(0),
122 fTable(NULL)
126 ~BOpenHashTable()
128 fAllocator.Free(fTable);
131 status_t Init(size_t initialSize = kMinimumSize)
133 if (initialSize > 0 && !_Resize(initialSize))
134 return B_NO_MEMORY;
135 return B_OK;
138 size_t TableSize() const
140 return fTableSize;
143 bool IsEmpty() const
145 return fItemCount == 0;
148 size_t CountElements() const
150 return fItemCount;
153 ValueType* Lookup(typename TypeOperation<KeyType>::ConstRefT key) const
155 if (fTableSize == 0)
156 return NULL;
158 size_t index = fDefinition.HashKey(key) & (fTableSize - 1);
159 ValueType* slot = fTable[index];
161 while (slot) {
162 if (fDefinition.Compare(key, slot))
163 break;
164 slot = _Link(slot);
167 return slot;
170 status_t Insert(ValueType* value)
172 if (fTableSize == 0) {
173 if (!_Resize(kMinimumSize))
174 return B_NO_MEMORY;
175 } else if (AutoExpand && fItemCount >= (fTableSize * 200 / 256))
176 _Resize(fTableSize * 2);
178 InsertUnchecked(value);
179 return B_OK;
182 /*! \brief Inserts a value without resizing the table.
184 Use this method if you need to insert a value into the table while
185 iterating it, as regular insertion can invalidate iterators.
187 void InsertUnchecked(ValueType* value)
189 if (CheckDuplicates && _ExhaustiveSearch(value)) {
190 #if defined(_KERNEL_MODE) || defined(FS_SHELL)
191 panic("Hash Table: value already in table.");
192 #else
193 debugger("Hash Table: value already in table.");
194 #endif
197 _Insert(fTable, fTableSize, value);
198 fItemCount++;
201 // TODO: a ValueType* Remove(const KeyType& key) method is missing
203 bool Remove(ValueType* value)
205 if (!RemoveUnchecked(value))
206 return false;
208 if (AutoExpand && fTableSize > kMinimumSize
209 && fItemCount < (fTableSize * 50 / 256))
210 _Resize(fTableSize / 2);
212 return true;
215 /*! \brief Removes a value without resizing the table.
217 Use this method if you need to remove a value from the table while
218 iterating it, as Remove can invalidate iterators.
220 Also use this method if you know you are going to reinsert the item soon
221 (possibly with a different hash) to avoid shrinking then growing the
222 table again.
224 bool RemoveUnchecked(ValueType* value)
226 size_t index = fDefinition.Hash(value) & (fTableSize - 1);
227 ValueType* previous = NULL;
228 ValueType* slot = fTable[index];
230 while (slot) {
231 ValueType* next = _Link(slot);
233 if (value == slot) {
234 if (previous)
235 _Link(previous) = next;
236 else
237 fTable[index] = next;
238 break;
241 previous = slot;
242 slot = next;
245 if (slot == NULL)
246 return false;
248 if (CheckDuplicates && _ExhaustiveSearch(value)) {
249 #if defined(_KERNEL_MODE) || defined(FS_SHELL)
250 panic("Hash Table: duplicate detected.");
251 #else
252 debugger("Hash Table: duplicate detected.");
253 #endif
256 fItemCount--;
257 return true;
260 /*! \brief Removes all elements from the hash table.
262 No resizing happens. The elements are not deleted. If \a returnElements
263 is \c true, the method returns all elements chained via their hash table
264 link.
266 ValueType* Clear(bool returnElements = false)
268 if (fItemCount == 0)
269 return NULL;
271 ValueType* result = NULL;
273 if (returnElements) {
274 ValueType** nextPointer = &result;
276 // iterate through all buckets
277 for (size_t i = 0; i < fTableSize; i++) {
278 ValueType* element = fTable[i];
279 if (element != NULL) {
280 // add the bucket to the list
281 *nextPointer = element;
283 // update nextPointer to point to the fNext of the last
284 // element in the bucket
285 while (element != NULL) {
286 nextPointer = &_Link(element);
287 element = *nextPointer;
293 memset(this->fTable, 0, sizeof(ValueType*) * this->fTableSize);
294 fItemCount = 0;
296 return result;
299 /*! If the table needs resizing, the number of bytes for the required
300 allocation is returned. If no resizing is needed, 0 is returned.
302 size_t ResizeNeeded() const
304 size_t size = fTableSize;
305 if (size == 0 || fItemCount >= (size * 200 / 256)) {
306 // grow table
307 if (size == 0)
308 size = kMinimumSize;
309 while (fItemCount >= size * 200 / 256)
310 size <<= 1;
311 } else if (size > kMinimumSize && fItemCount < size * 50 / 256) {
312 // shrink table
313 while (fItemCount < size * 50 / 256)
314 size >>= 1;
315 if (size < kMinimumSize)
316 size = kMinimumSize;
319 if (size == fTableSize)
320 return 0;
322 return size * sizeof(ValueType*);
325 /*! Resizes the table using the given allocation. The allocation must not
326 be \c NULL. It must be of size \a size, which must be a value returned
327 earlier by ResizeNeeded(). If the size requirements have changed in the
328 meantime, the method free()s the given allocation and returns \c false,
329 unless \a force is \c true, in which case the supplied allocation is
330 used in any event.
331 Otherwise \c true is returned.
332 If \a oldTable is non-null and resizing is successful, the old table
333 will not be freed, but will be returned via this parameter instead.
335 bool Resize(void* allocation, size_t size, bool force = false,
336 void** oldTable = NULL)
338 if (!force && size != ResizeNeeded()) {
339 fAllocator.Free(allocation);
340 return false;
343 _Resize((ValueType**)allocation, size / sizeof(ValueType*), oldTable);
344 return true;
347 /*! \brief Iterator for BOpenHashMap
349 The iterator is not invalidated when removing the current element from
350 the table, unless the removal triggers a resize.
352 class Iterator {
353 public:
354 Iterator(const HashTable* table)
355 : fTable(table)
357 Rewind();
360 Iterator(const HashTable* table, size_t index, ValueType* value)
361 : fTable(table), fIndex(index), fNext(value) {}
363 bool HasNext() const { return fNext != NULL; }
365 ValueType* Next()
367 ValueType* current = fNext;
368 _GetNext();
369 return current;
372 void Rewind()
374 // get the first one
375 fIndex = 0;
376 fNext = NULL;
377 _GetNext();
380 protected:
381 Iterator() {}
383 void _GetNext()
385 if (fNext)
386 fNext = fTable->_Link(fNext);
388 while (fNext == NULL && fIndex < fTable->fTableSize)
389 fNext = fTable->fTable[fIndex++];
392 const HashTable* fTable;
393 size_t fIndex;
394 ValueType* fNext;
397 Iterator GetIterator() const
399 return Iterator(this);
402 Iterator GetIterator(typename TypeOperation<KeyType>::ConstRefT key) const
404 if (fTableSize == 0)
405 return Iterator(this, fTableSize, NULL);
407 size_t index = fDefinition.HashKey(key) & (fTableSize - 1);
408 ValueType* slot = fTable[index];
410 while (slot) {
411 if (fDefinition.Compare(key, slot))
412 break;
413 slot = _Link(slot);
416 if (slot == NULL)
417 return Iterator(this, fTableSize, NULL);
419 return Iterator(this, index + 1, slot);
422 protected:
423 // for g++ 2.95
424 friend class Iterator;
426 void _Insert(ValueType** table, size_t tableSize, ValueType* value)
428 size_t index = fDefinition.Hash(value) & (tableSize - 1);
430 _Link(value) = table[index];
431 table[index] = value;
434 bool _Resize(size_t newSize)
436 ValueType** newTable
437 = (ValueType**)fAllocator.Allocate(sizeof(ValueType*) * newSize);
438 if (newTable == NULL)
439 return false;
441 _Resize(newTable, newSize);
442 return true;
445 void _Resize(ValueType** newTable, size_t newSize, void** _oldTable = NULL)
447 for (size_t i = 0; i < newSize; i++)
448 newTable[i] = NULL;
450 if (fTable) {
451 for (size_t i = 0; i < fTableSize; i++) {
452 ValueType* bucket = fTable[i];
453 while (bucket) {
454 ValueType* next = _Link(bucket);
455 _Insert(newTable, newSize, bucket);
456 bucket = next;
460 if (_oldTable != NULL)
461 *_oldTable = fTable;
462 else
463 fAllocator.Free(fTable);
464 } else if (_oldTable != NULL)
465 *_oldTable = NULL;
467 fTableSize = newSize;
468 fTable = newTable;
471 ValueType*& _Link(ValueType* bucket) const
473 return fDefinition.GetLink(bucket);
476 bool _ExhaustiveSearch(ValueType* value) const
478 for (size_t i = 0; i < fTableSize; i++) {
479 ValueType* bucket = fTable[i];
480 while (bucket) {
481 if (bucket == value)
482 return true;
483 bucket = _Link(bucket);
487 return false;
490 Definition fDefinition;
491 Allocator fAllocator;
492 size_t fTableSize;
493 size_t fItemCount;
494 ValueType** fTable;
497 #endif // _KERNEL_UTIL_OPEN_HASH_TABLE_H