updates.
[silc.git] / lib / silcutil / silchashtable.c
1 /*
2
3   silchashtable.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* Implementation of collision resistant hash table. */
21 /* $Id$ */
22
23 #include "silcincludes.h"
24 #include "silchashtable.h"
25
26 /* Default size of the hash table (index to prime table) */
27 #define SILC_HASH_TABLE_SIZE 3
28
29 /* Produce the index by hashing the key */
30 #define SILC_HASH_TABLE_HASH (ht->hash(key) % primesize[ht->table_size])
31
32 /* One entry in the hash table. Includes the key and the associated
33    context. The `next' pointer is non-NULL if two (or more) different
34    keys hashed to same value.  The pointer is the pointer to the next
35    entry. */
36 typedef struct SilcHashTableEntryStruct {
37   void *key;
38   void *context;
39   struct SilcHashTableEntryStruct *next;
40 } *SilcHashTableEntry;
41
42 /* Hash table. */
43 struct SilcHashTableStruct {
44   SilcHashTableEntry *table;
45   uint32 table_size;
46   SilcHashFunction hash;
47   SilcHashCompare compare;
48   SilcHashDestructor destructor;
49 };
50
51 /* Prime sizes for the hash table. The size of the table will always
52    be one of these. */
53 const uint32 primesize[42] = 
54 {
55   1, 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031, 
56   1237, 2053, 2777, 4099, 6247, 8209, 14057, 16411, 21089, 32771, 47431,
57   65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169,
58   4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879
59 };
60
61 /* Find appropriate size for the hash table. The size will be a prime. */
62
63 static uint32 silc_hash_table_primesize(uint32 size, uint32 *index)
64 {
65   int i;
66
67   for (i = 0; i < sizeof(primesize); i++)
68     if (primesize[i] >= size) {
69       *index = i;
70       return primesize[i];
71     }
72
73   *index = i - 1;
74   return primesize[i - 1];
75 }
76
77 /* Internal routine to find entry in the hash table by `key'. */
78
79 static inline SilcHashTableEntry *
80 silc_hash_table_find_internal(SilcHashTable ht, void *key,
81                               SilcHashTableEntry *prev_entry)
82 {
83   SilcHashTableEntry *entry, prev = NULL;
84
85   entry = &ht->table[SILC_HASH_TABLE_HASH];
86   if (ht->compare) {
87     while (*entry && ht->compare((*entry)->key, key) == FALSE) {
88       prev = *entry;
89       entry = &(*entry)->next;
90     }
91   } else {
92     while (*entry && (*entry)->key != key) {
93       prev = *entry;
94       entry = &(*entry)->next;
95     }
96   }
97
98   *prev_entry = prev;
99   return entry;
100 }
101
102 /* Allocates new hash table and returns it.  If the `table_size' is not
103    zero then the hash table size is the size provided. If zero then the
104    default size will be used. Note that if the `table_size' is provided
105    it should be a prime. The `hash', `compare' and `destructor' are
106    the hash function, the key comparison function and key and context
107    destructor function, respectively. The `hash' is mandatory, the others
108    are optional. */
109
110 SilcHashTable silc_hash_table_alloc(uint32 table_size, 
111                                     SilcHashFunction hash,
112                                     SilcHashCompare compare,
113                                     SilcHashDestructor destructor)
114 {
115   SilcHashTable ht;
116   uint32 size_index = SILC_HASH_TABLE_SIZE;
117
118   if (!hash)
119     return NULL;
120
121   ht = silc_calloc(1, sizeof(*ht));
122   ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
123                                                                  &size_index) :
124                           primesize[SILC_HASH_TABLE_SIZE],
125                           sizeof(*ht->table));
126   ht->table_size = size_index;
127   ht->hash = hash;
128   ht->compare = compare;
129   ht->destructor = destructor;
130
131   return ht;
132 }
133
134 /* Frees the hash table. The destructor function provided in the
135    silc_hash_table_alloc will be called for all keys in the hash table. */
136
137 void silc_hash_table_free(SilcHashTable ht)
138 {
139   int i;
140
141   for (i = 0; i < primesize[ht->table_size]; i++)
142     if (ht->table[i]) {
143       if (ht->destructor)
144         ht->destructor(ht->table[i]->key, ht->table[i]->context);
145       silc_free(ht->table[i]);
146     }
147
148   silc_free(ht->table);
149   silc_free(ht);
150 }
151
152 /* Returns the size of the hash table */
153
154 uint32 silc_hash_table_size(SilcHashTable ht)
155 {
156   return primesize[ht->table_size];
157 }
158
159 /* Adds new entry to the hash table. The `key' is hashed using the
160    hash function and the both `key' and `context' will be saved to the
161    hash table. This function quarantees that the entry is always added
162    to the hash table reliably (it is collision resistant). */
163
164 void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
165 {
166   SilcHashTableEntry *entry;
167   uint32 index = SILC_HASH_TABLE_HASH;
168
169   entry = &ht->table[index];
170   if (*entry) {
171     /* The entry exists already. We have a collision, add it to the
172        list to avoid collision. */
173     SilcHashTableEntry e, tmp;
174
175     e = *entry;
176     tmp = e->next;
177     while (tmp) {
178       e = tmp;
179       tmp = tmp->next;
180     }
181
182     e->next = silc_calloc(1, sizeof(*e->next));
183     e->next->key = key;
184     e->next->context = context;
185   } else {
186     /* New key */
187     *entry = silc_calloc(1, sizeof(**entry));
188     (*entry)->key = key;
189     (*entry)->context = context;
190   }
191 }
192
193 /* Same as above but if the `key' already exists in the hash table
194    the old key and the old context will be replace with the `key' and
195    the `context. The destructor function will be called for the
196    replaced key and context. */
197
198 void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
199 {
200   SilcHashTableEntry *entry;
201   uint32 index = SILC_HASH_TABLE_HASH;
202
203   entry = &ht->table[index];
204   if (*entry) {
205     /* The entry exists already. We have a collision, replace the old
206        key and context. */
207     if (ht->destructor)
208       ht->destructor((*entry)->key, (*entry)->context);
209   } else {
210     /* New key */
211     *entry = silc_calloc(1, sizeof(**entry));
212   }
213
214   (*entry)->key = key;
215   (*entry)->context = context;
216 }
217
218 /* Removes the entry from the hash table by the provided `key'. This will
219    call the destructor funtion for the found entry. Return TRUE if the
220    entry was removed successfully and FALSE otherwise. */
221
222 bool silc_hash_table_del(SilcHashTable ht, void *key)
223 {
224   SilcHashTableEntry *entry, prev, e;
225
226   entry = silc_hash_table_find_internal(ht, key, &prev);
227   if (*entry == NULL)
228     return FALSE;
229
230   e = *entry;
231
232   if (!prev && e->next)
233     *entry = e->next;
234   if (!prev && e->next == NULL)
235     *entry = NULL;
236   if (prev)
237     prev->next = NULL;
238   if (prev && e->next)
239     prev->next = e->next;
240
241   if (ht->destructor)
242     ht->destructor(e->key, e->context);
243   silc_free(e);
244
245   return TRUE;
246 }
247
248 /* Finds the entry in the hash table by the provided `key' as fast as
249    possible. Return TRUE if the entry was found and FALSE otherwise. 
250    The found entry is returned to the `ret_key' and `ret_context',
251    respectively. If the `ret_key and `ret_context' are NULL then this
252    maybe used only to check whether given key exists in the table. */
253
254 bool silc_hash_table_find(SilcHashTable ht, void *key,
255                           void **ret_key, void **ret_context)
256 {
257   SilcHashTableEntry *entry, prev;
258
259   entry = silc_hash_table_find_internal(ht, key, &prev);
260   if (*entry == NULL)
261     return FALSE;
262
263   if (ret_key)
264     *ret_key = (*entry)->key;
265   if (ret_context)
266     *ret_context = (*entry)->context;
267
268   return TRUE;
269 }
270
271 /* Rehashs the hash table. The size of the new hash table is provided
272    as `new_size'. If the `new_size' is zero then this routine will make
273    the new table of a suitable size. Note that this operation may be
274    very slow. */
275
276 void silc_hash_table_rehash(SilcHashTable ht, uint32 new_size)
277 {
278
279 }