xref: /haiku/headers/private/kernel/util/OpenHashTable.h (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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