15 /* /* private fields... */
19 /* HTABLE *htable_create(size)
22 /* HTABLE_INFO *htable_enter(table, key, value)
27 /* char *htable_find(table, key)
31 /* HTABLE_INFO *htable_locate(table, key)
35 /* void htable_delete(table, key, free_fn)
38 /* void (*free_fn)(char *);
40 /* void htable_free(table, free_fn)
42 /* void (*free_fn)(char *);
44 /* void htable_walk(table, action, ptr)
46 /* void (*action)(HTABLE_INFO *, char *ptr);
49 /* HTABLE_INFO **htable_list(table)
52 /* This module maintains one or more hash tables. Each table entry
53 /* consists of a unique string-valued lookup key and a generic
54 /* character-pointer value.
55 /* The tables are automatically resized when they fill up. When the
56 /* values to be remembered are not character pointers, proper casts
57 /* should be used or the code will not be portable.
59 /* htable_create() creates a table of the specified size and returns a
60 /* pointer to the result. The lookup keys are saved with mystrdup().
61 /* htable_enter() stores a (key, value) pair into the specified table
62 /* and returns a pointer to the resulting entry. The code does not
63 /* check if an entry with that key already exists: use htable_locate()
64 /* for updating an existing entry.
66 /* htable_find() returns the value that was stored under the given key,
67 /* or a null pointer if it was not found. In order to distinguish
68 /* a null value from a non-existent value, use htable_locate().
70 /* htable_locate() returns a pointer to the entry that was stored
71 /* for the given key, or a null pointer if it was not found.
73 /* htable_delete() removes one entry that was stored under the given key.
74 /* If the free_fn argument is not a null pointer, the corresponding
75 /* function is called with as argument the non-zero value stored under
78 /* htable_free() destroys a hash table, including contents. If the free_fn
79 /* argument is not a null pointer, the corresponding function is called
80 /* for each table entry, with as argument the non-zero value stored
83 /* htable_walk() invokes the action function for each table entry, with
84 /* a pointer to the entry as its argument. The ptr argument is passed
85 /* on to the action function.
87 /* htable_list() returns a null-terminated list of pointers to
88 /* all elements in the named table. The list should be passed to
91 /* A callback function should not modify the hash table that is
92 /* specified to its caller.
94 /* The following conditions are reported and cause the program to
95 /* terminate immediately: memory allocation failure; an attempt
96 /* to delete a non-existent entry.
98 /* mymalloc(3) memory management wrapper
102 /* The Secure Mailer license must be distributed with this software.
105 /* IBM T.J. Watson Research
107 /* Yorktown Heights, NY 10598, USA
112 #include <sys_defs.h>
117 #include "mymalloc.h"
121 /* htable_hash - hash a string */
123 static unsigned htable_hash(const char *s
, unsigned size
)
129 * From the "Dragon" book by Aho, Sethi and Ullman.
133 h
= (h
<< 4U) + *s
++;
134 if ((g
= (h
& 0xf0000000)) != 0) {
142 /* htable_link - insert element into table */
144 #define htable_link(table, element) { \
145 HTABLE_INFO **_h = table->data + htable_hash(element->key, table->size);\
147 if ((element->next = *_h) != 0) \
148 (*_h)->prev = element; \
153 /* htable_size - allocate and initialize hash table */
155 static void htable_size(HTABLE
*table
, unsigned size
)
161 table
->data
= h
= (HTABLE_INFO
**) mymalloc(size
* sizeof(HTABLE_INFO
*));
169 /* htable_create - create initial hash table */
171 HTABLE
*htable_create(int size
)
175 table
= (HTABLE
*) mymalloc(sizeof(HTABLE
));
176 htable_size(table
, size
< 13 ? 13 : size
);
180 /* htable_grow - extend existing table */
182 static void htable_grow(HTABLE
*table
)
186 unsigned old_size
= table
->size
;
187 HTABLE_INFO
**h
= table
->data
;
188 HTABLE_INFO
**old_entries
= h
;
190 htable_size(table
, 2 * old_size
);
192 while (old_size
-- > 0) {
193 for (ht
= *h
++; ht
; ht
= next
) {
195 htable_link(table
, ht
);
198 myfree((char *) old_entries
);
201 /* htable_enter - enter (key, value) pair */
203 HTABLE_INFO
*htable_enter(HTABLE
*table
, const char *key
, char *value
)
207 if (table
->used
>= table
->size
)
209 ht
= (HTABLE_INFO
*) mymalloc(sizeof(HTABLE_INFO
));
210 ht
->key
= mystrdup(key
);
212 htable_link(table
, ht
);
216 /* htable_find - lookup value */
218 char *htable_find(HTABLE
*table
, const char *key
)
222 #define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0))
225 for (ht
= table
->data
[htable_hash(key
, table
->size
)]; ht
; ht
= ht
->next
)
226 if (STREQ(key
, ht
->key
))
231 /* htable_locate - lookup entry */
233 HTABLE_INFO
*htable_locate(HTABLE
*table
, const char *key
)
237 #define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0))
240 for (ht
= table
->data
[htable_hash(key
, table
->size
)]; ht
; ht
= ht
->next
)
241 if (STREQ(key
, ht
->key
))
246 /* htable_delete - delete one entry */
248 void htable_delete(HTABLE
*table
, const char *key
, void (*free_fn
) (char *))
252 HTABLE_INFO
**h
= table
->data
+ htable_hash(key
, table
->size
);
254 #define STREQ(x,y) (x == y || (x[0] == y[0] && strcmp(x,y) == 0))
256 for (ht
= *h
; ht
; ht
= ht
->next
) {
257 if (STREQ(key
, ht
->key
)) {
259 ht
->next
->prev
= ht
->prev
;
261 ht
->prev
->next
= ht
->next
;
266 if (free_fn
&& ht
->value
)
267 (*free_fn
) (ht
->value
);
272 msg_panic("htable_delete: unknown_key: \"%s\"", key
);
276 /* htable_free - destroy hash table */
278 void htable_free(HTABLE
*table
, void (*free_fn
) (char *))
281 unsigned i
= table
->size
;
284 HTABLE_INFO
**h
= table
->data
;
287 for (ht
= *h
++; ht
; ht
= next
) {
290 if (free_fn
&& ht
->value
)
291 (*free_fn
) (ht
->value
);
295 myfree((char *) table
->data
);
297 myfree((char *) table
);
301 /* htable_walk - iterate over hash table */
303 void htable_walk(HTABLE
*table
, void (*action
) (HTABLE_INFO
*, char *),
306 unsigned i
= table
->size
;
307 HTABLE_INFO
**h
= table
->data
;
311 for (ht
= *h
++; ht
; ht
= ht
->next
)
316 /* htable_list - list all table members */
318 HTABLE_INFO
**htable_list(HTABLE
*table
)
326 list
= (HTABLE_INFO
**) mymalloc(sizeof(*list
) * (table
->used
+ 1));
327 for (i
= 0; i
< table
->size
; i
++)
328 for (member
= table
->data
[i
]; member
!= 0; member
= member
->next
)
329 list
[count
++] = member
;
331 list
= (HTABLE_INFO
**) mymalloc(sizeof(*list
));
338 #include <vstring_vstream.h>
341 int main(int unused_argc
, char **unused_argv
)
343 VSTRING
*buf
= vstring_alloc(10);
346 HTABLE_INFO
**ht_info
;
353 * Load a large number of strings and delete them in a random order.
355 hash
= htable_create(10);
356 while (vstring_get(buf
, VSTREAM_IN
) != VSTREAM_EOF
)
357 htable_enter(hash
, vstring_str(buf
), CAST_INT_TO_CHAR_PTR(count
++));
358 ht_info
= htable_list(hash
);
359 for (i
= 0; i
< hash
->used
; i
++) {
360 r
= myrand() % hash
->used
;
362 ht_info
[i
] = ht_info
[r
];
365 for (ht
= ht_info
; *ht
; ht
++)
366 htable_delete(hash
, ht
[0]->key
, (void (*) (char *)) 0);
368 msg_panic("%d entries not deleted", hash
->used
);
369 myfree((char *) ht_info
);
370 htable_free(hash
, (void (*) (char *)) 0);