Subversion Repositories gelsvn

Rev

Rev 67 | Details | Compare with Previous | Last modification | View Log | RSS feed

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