Subversion Repositories gelsvn

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
667 khor 1
/* ----------------------------------------------------------------------- *
2
 * This file is part of GEL, http://www.imm.dtu.dk/GEL
3
 * Copyright (C) the authors and DTU Informatics
4
 * For license and list of authors, see ../../doc/intro.pdf
5
 * ----------------------------------------------------------------------- */
6
 
7
/**
8
 * @file HashTable.h
9
 * @brief Hash table class.
10
 */
11
 
12
#ifndef __UTIL_HASHTABLE_H
13
#define __UTIL_HASHTABLE_H
14
 
15
#include "HashKey.h"
16
 
17
#include <functional>
18
#include <iostream>
19
 
20
namespace Util
21
{
22
 
23
	namespace
24
	{
25
		const int HASH_EXPAND_LOAD = 80;
26
	};
27
 
28
	/** \brief Hashtable class template. 
29
 
30
	This class template has taken for too long to get right. That is because
31
	it is flexible. It can be used either as a simple (key, value) database
32
	or as a reference counted database.
33
 
34
	There are two template arguments: KEY_T is the hash key type. This type
35
	must provide operators == and != and a hash function which takes an 
36
	integer denoting the table size and returns another integer - the table
37
	argument.
38
 
39
	VAL_T is the value type. There are few restrictions on this type but it 
40
	must have default and copy constructors and operator=. The HashTable 
41
	template is intended for use with simple types.
42
	*/
43
	template<class KEY_T, class VAL_T>
44
	class HashTable
45
	{
46
		/** struct containing hashtable entries.
47
				Apart from the value, this struct also contains a reference
48
				count and the hash key */
49
		struct EntryType
50
		{
51
			// Hash key
52
			KEY_T key;
53
 
54
			/// Table value corresponding to key
55
			VAL_T val;
56
 
57
			/// Reference counter
58
			signed char ref;
59
 
60
		public:
61
			EntryType(): ref(-1) {}
62
		};
63
 
64
		/// The table itself (array of entries)
65
		EntryType* table;   
66
 
67
	private:
68
 
69
		/// Number of elements in the table
70
		int items;          
71
 
72
		/// Size of table
73
		int size;       
74
 
75
		/// Resize the table (new size is argument)
76
		void resize(int new_size);
77
 
78
		/** Private find entry function. Returns true iff entry was found 
79
				and reference non-zero. the second argument is set to the table
80
				index corresponding to key. */
81
		bool find_entry_priv(const KEY_T&, int&) const;
82
 
83
	public:
84
 
85
		/// Construct an empty hash table
86
		HashTable(): items(0), size(1) 
87
		{
88
			table = new EntryType[size];
89
		}
90
 
91
		/// Destruct it
92
		~HashTable() { delete [] table; }
93
 
94
		/** Create entry in hash table.
95
				The first argument is the key, and the second is the value
96
				to be inserted. The third argument is the amount that the 
97
				reference count should be increased.
98
 
99
				If third argument is zero: The key,value pair is inserted
100
				if key is not found. In either case the reference count is 
101
				set to 1. function returns true if key was not found (or 
102
				reference count=0 which counts as key begin absent)
103
 
104
				If third argument is non-zero: Behaviour is as above except 
105
				that the reference count is incremented or set to value of 
106
				third argument depending on whether the key was already present. */
107
		bool create_entry(const KEY_T&, const VAL_T&,int=0);
108
 
109
		bool create_entry(const KEY_T& kv, VAL_T*& val, int incr=0);
110
 
111
 
112
 
113
		/** Removes an entry from database.
114
				This function returns false if the key was not found. Otherwise
115
				the reference count is set to zero and the function returns true
116
				having first reduced the size of the table, if the size was 
117
				below threshold after removal. */
118
		bool remove_entry(const KEY_T&);
119
 
120
		/** Find entry in database.
121
				The second argument is set to the value that corresponds to the key
122
				passed as first argument. The function returns true if the key
123
				was found (and reference non-zero) */
124
		bool find_entry(const KEY_T&, VAL_T&) const;
125
 
126
		/** Decrease reference count. THe key is first argument, the value
127
				is returned in second argument, and the third argument contains 
128
				the amount of decrease, 1 is default. The function returns true
129
				if the key was found and reference non-zero. False otherwise.
130
 
131
				if the reference count is zero after being decreased, the function
132
				may decrease the size of the table. 
133
		*/
134
		bool decr_reference(const KEY_T&, VAL_T&, int=1);
135
 
136
 
137
		/** Change Entry. Does not affect reference count. Returns true
138
				if the entry exists - false otherwise. */
139
		bool change_entry(const KEY_T&, const VAL_T&);
140
 
141
		/// Print various statistics for the table 
142
		void print() const;
143
 
144
		/** Get the first entry in HashTable. All arguments are passed by 
145
				reference. The first argument is the index. Upon return the 
146
				first argument will contain the first valid index in the table.
147
				This index should be passed to the get_next function. (see below).
148
				Subsequent arguments are the key, the value and the reference 
149
				count. These are set to the values that correspond to the index. 
150
				The function returns false if there are no valid indices - i.e. 
151
				the table is empty 
152
		*/
153
		bool get_first(int& idx, KEY_T&,VAL_T& val,int&) const;
154
 
155
		/** Get the next entry in the HashTable. All arguments are passed
156
				by reference.
157
				This first argument is a valid index to an element in the table 
158
				(from a previous call to either this function or get_first)
159
				get_next finds the next valid entry and returns key, value and 
160
				reference count in the next three arguments. get_next returns 
161
				false if there is no next valid index. */
162
		bool get_next(int& idx, KEY_T&,VAL_T& val,int&) const;
163
 
164
		/// Returns the sum of the reference counts that are > 0
165
		int integrate() const; 
166
 
167
 
168
		/// Returns no of items in table.
169
		int no_items() const {return items;}
170
 
171
		/// Returns true if number of items==0
172
		bool empty() const {return items==0;}
173
 
174
		/// Returns total size of table (in bytes)
175
		int total_size() const {return sizeof(*this) + sizeof(EntryType)*size;}
176
 
177
		/// Returns the full size of an entry (key + value + ref.count).
178
		static int entry_size() {return sizeof(EntryType);}
179
 
180
	};
181
 
182
 
183
	template<class KEY_T, class VAL_T>
184
	bool HashTable<KEY_T, VAL_T>::find_entry_priv(const KEY_T& kv, int& k) const
185
	{
186
		k = kv.hash(size);
187
		int fk = k;
188
		while(table[k].ref != -1)
189
			{
190
				if (table[k].key == kv) 
191
					return (table[k].ref > 0) ? true : false;
192
				k = (k+1)&(size-1);
193
				if (k==fk) break;
194
			}
195
		return false;
196
	}
197
 
198
	template<class KEY_T, class VAL_T>
199
	bool HashTable<KEY_T, VAL_T>::find_entry(const KEY_T& kv, VAL_T& val) const
200
	{
201
		int k;
202
		if(find_entry_priv(kv,k))
203
			{
204
				val = table[k].val;
205
				return true;
206
			}
207
		return false;
208
	}
209
 
210
	template<class KEY_T, class VAL_T>
211
	void HashTable<KEY_T, VAL_T>::resize(int new_size)
212
	{ 
213
		int j=0;
214
		EntryType* tmp_table = new EntryType[new_size];
215
		for(int i=0; i<size;i++)
216
			if (table[i].ref >0)
217
				{
218
					j++;
219
					int k = table[i].key.hash(new_size);
220
 
221
					while(tmp_table[k].ref != -1) 
222
						k=(k+1)&(new_size-1); 
223
 
224
					tmp_table[k].key = table[i].key;
225
					tmp_table[k].val = table[i].val;
226
					tmp_table[k].ref = table[i].ref;
227
				}
228
 
229
		delete table;
230
		table = tmp_table;
231
		size = new_size;
232
	}
233
 
234
	template<class KEY_T, class VAL_T>
235
	bool HashTable<KEY_T, VAL_T>::create_entry(const KEY_T& kv, 
236
																						 const VAL_T& val,
237
																						 int incr)
238
	{
239
		if (items*100 >= HASH_EXPAND_LOAD * size) 
240
			resize(size*2);
241
 
242
		int k;
243
		if(!find_entry_priv(kv,k))
244
			{
245
				++items;
246
				table[k].ref = (incr==0? 1: incr);
247
				table[k].key = kv;
248
				table[k].val = val;
249
				return true;
250
			}
251
		table[k].ref += incr;
252
		table[k].val = val;
253
		return false;
254
	}
255
 
256
	template<class KEY_T, class VAL_T>
257
	bool HashTable<KEY_T, VAL_T>::create_entry(const KEY_T& kv, 
258
																						 VAL_T*& val,
259
																						 int incr)
260
	{
261
		if (items*100 >= HASH_EXPAND_LOAD * size) 
262
			resize(size*2);
263
 
264
		int k;
265
		if(!find_entry_priv(kv,k))
266
			{
267
				++items;
268
				table[k].ref = (incr==0? 1: incr);
269
				table[k].key = kv;
270
				val = &table[k].val;
271
				return true;
272
			}
273
		table[k].ref += incr;
274
		val=&table[k].val;
275
		return false;
276
	}
277
 
278
 
279
	template<class KEY_T, class VAL_T>
280
	bool HashTable<KEY_T, VAL_T>::remove_entry(const KEY_T& kv)
281
	{
282
		int k;
283
		if(find_entry_priv(kv,k))
284
			{
285
				table[k].ref = 0;
286
				--items;
287
				if(items*100 < (HASH_EXPAND_LOAD * size) / 2)
288
					resize(size/2);
289
				return true;
290
			}
291
		return false;
292
	}
293
 
294
 
295
 
296
	template<class KEY_T, class VAL_T>
297
	bool HashTable<KEY_T, VAL_T>::decr_reference(const KEY_T& kv,  
298
																							 VAL_T& val, 
299
																							 int decr)
300
	{
301
		int k;
302
		if(find_entry_priv(kv,k))
303
			{
304
				table[k].ref -= decr;
305
				val = table[k].val;
306
				if(table[k].ref == 0)
307
					{
308
						--items;
309
						if(items*100 < (HASH_EXPAND_LOAD * size) / 2)
310
							resize(size/2);
311
					}
312
				return true;
313
			}
314
		return false;
315
	}
316
 
317
	template<class KEY_T, class VAL_T>
318
	bool HashTable<KEY_T, VAL_T>::change_entry(const KEY_T& kv,  
319
																						 const VAL_T& val)
320
	{
321
		int k;
322
		if(find_entry_priv(kv,k))
323
			{
324
				table[k].val = val;
325
				return true;
326
			}
327
		return false;
328
	}
329
 
330
 
331
	template<class KEY_T, class VAL_T>
332
	void HashTable<KEY_T, VAL_T>::print() const
333
	{
334
		std::cout << "Entry size " << sizeof(EntryType) << " bytes\n"
335
							<< "Max number of items " << size	<< "\n" 
336
							<< "Number of items " << items << std::endl;
337
	}
338
 
339
	template<class KEY_T, class VAL_T>
340
	int HashTable<KEY_T, VAL_T>::integrate() const 
341
	{
342
		int integral =0;
343
		for(int i=0;i<size; i++)
344
			if(table[i].ref>0) integral += table[i].ref;
345
		return integral;
346
	}
347
 
348
	template<class KEY_T, class VAL_T>
349
	bool HashTable<KEY_T, VAL_T>::get_first(int& idx, 
350
																					KEY_T& key,
351
																					VAL_T& val,
352
																					int& r) const 
353
	{
354
		if(size==0) return false;
355
 
356
		idx=0;
357
		while(table[idx].ref <= 0)
358
			{
359
				++idx;
360
				if(idx==size) return false;
361
			}
362
		key = table[idx].key;
363
		val = table[idx].val;
364
		r   = table[idx].ref;
365
		return true;
366
	}
367
 
368
	template<class KEY_T, class VAL_T>
369
	bool HashTable<KEY_T, VAL_T>::get_next(int& idx, 
370
																				 KEY_T& key,
371
																				 VAL_T& val,
372
																				 int& r) const
373
	{
374
		idx++;
375
		if(idx==size) return false;
376
		while(table[idx].ref <= 0)
377
			{
378
				++idx;
379
				if(idx==size) return false;
380
			}
381
		key = table[idx].key;
382
		val = table[idx].val;
383
		r   = table[idx].ref;
384
		return true;
385
	}
386
 
387
}
388
 
389
 
390
#endif