2 * Copyright 2007, Hugo Santos. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 #ifndef _KERNEL_UTIL_OPEN_HASH_TABLE_H
6 #define _KERNEL_UTIL_OPEN_HASH_TABLE_H
14 # include <KernelExport.h>
15 # include <util/kernel_cpp.h>
16 # include <util/TypeOperation.h>
18 # include <TypeOperation.h>
23 The Definition template must have four methods: `HashKey', `Hash',
24 `Compare' and `GetLink;. It must also define several types as shown in the
33 struct HashTableDefinition {
35 typedef Foo ValueType;
37 size_t HashKey(KeyType key) const
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
60 struct MallocAllocator
{
61 void* Allocate(size_t size
) const
66 void Free(void* memory
) const
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
{
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%
106 BOpenHashTable(const Definition
& definition
)
108 fDefinition(definition
),
115 BOpenHashTable(const Definition
& definition
, const Allocator
& allocator
)
117 fDefinition(definition
),
118 fAllocator(allocator
),
127 fAllocator
.Free(fTable
);
130 status_t
Init(size_t initialSize
= kMinimumSize
)
132 if (initialSize
> 0 && !_Resize(initialSize
))
137 size_t TableSize() const
144 return fItemCount
== 0;
147 size_t CountElements() const
152 ValueType
* Lookup(typename TypeOperation
<KeyType
>::ConstRefT key
) const
157 size_t index
= fDefinition
.HashKey(key
) & (fTableSize
- 1);
158 ValueType
* slot
= fTable
[index
];
161 if (fDefinition
.Compare(key
, slot
))
169 status_t
Insert(ValueType
* value
)
171 if (fTableSize
== 0) {
172 if (!_Resize(kMinimumSize
))
174 } else if (AutoExpand
&& fItemCount
>= (fTableSize
* 200 / 256))
175 _Resize(fTableSize
* 2);
177 InsertUnchecked(value
);
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
)) {
190 panic("Hash Table: value already in table.");
192 debugger("Hash Table: value already in table.");
196 _Insert(fTable
, fTableSize
, value
);
200 // TODO: a ValueType* Remove(const KeyType& key) method is missing
202 bool Remove(ValueType
* value
)
204 if (!RemoveUnchecked(value
))
207 if (AutoExpand
&& fTableSize
> kMinimumSize
208 && fItemCount
< (fTableSize
* 50 / 256))
209 _Resize(fTableSize
/ 2);
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
223 bool RemoveUnchecked(ValueType
* value
)
225 size_t index
= fDefinition
.Hash(value
) & (fTableSize
- 1);
226 ValueType
* previous
= NULL
;
227 ValueType
* slot
= fTable
[index
];
230 ValueType
* next
= _Link(slot
);
234 _Link(previous
) = next
;
236 fTable
[index
] = next
;
247 if (CheckDuplicates
&& _ExhaustiveSearch(value
)) {
249 panic("Hash Table: duplicate detected.");
251 debugger("Hash Table: duplicate detected.");
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
265 ValueType
* Clear(bool returnElements
= false)
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
);
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)) {
308 while (fItemCount
>= size
* 200 / 256)
310 } else if (size
> kMinimumSize
&& fItemCount
< size
* 50 / 256) {
312 while (fItemCount
< size
* 50 / 256)
314 if (size
< kMinimumSize
)
318 if (size
== fTableSize
)
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
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
);
342 _Resize((ValueType
**)allocation
, size
/ sizeof(ValueType
*), oldTable
);
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.
353 Iterator(const HashTable
* table
)
359 Iterator(const HashTable
* table
, size_t index
, ValueType
* value
)
360 : fTable(table
), fIndex(index
), fNext(value
) {}
362 bool HasNext() const { return fNext
!= NULL
; }
366 ValueType
* current
= fNext
;
385 fNext
= fTable
->_Link(fNext
);
387 while (fNext
== NULL
&& fIndex
< fTable
->fTableSize
)
388 fNext
= fTable
->fTable
[fIndex
++];
391 const HashTable
* fTable
;
396 Iterator
GetIterator() const
398 return Iterator(this);
401 Iterator
GetIterator(typename TypeOperation
<KeyType
>::ConstRefT key
) const
404 return Iterator(this, fTableSize
, NULL
);
406 size_t index
= fDefinition
.HashKey(key
) & (fTableSize
- 1);
407 ValueType
* slot
= fTable
[index
];
410 if (fDefinition
.Compare(key
, slot
))
416 return Iterator(this, fTableSize
, NULL
);
418 return Iterator(this, index
+ 1, slot
);
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
)
436 = (ValueType
**)fAllocator
.Allocate(sizeof(ValueType
*) * newSize
);
437 if (newTable
== NULL
)
440 _Resize(newTable
, newSize
);
444 void _Resize(ValueType
** newTable
, size_t newSize
, void** _oldTable
= NULL
)
446 for (size_t i
= 0; i
< newSize
; i
++)
450 for (size_t i
= 0; i
< fTableSize
; i
++) {
451 ValueType
* bucket
= fTable
[i
];
453 ValueType
* next
= _Link(bucket
);
454 _Insert(newTable
, newSize
, bucket
);
459 if (_oldTable
!= NULL
)
462 fAllocator
.Free(fTable
);
463 } else if (_oldTable
!= NULL
)
466 fTableSize
= newSize
;
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
];
482 bucket
= _Link(bucket
);
489 Definition fDefinition
;
490 Allocator fAllocator
;
496 #endif // _KERNEL_UTIL_OPEN_HASH_TABLE_H